Compare commits
23 commits
main
...
release-1.
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
0e0a54e6e2 | ||
|
|
0eb8a70089 | ||
|
|
bd6f228886 | ||
|
|
c08a7d81b3 | ||
|
|
dc0651ddd2 | ||
|
|
2964ed5982 | ||
|
|
1b2cb1c7b2 | ||
|
|
0776b954c3 | ||
|
|
b86776df99 | ||
|
|
19a19ae1b7 | ||
|
|
1cd6fd3596 | ||
|
|
03380d6e34 | ||
|
|
a8937f5b83 | ||
|
|
0ad379e761 | ||
|
|
9214762d10 | ||
|
|
6dc18a7c8e | ||
|
|
405c55b070 | ||
|
|
ec9d2dbef8 | ||
|
|
b2e3c8c75d | ||
|
|
eae7bde3ac | ||
|
|
b66873dfdb | ||
|
|
6acddea8aa | ||
|
|
049bcfbd4b |
|
|
@ -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)
|
||||||
|
|
||||||
|
|
@ -9,7 +9,7 @@ Playwright is a framework for Web Testing and Automation. It allows testing [Chr
|
||||||
| | Linux | macOS | Windows |
|
| | Linux | macOS | Windows |
|
||||||
| :--- | :---: | :---: | :---: |
|
| :--- | :---: | :---: | :---: |
|
||||||
| Chromium <!-- GEN:chromium-version -->104.0.5112.48<!-- GEN:stop --> | :white_check_mark: | :white_check_mark: | :white_check_mark: |
|
| Chromium <!-- GEN:chromium-version -->104.0.5112.48<!-- GEN:stop --> | :white_check_mark: | :white_check_mark: | :white_check_mark: |
|
||||||
| WebKit <!-- GEN:webkit-version -->15.4<!-- GEN:stop --> | :white_check_mark: | :white_check_mark: | :white_check_mark: |
|
| WebKit <!-- GEN:webkit-version -->16.0<!-- GEN:stop --> | :white_check_mark: | :white_check_mark: | :white_check_mark: |
|
||||||
| Firefox <!-- GEN:firefox-version -->102.0<!-- GEN:stop --> | :white_check_mark: | :white_check_mark: | :white_check_mark: |
|
| Firefox <!-- GEN:firefox-version -->102.0<!-- GEN:stop --> | :white_check_mark: | :white_check_mark: | :white_check_mark: |
|
||||||
|
|
||||||
Headless execution is supported for all the browsers on all platforms. Check out [system requirements](https://playwright.dev/docs/library#system-requirements) for details.
|
Headless execution is supported for all the browsers on all platforms. Check out [system requirements](https://playwright.dev/docs/library#system-requirements) for details.
|
||||||
|
|
|
||||||
238
docs/src/accessibility-testing-java.md
Normal file
238
docs/src/accessibility-testing-java.md
Normal file
|
|
@ -0,0 +1,238 @@
|
||||||
|
---
|
||||||
|
id: accessibility-testing
|
||||||
|
title: "Accessibility testing"
|
||||||
|
---
|
||||||
|
|
||||||
|
Playwright can be used to test your application for many types of accessibility issues.
|
||||||
|
|
||||||
|
A few examples of problems this can catch include:
|
||||||
|
- Text that would be hard to read for users with vision impairments due to poor color contrast with the background behind it
|
||||||
|
- UI controls and form elements without labels that a screen reader could identify
|
||||||
|
- Interactive elements with duplicate IDs which can confuse assistive technologies
|
||||||
|
|
||||||
|
The following examples rely on the [`com.deque.html.axe-core/playwright`](https://mvnrepository.com/artifact/com.deque.html.axe-core/playwright) Maven package which adds support for running the [axe accessibility testing engine](https://www.deque.com/axe/) as part of your Playwright tests.
|
||||||
|
|
||||||
|
<!-- TOC -->
|
||||||
|
|
||||||
|
## Disclaimer
|
||||||
|
|
||||||
|
Automated accessibility tests can detect some common accessibility problems such as missing or invalid properties. But many accessibility problems can only be discovered through manual testing. We recommend using a combination of automated testing, manual accessibility assessments, and inclusive user testing.
|
||||||
|
|
||||||
|
For manual assessments, we recommend [Accessibility Insights for Web](https://accessibilityinsights.io/docs/web/overview/?referrer=playwright-accessibility-testing-java), a free and open source dev tool that walks you through assessing a website for [WCAG 2.1 AA](https://www.w3.org/WAI/WCAG21/quickref/?currentsidebar=%23col_customize&levels=aaa) coverage.
|
||||||
|
|
||||||
|
## Example accessibility tests
|
||||||
|
|
||||||
|
Accessibility tests work just like any other Playwright test. You can either create separate test cases for them, or integrate accessibility scans and assertions into your existing test cases.
|
||||||
|
|
||||||
|
The following examples demonstrate a few basic accessibility testing scenarios.
|
||||||
|
|
||||||
|
### Example 1: Scanning an entire page
|
||||||
|
|
||||||
|
This example demonstrates how to test an entire page for automatically detectable accessibility violations. The test:
|
||||||
|
1. Imports the [`com.deque.html.axe-core/playwright`](https://mvnrepository.com/artifact/com.deque.html.axe-core/playwright) package
|
||||||
|
1. Uses normal JUnit 5 `@Test` syntax to define a test case
|
||||||
|
1. Uses normal Playwright syntax to open a browser and navigate to the page under test
|
||||||
|
1. Invokes `AxeBuilder.analyze()` to run the accessibility scan against the page
|
||||||
|
1. Uses normal JUnit 5 test assertions to verify that there are no violations in the returned scan results
|
||||||
|
|
||||||
|
```java
|
||||||
|
import com.deque.html.axecore.playwright.*; // 1
|
||||||
|
import com.deque.html.axecore.utilities.axeresults.*;
|
||||||
|
|
||||||
|
import org.junit.jupiter.api.*;
|
||||||
|
import com.microsoft.playwright.*;
|
||||||
|
|
||||||
|
import static org.junit.jupiter.api.Assertions.*;
|
||||||
|
|
||||||
|
public class HomepageTests {
|
||||||
|
@Test // 2
|
||||||
|
void shouldNotHaveAutomaticallyDetectableAccessibilityIssues() throws Exception {
|
||||||
|
Playwright playwright = Playwright.create();
|
||||||
|
Browser browser = playwright.chromium().launch();
|
||||||
|
BrowserContext context = browser.newContext();
|
||||||
|
Page page = context.newPage();
|
||||||
|
|
||||||
|
page.navigate("https://your-site.com/"); // 3
|
||||||
|
|
||||||
|
AxeResults accessibilityScanResults = new AxeBuilder(page).analyze(); // 4
|
||||||
|
|
||||||
|
assertEquals(Collections.emptyList(), accessibilityScanResults.getViolations()); // 5
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### Example 2: Configuring axe to scan a specific part of a page
|
||||||
|
|
||||||
|
`com.deque.html.axe-core/playwright` supports many configuration options for axe. You can specify these options by using a Builder pattern with the `AxeBuilder` class.
|
||||||
|
|
||||||
|
For example, you can use [`AxeBuilder.include()`](https://github.com/dequelabs/axe-core-maven-html/blob/develop/playwright/README.md#axebuilderincludeliststring-selector) to constrain an accessibility scan to only run against one specific part of a page.
|
||||||
|
|
||||||
|
`AxeBuilder.analyze()` will scan the page *in its current state* when you call it. To scan parts of a page that are revealed based on UI interactions, use [Locators](./locators.md) to interact with the page before invoking `analyze()`:
|
||||||
|
|
||||||
|
```java
|
||||||
|
@Test
|
||||||
|
void navigationMenuFlyoutShouldNotHaveAutomaticallyDetectableAccessibilityViolations() throws Exception {
|
||||||
|
page.navigate("https://your-site.com/");
|
||||||
|
|
||||||
|
page.locator("button[aria-label=\"Navigation Menu\"]").click();
|
||||||
|
|
||||||
|
// It is important to waitFor() the page to be in the desired
|
||||||
|
// state *before* running analyze(). Otherwise, axe might not
|
||||||
|
// find all the elements your test expects it to scan.
|
||||||
|
page.locator("#navigation-menu-flyout").waitFor();
|
||||||
|
|
||||||
|
AxeResults accessibilityScanResults = new AxeBuilder(page)
|
||||||
|
.include(Arrays.asList("#navigation-menu-flyout"))
|
||||||
|
.analyze();
|
||||||
|
|
||||||
|
assertEquals(Collections.emptyList(), accessibilityScanResults.getViolations());
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### Example 3: Scanning for WCAG violations
|
||||||
|
|
||||||
|
By default, axe checks against a wide variety of accessibility rules. Some of these rules correspond to specific success criteria from the [Web Content Accessibility Guidelines (WCAG)](https://www.w3.org/TR/WCAG21/), and others are "best practice" rules that are not specifically required by any WCAG criteron.
|
||||||
|
|
||||||
|
You can constrain an accessibility scan to only run those rules which are "tagged" as corresponding to specific WCAG success criteria by using [`AxeBuilder.withTags()`](https://github.com/dequelabs/axe-core-maven-html/blob/develop/playwright/README.md#axebuilderwithtagsliststring-rules). For example, [Accessibility Insights for Web's Automated Checks](https://accessibilityinsights.io/docs/web/getstarted/fastpass/?referrer=playwright-accessibility-testing-java) only include axe rules that test for violations of WCAG A and AA success criteria; to match that behavior, you would use the tags `wcag2a`, `wcag2aa`, `wcag21a`, and `wcag21aa`.
|
||||||
|
|
||||||
|
Note that [automated testing cannot detect all types of WCAG violations](#disclaimer).
|
||||||
|
|
||||||
|
```java
|
||||||
|
AxeResults accessibilityScanResults = new AxeBuilder(page)
|
||||||
|
.withTags(Arrays.asList("wcag2a", "wcag2aa", "wcag21a", "wcag21aa"))
|
||||||
|
.analyze();
|
||||||
|
|
||||||
|
assertEquals(Collections.emptyList(), accessibilityScanResults.getViolations());
|
||||||
|
```
|
||||||
|
|
||||||
|
You can find a complete listing of the rule tags axe-core supports in [the "Axe-core Tags" section of the axe API documentation](https://www.deque.com/axe/core-documentation/api-documentation/#axe-core-tags).
|
||||||
|
|
||||||
|
## Handling known issues
|
||||||
|
|
||||||
|
A common question when adding accessibility tests to an application is "how do I suppress known violations?" The following examples demonstrate a few techniques you can use.
|
||||||
|
|
||||||
|
### Excluding individual elements from a scan
|
||||||
|
|
||||||
|
If your application contains a few specific elements with known issues, you can use [`AxeBuilder.exclude()`](https://github.com/dequelabs/axe-core-maven-html/blob/develop/playwright/README.md#axebuilderexcludeliststring-selector) to exclude them from being scanned until you're able to fix the issues.
|
||||||
|
|
||||||
|
This is usually the simplest option, but it has some important downsides:
|
||||||
|
* `exclude()` will exclude the specified elements *and all of their descendants*. Avoid using it with components that contain many children.
|
||||||
|
* `exclude()` will prevent *all* rules from running against the specified elements, not just the rules corresponding to known issues.
|
||||||
|
|
||||||
|
Here is an example of excluding one element from being scanned in one specific test:
|
||||||
|
|
||||||
|
```java
|
||||||
|
AxeResults accessibilityScanResults = new AxeBuilder(page)
|
||||||
|
.exclude(Arrays.asList("#element-with-known-issue"))
|
||||||
|
.analyze();
|
||||||
|
|
||||||
|
assertEquals(Collections.emptyList(), accessibilityScanResults.getViolations());
|
||||||
|
```
|
||||||
|
|
||||||
|
If the element in question is used repeatedly in many pages, consider [using a test fixture](#using-a-test-fixture-for-common-axe-configuration) to reuse the same `AxeBuilder` configuration across multiple tests.
|
||||||
|
|
||||||
|
### Disabling individual scan rules
|
||||||
|
|
||||||
|
If your application contains many different pre-existing violations of a specific rule, you can use [`AxeBuilder.disableRules()`](https://github.com/dequelabs/axe-core-maven-html/blob/develop/playwright/README.md#axebuilderdisablerulesliststring-rules) to temporarily disable individual rules until you're able to fix the issues.
|
||||||
|
|
||||||
|
You can find the rule IDs to pass to `disableRules()` in the `id` property of the violations you want to suppress. A [complete list of axe's rules](https://github.com/dequelabs/axe-core/blob/master/doc/rule-descriptions.md) can be found in `axe-core`'s documentation.
|
||||||
|
|
||||||
|
```java
|
||||||
|
AxeResults accessibilityScanResults = new AxeBuilder(page)
|
||||||
|
.disableRules(Arrays.asList("duplicate-id"))
|
||||||
|
.analyze();
|
||||||
|
|
||||||
|
assertEquals(Collections.emptyList(), accessibilityScanResults.getViolations());
|
||||||
|
```
|
||||||
|
|
||||||
|
### Using violation fingerprints to specific known issues
|
||||||
|
|
||||||
|
If you would like to allow for a more granular set of known issues, you can use the following pattern:
|
||||||
|
|
||||||
|
1. Perform an accessibility scan which is expected to find some known violations
|
||||||
|
1. Convert the violations into "violation fingerprint" objects
|
||||||
|
1. Assert that the set of fingerprints is equivalent to the expected ones
|
||||||
|
|
||||||
|
This approach avoids the downsides of using `AxeBuilder.exclude()` at the cost of slightly more complexity and fragility.
|
||||||
|
|
||||||
|
Here is an example of using fingerprints based on only rule IDs and "target" selectors pointing to each violation:
|
||||||
|
|
||||||
|
```java
|
||||||
|
@Test
|
||||||
|
shouldOnlyHaveAccessibilityViolationsMatchingKnownFingerprints() throws Exception {
|
||||||
|
page.navigate("https://your-site.com/");
|
||||||
|
|
||||||
|
AxeResults accessibilityScanResults = new AxeBuilder(page).analyze();
|
||||||
|
|
||||||
|
List<ViolationFingerprint> violationFingerprints = fingerprintsFromScanResults(accessibilityScanResults);
|
||||||
|
|
||||||
|
assertEquals(Arrays.asList(
|
||||||
|
new ViolationFingerprint("aria-roles", "[span[role=\"invalid\"]]"),
|
||||||
|
new ViolationFingerprint("color-contrast", "[li:nth-child(2) > span]"),
|
||||||
|
new ViolationFingerprint("label", "[input]")
|
||||||
|
), violationFingerprints);
|
||||||
|
}
|
||||||
|
|
||||||
|
// You can make your "fingerprint" as specific as you like. This one considers a violation to be
|
||||||
|
// "the same" if it corresponds the same Axe rule on the same element.
|
||||||
|
//
|
||||||
|
// Using a record type makes it easy to compare fingerprints with assertEquals
|
||||||
|
public record ViolationFingerprint(String ruleId, String target) { }
|
||||||
|
|
||||||
|
public List<ViolationFingerprint> fingerprintsFromScanResults(AxeResults results) {
|
||||||
|
return results.getViolations().stream()
|
||||||
|
// Each violation refers to one rule and multiple "nodes" which violate it
|
||||||
|
.flatMap(violation -> violation.getNodes().stream()
|
||||||
|
.map(node -> new ViolationFingerprint(
|
||||||
|
violation.getId(),
|
||||||
|
// Each node contains a "target", which is a CSS selector that uniquely identifies it
|
||||||
|
// If the page involves iframes or shadow DOMs, it may be a chain of CSS selectors
|
||||||
|
node.getTarget().toString()
|
||||||
|
)))
|
||||||
|
.collect(Collectors.toList());
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
## Using a test fixture for common axe configuration
|
||||||
|
|
||||||
|
A [`TestFixtures` class](./test-runners#running-tests-in-parallel) is a good way to share common `AxeBuilder` configuration across many tests. Some scenarios where this might be useful include:
|
||||||
|
* Using a common set of rules among all of your tests
|
||||||
|
* Suppressing a known violation in a common element which appears in many different pages
|
||||||
|
* Attaching standalone accessibility reports consistently for many scans
|
||||||
|
|
||||||
|
The following example demonstrates extending the `TestFixtures` class from the [Test Runners example](./test-runners#running-tests-in-parallel) with a new fixture that contains some common `AxeBuilder` configuration.
|
||||||
|
|
||||||
|
### Creating a fixture
|
||||||
|
|
||||||
|
This example fixture creates an `AxeBuilder` object which is pre-configured with shared `withTags()` and `exclude()` configuration.
|
||||||
|
|
||||||
|
```java
|
||||||
|
class AxeTestFixtures extends TestFixtures {
|
||||||
|
AxeBuilder makeAxeBuilder() {
|
||||||
|
return new AxeBuilder(page)
|
||||||
|
.withTags(['wcag2a', 'wcag2aa', 'wcag21a', 'wcag21aa'])
|
||||||
|
.exclude('#commonly-reused-element-with-known-issue');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### Using a fixture
|
||||||
|
|
||||||
|
To use the fixture, replace the earlier examples' `new AxeBuilder(page)` with the newly defined `makeAxeBuilder` fixture:
|
||||||
|
|
||||||
|
```java
|
||||||
|
public class HomepageTests extends AxeTestFixtures {
|
||||||
|
@Test
|
||||||
|
void exampleUsingCustomFixture() throws Exception {
|
||||||
|
page.navigate("https://your-site.com/");
|
||||||
|
|
||||||
|
AxeResults accessibilityScanResults = makeAxeBuilder()
|
||||||
|
// Automatically uses the shared AxeBuilder configuration,
|
||||||
|
// but supports additional test-specific configuration too
|
||||||
|
.include('#specific-element-under-test')
|
||||||
|
.analyze();
|
||||||
|
|
||||||
|
assertEquals(Collections.emptyList(), accessibilityScanResults.getViolations());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
@ -18,7 +18,7 @@ The following examples rely on the [`@axe-core/playwright`](https://npmjs.org/@a
|
||||||
|
|
||||||
Automated accessibility tests can detect some common accessibility problems such as missing or invalid properties. But many accessibility problems can only be discovered through manual testing. We recommend using a combination of automated testing, manual accessibility assessments, and inclusive user testing.
|
Automated accessibility tests can detect some common accessibility problems such as missing or invalid properties. But many accessibility problems can only be discovered through manual testing. We recommend using a combination of automated testing, manual accessibility assessments, and inclusive user testing.
|
||||||
|
|
||||||
For manual assessments, we recommend [Accessibility Insights for Web](https://accessibilityinsights.io/docs/web/overview/?referrer=playwright-accessibility-testing-js), a free and open source dev tool that walks you through assessing a website for [WCAG 2.1 AA](https://www.w3.org/WAI/WCAG21/quickref/?currentsidebar=%23col_customize&levels=aa) coverage.
|
For manual assessments, we recommend [Accessibility Insights for Web](https://accessibilityinsights.io/docs/web/overview/?referrer=playwright-accessibility-testing-js), a free and open source dev tool that walks you through assessing a website for [WCAG 2.1 AA](https://www.w3.org/WAI/WCAG21/quickref/?currentsidebar=%23col_customize&levels=aaa) coverage.
|
||||||
|
|
||||||
## Example accessibility tests
|
## Example accessibility tests
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -39,7 +39,7 @@ using Microsoft.Playwright.NUnit;
|
||||||
using Microsoft.Playwright;
|
using Microsoft.Playwright;
|
||||||
using NUnit.Framework;
|
using NUnit.Framework;
|
||||||
|
|
||||||
namespace Playwright.TestingHarnessTest.NUnit
|
namespace PlaywrightTests
|
||||||
{
|
{
|
||||||
|
|
||||||
public class TestGitHubAPI : PlaywrightTest
|
public class TestGitHubAPI : PlaywrightTest
|
||||||
|
|
@ -91,7 +91,7 @@ using Microsoft.Playwright.NUnit;
|
||||||
using Microsoft.Playwright;
|
using Microsoft.Playwright;
|
||||||
using NUnit.Framework;
|
using NUnit.Framework;
|
||||||
|
|
||||||
namespace Playwright.TestingHarnessTest.NUnit
|
namespace PlaywrightTests
|
||||||
{
|
{
|
||||||
|
|
||||||
public class TestGitHubAPI : PlaywrightTest
|
public class TestGitHubAPI : PlaywrightTest
|
||||||
|
|
@ -216,7 +216,7 @@ using Microsoft.Playwright.NUnit;
|
||||||
using Microsoft.Playwright;
|
using Microsoft.Playwright;
|
||||||
using NUnit.Framework;
|
using NUnit.Framework;
|
||||||
|
|
||||||
namespace Playwright.TestingHarnessTest.NUnit
|
namespace PlaywrightTests
|
||||||
{
|
{
|
||||||
|
|
||||||
public class TestGitHubAPI : PlaywrightTest
|
public class TestGitHubAPI : PlaywrightTest
|
||||||
|
|
|
||||||
|
|
@ -114,7 +114,7 @@ def test_should_create_feature_request(api_request_context: APIRequestContext) -
|
||||||
|
|
||||||
### Setup and teardown
|
### Setup and teardown
|
||||||
|
|
||||||
These tests assume that repository exists. You probably want to create a new one before running tests and delete it afterwards. Use a [session fixture](https://docs.pytest.org/en/6.2.x/fixture.html#fixture-scopes) for that. The part before `yield` is the before all and after is the after all.
|
These tests assume that repository exists. You probably want to create a new one before running tests and delete it afterwards. Use a [session fixture](https://docs.pytest.org/en/stable/fixture.html#fixture-scopes) for that. The part before `yield` is the before all and after is the after all.
|
||||||
|
|
||||||
```python
|
```python
|
||||||
# ...
|
# ...
|
||||||
|
|
|
||||||
|
|
@ -189,6 +189,7 @@ from a particular page, use [`event: Page.response`].
|
||||||
|
|
||||||
## event: BrowserContext.serviceWorker
|
## event: BrowserContext.serviceWorker
|
||||||
* since: v1.11
|
* since: v1.11
|
||||||
|
* langs: js, python
|
||||||
- argument: <[Worker]>
|
- argument: <[Worker]>
|
||||||
|
|
||||||
:::note
|
:::note
|
||||||
|
|
@ -1102,6 +1103,7 @@ A glob pattern, regular expression or predicate to match the request URL. Only r
|
||||||
|
|
||||||
## method: BrowserContext.serviceWorkers
|
## method: BrowserContext.serviceWorkers
|
||||||
* since: v1.11
|
* since: v1.11
|
||||||
|
* langs: js, python
|
||||||
- returns: <[Array]<[Worker]>>
|
- returns: <[Array]<[Worker]>>
|
||||||
|
|
||||||
:::note
|
:::note
|
||||||
|
|
|
||||||
|
|
@ -89,7 +89,7 @@ class BrowserTypeExamples
|
||||||
* since: v1.8
|
* since: v1.8
|
||||||
- returns: <[Browser]>
|
- returns: <[Browser]>
|
||||||
|
|
||||||
This method attaches Playwright to an existing browser instance.
|
This method attaches Playwright to an existing browser instance. When connecting to another browser launched via `BrowserType.launchServer` in Node.js, the major and minor version needs to match the client version (1.2.3 → is compatible with 1.2.x).
|
||||||
|
|
||||||
### param: BrowserType.connect.wsEndpoint
|
### param: BrowserType.connect.wsEndpoint
|
||||||
* since: v1.10
|
* since: v1.10
|
||||||
|
|
@ -283,7 +283,7 @@ use a temporary directory instead.
|
||||||
* langs: js
|
* langs: js
|
||||||
- returns: <[BrowserServer]>
|
- returns: <[BrowserServer]>
|
||||||
|
|
||||||
Returns the browser app instance.
|
Returns the browser app instance. You can connect to it via [`method: BrowserType.connect`], which requires the major/minor client/server version to match (1.2.3 → is compatible with 1.2.x).
|
||||||
|
|
||||||
Launches browser server that client can connect to. An example of launching a browser executable and connecting to it
|
Launches browser server that client can connect to. An example of launching a browser executable and connecting to it
|
||||||
later:
|
later:
|
||||||
|
|
|
||||||
|
|
@ -472,7 +472,7 @@ rowLocator
|
||||||
.screenshot();
|
.screenshot();
|
||||||
```
|
```
|
||||||
```python async
|
```python async
|
||||||
row_locator = page.lsocator("tr")
|
row_locator = page.locator("tr")
|
||||||
# ...
|
# ...
|
||||||
await row_locator
|
await row_locator
|
||||||
.filter(has_text="text in column 1")
|
.filter(has_text="text in column 1")
|
||||||
|
|
@ -480,7 +480,7 @@ await row_locator
|
||||||
.screenshot()
|
.screenshot()
|
||||||
```
|
```
|
||||||
```python sync
|
```python sync
|
||||||
row_locator = page.lsocator("tr")
|
row_locator = page.locator("tr")
|
||||||
# ...
|
# ...
|
||||||
row_locator
|
row_locator
|
||||||
.filter(has_text="text in column 1")
|
.filter(has_text="text in column 1")
|
||||||
|
|
|
||||||
|
|
@ -721,99 +721,6 @@ await Expect(locator).ToBeVisibleAsync();
|
||||||
### option: LocatorAssertions.toBeVisible.timeout = %%-csharp-java-python-assertions-timeout-%%
|
### option: LocatorAssertions.toBeVisible.timeout = %%-csharp-java-python-assertions-timeout-%%
|
||||||
* since: v1.18
|
* since: v1.18
|
||||||
|
|
||||||
## async method: LocatorAssertions.toContainClass
|
|
||||||
* since: v1.24
|
|
||||||
* langs:
|
|
||||||
- alias-java: containsClass
|
|
||||||
|
|
||||||
Ensures the [Locator] points to an element that contains the given CSS class (or multiple).
|
|
||||||
In contrast to [`method: LocatorAssertions.toHaveClass`] which requires that the [Locator] has exactly the provided classes, `toContainClass` verifies that the [Locator] has a subset (or all) of the given CSS classes.
|
|
||||||
|
|
||||||
```html
|
|
||||||
<div class='foo bar baz' id='component'>
|
|
||||||
<div class='item alice'></div>
|
|
||||||
<div class='item bob'></div>
|
|
||||||
</div>
|
|
||||||
```
|
|
||||||
|
|
||||||
```js
|
|
||||||
const locator = page.locator('#component');
|
|
||||||
await expect(locator).toContainClass('bar baz'); // pass, both classes are on element
|
|
||||||
await expect(locator).toContainClass('ba'); // fail, element has no 'ba' class
|
|
||||||
|
|
||||||
const itemLocator = page.locator('#component .item');
|
|
||||||
await expect(itemLocator).toContainClass(['alice', 'bob']); // pass, first element has alice, second bob
|
|
||||||
await expect(itemLocator).toContainClass(['alice', 'bob carl']); // no carl class found on second item element
|
|
||||||
await expect(itemLocator).toContainClass(['alice', 'bob', 'foobar']); // we expect 3 elements with the item class, but there are only 2
|
|
||||||
```
|
|
||||||
|
|
||||||
```java
|
|
||||||
Locator locator = page.locator("#component");
|
|
||||||
assertThat(locator).containsClass("bar baz"); // pass, both classes are on element
|
|
||||||
assertThat(locator).containsClass("ba"); // fail, element has no 'ba' class
|
|
||||||
|
|
||||||
Locator itemLocator = page.locator("#component .item");
|
|
||||||
assertThat(itemLocator).toContainClass(new String[] {"alice", "bob"}); // pass, first element has alice, second bob
|
|
||||||
assertThat(itemLocator).toContainClass(new String[] {"alice", "bob carl"}); // no carl class found on second item element
|
|
||||||
assertThat(itemLocator).toContainClass(new String[] {"alice", "bob", "foobar"}); // we expect 3 elements with the item class, but there are only 2
|
|
||||||
```
|
|
||||||
|
|
||||||
```python async
|
|
||||||
from playwright.async_api import expect
|
|
||||||
|
|
||||||
locator = page.locator('#component')
|
|
||||||
expect(locator).to_contain_class('bar baz') # pass, both classes are on element
|
|
||||||
expect(locator).to_contain_class('ba') # fail, element has no 'ba' class
|
|
||||||
|
|
||||||
item_locator = page.locator('#component .item')
|
|
||||||
expect(item_locator).to_contain_class(['alice', 'bob']) # pass, first element has alice, second bob
|
|
||||||
expect(item_locator).to_contain_class(['alice', 'bob carl']) # no carl class found on second item element
|
|
||||||
expect(item_locator).to_contain_class(['alice', 'bob', 'foobar']) # we expect 3 elements with the item class, but there are only 2
|
|
||||||
```
|
|
||||||
|
|
||||||
```python sync
|
|
||||||
from playwright.sync_api import expect
|
|
||||||
|
|
||||||
locator = page.locator('#component')
|
|
||||||
await expect(locator).to_contain_class('bar baz') # pass, both classes are on element
|
|
||||||
await expect(locator).to_contain_class('ba') # fail, element has no 'ba' class
|
|
||||||
|
|
||||||
item_locator = page.locator('#component .item')
|
|
||||||
await expect(item_locator).to_contain_class(['alice', 'bob']) # pass, first element has alice, second bob
|
|
||||||
await expect(item_locator).to_contain_class(['alice', 'bob carl']) # no carl class found on second item element
|
|
||||||
await expect(item_locator).to_contain_class(['alice', 'bob', 'foobar']) # we expect 3 elements with the item class, but there are only 2
|
|
||||||
```
|
|
||||||
|
|
||||||
```csharp
|
|
||||||
var locator = Page.Locator("#component");
|
|
||||||
await Expect(locator).ToContainClassAsync("bar baz"); // pass, both classes are on element
|
|
||||||
await Expect(locator).ToContainClassAsync("ba"); // fail, element has no "ba" class
|
|
||||||
|
|
||||||
var itemLocator = page.locator("#component .item");
|
|
||||||
await Expect(itemLocator).ToContainClassAsync(new string[]{"alice", "bob"}); // pass, first element has alice, second bob
|
|
||||||
await Expect(itemLocator).ToContainClassAsync(new string[]{"alice", "bob carl"}); // no carl class found on second item element
|
|
||||||
await Expect(itemLocator).ToContainClassAsync(new string[]{"alice", "bob", "foobar"}); // we expect 3 elements with the item class, but there are only 2
|
|
||||||
```
|
|
||||||
|
|
||||||
Note that locator must point to a single element when passing a string or to multiple elements when passing an array.
|
|
||||||
|
|
||||||
### param: LocatorAssertions.toContainClass.expected
|
|
||||||
* since: v1.24
|
|
||||||
- `expected` <[string]|[Array]<[string]>>
|
|
||||||
|
|
||||||
Expected classnames, whitespace separated. When passing an array, the given classes must be present on the locator elements.
|
|
||||||
|
|
||||||
### option: LocatorAssertions.toContainClass.ignoreCase
|
|
||||||
* since: v1.24
|
|
||||||
- `ignoreCase` <[boolean]>
|
|
||||||
|
|
||||||
Whether to perform case-insensitive match.
|
|
||||||
|
|
||||||
### option: LocatorAssertions.toContainClass.timeout = %%-js-assertions-timeout-%%
|
|
||||||
* since: v1.24
|
|
||||||
### option: LocatorAssertions.toContainClass.timeout = %%-csharp-java-python-assertions-timeout-%%
|
|
||||||
* since: v1.24
|
|
||||||
|
|
||||||
## async method: LocatorAssertions.toContainText
|
## async method: LocatorAssertions.toContainText
|
||||||
* since: v1.20
|
* since: v1.20
|
||||||
* langs:
|
* langs:
|
||||||
|
|
@ -977,7 +884,7 @@ Expected attribute value.
|
||||||
- alias-java: hasClass
|
- alias-java: hasClass
|
||||||
|
|
||||||
Ensures the [Locator] points to an element with given CSS classes. This needs to be a full match
|
Ensures the [Locator] points to an element with given CSS classes. This needs to be a full match
|
||||||
or using a relaxed regular expression. For matching partial class names, use [`method: LocatorAssertions.toContainClass`].
|
or using a relaxed regular expression.
|
||||||
|
|
||||||
```html
|
```html
|
||||||
<div class='selected row' id='component'></div>
|
<div class='selected row' id='component'></div>
|
||||||
|
|
|
||||||
|
|
@ -116,7 +116,7 @@ The opposite of [`method: PageAssertions.toHaveURL`].
|
||||||
* since: v1.18
|
* since: v1.18
|
||||||
- `urlOrRegExp` <[string]|[RegExp]>
|
- `urlOrRegExp` <[string]|[RegExp]>
|
||||||
|
|
||||||
Expected substring or RegExp.
|
Expected URL string or RegExp.
|
||||||
|
|
||||||
### option: PageAssertions.NotToHaveURL.timeout = %%-js-assertions-timeout-%%
|
### option: PageAssertions.NotToHaveURL.timeout = %%-js-assertions-timeout-%%
|
||||||
* since: v1.18
|
* since: v1.18
|
||||||
|
|
@ -285,7 +285,7 @@ await Expect(page).ToHaveURL(new Regex(".*checkout"));
|
||||||
* since: v1.18
|
* since: v1.18
|
||||||
- `urlOrRegExp` <[string]|[RegExp]>
|
- `urlOrRegExp` <[string]|[RegExp]>
|
||||||
|
|
||||||
Expected substring or RegExp.
|
Expected URL string or RegExp.
|
||||||
|
|
||||||
### option: PageAssertions.toHaveURL.timeout = %%-js-assertions-timeout-%%
|
### option: PageAssertions.toHaveURL.timeout = %%-js-assertions-timeout-%%
|
||||||
* since: v1.18
|
* since: v1.18
|
||||||
|
|
|
||||||
|
|
@ -53,7 +53,7 @@ using System.Threading.Tasks;
|
||||||
using Microsoft.Playwright.NUnit;
|
using Microsoft.Playwright.NUnit;
|
||||||
using NUnit.Framework;
|
using NUnit.Framework;
|
||||||
|
|
||||||
namespace Playwright.TestingHarnessTest.NUnit;
|
namespace PlaywrightTests;
|
||||||
|
|
||||||
public class ExampleTests : PageTest
|
public class ExampleTests : PageTest
|
||||||
{
|
{
|
||||||
|
|
|
||||||
|
|
@ -228,6 +228,7 @@ Returns the matching [Response] object, or `null` if the response was not receiv
|
||||||
|
|
||||||
## method: Request.serviceWorker
|
## method: Request.serviceWorker
|
||||||
* since: v1.24
|
* since: v1.24
|
||||||
|
* langs: js
|
||||||
- returns: <[null]|[Worker]>
|
- returns: <[null]|[Worker]>
|
||||||
|
|
||||||
:::note
|
:::note
|
||||||
|
|
|
||||||
|
|
@ -41,6 +41,18 @@ upon closing the browser context. This method throws when connected remotely.
|
||||||
Saves the video to a user-specified path. It is safe to call this method while the video
|
Saves the video to a user-specified path. It is safe to call this method while the video
|
||||||
is still in progress, or after the page has closed. This method waits until the page is closed and the video is fully saved.
|
is still in progress, or after the page has closed. This method waits until the page is closed and the video is fully saved.
|
||||||
|
|
||||||
|
## method: Video.saveAs
|
||||||
|
* langs: java
|
||||||
|
* since: v1.11
|
||||||
|
|
||||||
|
Saves the video to a user-specified path. This must be called after [`method: Page.close`] (or [`method: BrowserContext.close`]), otherwise an error will be thrown. This method waits until the video is fully saved.
|
||||||
|
|
||||||
|
## async method: Video.saveAs
|
||||||
|
* langs: python
|
||||||
|
* since: v1.11
|
||||||
|
|
||||||
|
Saves the video to a user-specified path. If using the sync API, this must be called after [`method: Page.close`] (or [`method: BrowserContext.close`]), otherwise an error will be thrown. If using the async API, it is safe to call this method while the video is still in progress, or after the page has closed. This method waits until the page is closed and the video is fully saved.
|
||||||
|
|
||||||
### param: Video.saveAs.path
|
### param: Video.saveAs.path
|
||||||
* since: v1.11
|
* since: v1.11
|
||||||
- `path` <[path]>
|
- `path` <[path]>
|
||||||
|
|
|
||||||
|
|
@ -148,14 +148,48 @@ var context = await browser.NewContextAsync(new()
|
||||||
```
|
```
|
||||||
|
|
||||||
### Code generation
|
### Code generation
|
||||||
|
* langs: js
|
||||||
|
|
||||||
Logging in via the UI and then reusing authentication state can be combined to
|
Logging in via the UI and then reusing authentication state can be combined to
|
||||||
implement **login once and run multiple scenarios**. The lifecycle looks like:
|
implement **login once and run multiple scenarios**. The lifecycle looks like:
|
||||||
|
|
||||||
1. Run tests (for example, with `npm run test`).
|
1. Run tests (for example, with `npm run test`).
|
||||||
1. Login via UI and retrieve authentication state.
|
2. Login via UI and retrieve authentication state.
|
||||||
* In Jest, this can be executed in [`globalSetup`](https://jestjs.io/docs/en/configuration#globalsetup-string).
|
* In Jest, this can be executed in [`globalSetup`](https://jestjs.io/docs/en/configuration#globalsetup-string).
|
||||||
1. In each test, load authentication state in `beforeEach` or `beforeAll` step.
|
3. In each test, load authentication state in `beforeEach` or `beforeAll` step.
|
||||||
|
|
||||||
|
This approach will also **work in CI environments**, since it does not rely on any external state.
|
||||||
|
|
||||||
|
### Code generation
|
||||||
|
* langs: python
|
||||||
|
|
||||||
|
Logging in via the UI and then reusing authentication state can be combined to implement **login once and run multiple scenarios**. The lifecycle looks like:
|
||||||
|
|
||||||
|
1. Run tests (for example, with `pytest`).
|
||||||
|
2. Login via UI and retrieve authentication state.
|
||||||
|
3. In each test, load authentication state using `autouse=True` fixture with `scope=function`.
|
||||||
|
|
||||||
|
This approach will also **work in CI environments**, since it does not rely on any external state.
|
||||||
|
|
||||||
|
### Code generation
|
||||||
|
* langs: csharp
|
||||||
|
|
||||||
|
Logging in via the UI and then reusing authentication state can be combined to implement **login once and run multiple scenarios**. The lifecycle looks like:
|
||||||
|
|
||||||
|
1. Run tests (for example, with `dotnet test`).
|
||||||
|
2. Login via UI and retrieve authentication state.
|
||||||
|
3. In each test, load authentication state in `SetUp`.
|
||||||
|
|
||||||
|
This approach will also **work in CI environments**, since it does not rely on any external state.
|
||||||
|
|
||||||
|
### Code generation
|
||||||
|
* langs: java
|
||||||
|
|
||||||
|
Logging in via the UI and then reusing authentication state can be combined to implement **login once and run multiple scenarios**. The lifecycle looks like:
|
||||||
|
|
||||||
|
1. Run tests (for example, with `mvn test`).
|
||||||
|
2. Login via UI and retrieve authentication state.
|
||||||
|
3. In each test, load authentication state in `@beforeEach` or `@beforeAll` step.
|
||||||
|
|
||||||
This approach will also **work in CI environments**, since it does not rely on any external state.
|
This approach will also **work in CI environments**, since it does not rely on any external state.
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -157,6 +157,19 @@ See all supported browsers:
|
||||||
pwsh bin\Debug\netX\playwright.ps1 install --help
|
pwsh bin\Debug\netX\playwright.ps1 install --help
|
||||||
```
|
```
|
||||||
|
|
||||||
|
## Install browsers via API
|
||||||
|
* langs: csharp
|
||||||
|
|
||||||
|
It's possible to run [Command line tools](./cli.md) commands via the .NET API:
|
||||||
|
|
||||||
|
```csharp
|
||||||
|
var exitCode = Microsoft.Playwright.Program.Main(new[] {"install"});
|
||||||
|
if (exitCode != 0)
|
||||||
|
{
|
||||||
|
throw new Exception($"Playwright exited with code {exitCode}");
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
## Managing browser binaries
|
## Managing browser binaries
|
||||||
|
|
||||||
Playwright downloads Chromium, WebKit and Firefox browsers into the OS-specific cache folders:
|
Playwright downloads Chromium, WebKit and Firefox browsers into the OS-specific cache folders:
|
||||||
|
|
|
||||||
|
|
@ -176,7 +176,7 @@ steps:
|
||||||
name: 'Playwright Tests'
|
name: 'Playwright Tests'
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
container:
|
container:
|
||||||
image: mcr.microsoft.com/playwright:v1.24.0-focal
|
image: mcr.microsoft.com/playwright:v1.24.2-focal
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v3
|
- uses: actions/checkout@v3
|
||||||
- uses: actions/setup-node@v2
|
- uses: actions/setup-node@v2
|
||||||
|
|
@ -194,7 +194,7 @@ steps:
|
||||||
name: 'Playwright Tests'
|
name: 'Playwright Tests'
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
container:
|
container:
|
||||||
image: mcr.microsoft.com/playwright:v1.24.0-focal
|
image: mcr.microsoft.com/playwright:v1.24.2-focal
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v3
|
- uses: actions/checkout@v3
|
||||||
- name: Set up Python
|
- name: Set up Python
|
||||||
|
|
@ -218,7 +218,7 @@ steps:
|
||||||
name: 'Playwright Tests'
|
name: 'Playwright Tests'
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
container:
|
container:
|
||||||
image: mcr.microsoft.com/playwright:v1.24.0-focal
|
image: mcr.microsoft.com/playwright:v1.24.2-focal
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v3
|
- uses: actions/checkout@v3
|
||||||
- uses: actions/setup-java@v3
|
- uses: actions/setup-java@v3
|
||||||
|
|
@ -239,7 +239,7 @@ steps:
|
||||||
name: 'Playwright Tests'
|
name: 'Playwright Tests'
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
container:
|
container:
|
||||||
image: mcr.microsoft.com/playwright:v1.24.0-focal
|
image: mcr.microsoft.com/playwright:v1.24.2-focal
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v3
|
- uses: actions/checkout@v3
|
||||||
- name: Setup dotnet
|
- name: Setup dotnet
|
||||||
|
|
@ -264,7 +264,7 @@ steps:
|
||||||
name: 'Playwright Tests - ${{ matrix.project }} - Shard ${{ matrix.shardIndex }} of ${{ matrix.shardTotal }}'
|
name: 'Playwright Tests - ${{ matrix.project }} - Shard ${{ matrix.shardIndex }} of ${{ matrix.shardTotal }}'
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
container:
|
container:
|
||||||
image: mcr.microsoft.com/playwright:v1.24.0-focal
|
image: mcr.microsoft.com/playwright:v1.24.2-focal
|
||||||
strategy:
|
strategy:
|
||||||
fail-fast: false
|
fail-fast: false
|
||||||
matrix:
|
matrix:
|
||||||
|
|
@ -297,7 +297,7 @@ Alternatively, you can use [Command line tools](./cli.md#install-system-dependen
|
||||||
pool:
|
pool:
|
||||||
vmImage: 'ubuntu-20.04'
|
vmImage: 'ubuntu-20.04'
|
||||||
|
|
||||||
container: mcr.microsoft.com/playwright:v1.24.0-focal
|
container: mcr.microsoft.com/playwright:v1.24.2-focal
|
||||||
|
|
||||||
steps:
|
steps:
|
||||||
...
|
...
|
||||||
|
|
@ -311,7 +311,7 @@ Running Playwright on CircleCI requires the following steps:
|
||||||
|
|
||||||
```yml
|
```yml
|
||||||
docker:
|
docker:
|
||||||
- image: mcr.microsoft.com/playwright:v1.24.0-focal
|
- image: mcr.microsoft.com/playwright:v1.24.2-focal
|
||||||
environment:
|
environment:
|
||||||
NODE_ENV: development # Needed if playwright is in `devDependencies`
|
NODE_ENV: development # Needed if playwright is in `devDependencies`
|
||||||
```
|
```
|
||||||
|
|
@ -333,7 +333,7 @@ to run tests on Jenkins.
|
||||||
|
|
||||||
```groovy
|
```groovy
|
||||||
pipeline {
|
pipeline {
|
||||||
agent { docker { image 'mcr.microsoft.com/playwright:v1.24.0-focal' } }
|
agent { docker { image 'mcr.microsoft.com/playwright:v1.24.2-focal' } }
|
||||||
stages {
|
stages {
|
||||||
stage('e2e-tests') {
|
stage('e2e-tests') {
|
||||||
steps {
|
steps {
|
||||||
|
|
@ -351,7 +351,7 @@ pipeline {
|
||||||
Bitbucket Pipelines can use public [Docker images as build environments](https://confluence.atlassian.com/bitbucket/use-docker-images-as-build-environments-792298897.html). To run Playwright tests on Bitbucket, use our public Docker image ([see Dockerfile](./docker.md)).
|
Bitbucket Pipelines can use public [Docker images as build environments](https://confluence.atlassian.com/bitbucket/use-docker-images-as-build-environments-792298897.html). To run Playwright tests on Bitbucket, use our public Docker image ([see Dockerfile](./docker.md)).
|
||||||
|
|
||||||
```yml
|
```yml
|
||||||
image: mcr.microsoft.com/playwright:v1.24.0-focal
|
image: mcr.microsoft.com/playwright:v1.24.2-focal
|
||||||
```
|
```
|
||||||
|
|
||||||
### GitLab CI
|
### GitLab CI
|
||||||
|
|
@ -364,7 +364,7 @@ stages:
|
||||||
|
|
||||||
tests:
|
tests:
|
||||||
stage: test
|
stage: test
|
||||||
image: mcr.microsoft.com/playwright:v1.24.0-focal
|
image: mcr.microsoft.com/playwright:v1.24.2-focal
|
||||||
script:
|
script:
|
||||||
...
|
...
|
||||||
```
|
```
|
||||||
|
|
|
||||||
|
|
@ -6,86 +6,186 @@ title: "Test Generator"
|
||||||
Playwright comes with the ability to generate tests out of the box and is a great way to quickly get started with testing. It will open two windows, a browser window where you interact with the website you wish to test and the Playwright Inspector window where you can record your tests, copy the tests, clear your tests as well as change the language of your tests.
|
Playwright comes with the ability to generate tests out of the box and is a great way to quickly get started with testing. It will open two windows, a browser window where you interact with the website you wish to test and the Playwright Inspector window where you can record your tests, copy the tests, clear your tests as well as change the language of your tests.
|
||||||
|
|
||||||
```bash js
|
```bash js
|
||||||
npx playwright codegen wikipedia.org
|
npx playwright codegen playwright.dev
|
||||||
```
|
```
|
||||||
|
|
||||||
```bash java
|
```bash java
|
||||||
mvn exec:java -e -Dexec.mainClass=com.microsoft.playwright.CLI -Dexec.args="codegen wikipedia.org"
|
mvn exec:java -e -Dexec.mainClass=com.microsoft.playwright.CLI -Dexec.args="codegen playwright.dev"
|
||||||
```
|
```
|
||||||
|
|
||||||
```bash python
|
```bash python
|
||||||
playwright codegen wikipedia.org
|
playwright codegen playwright.dev
|
||||||
```
|
```
|
||||||
|
|
||||||
```bash csharp
|
```bash csharp
|
||||||
pwsh bin\Debug\netX\playwright.ps1 codegen wikipedia.org
|
pwsh bin\Debug\netX\playwright.ps1 codegen playwright.dev
|
||||||
```
|
```
|
||||||
|
|
||||||
Run `codegen` and perform actions in the browser. Playwright will generate the code for the user interactions. `codegen` will attempt to generate resilient text-based selectors.
|
Run `codegen` and perform actions in the browser. Playwright will generate the code for the user interactions. `codegen` will attempt to generate resilient text-based selectors.
|
||||||
|
|
||||||
<img width="1916" alt="image" src="https://user-images.githubusercontent.com/13063165/177550119-4e202a56-7d8e-43ac-ad91-bf2f7b2579bd.png"/>
|
<img width="1183" alt="Codegen generating code for tests for playwright.dev website" src="https://user-images.githubusercontent.com/13063165/181852815-971c10da-0b55-4e54-8a73-77e1e825193c.png" />
|
||||||
|
|
||||||
|
|
||||||
|
## Emulate viewport size
|
||||||
|
|
||||||
|
Playwright opens a browser window with it's viewport set to a specific width and height and is not responsive as tests need to be run under the same conditions. Use the `--viewport` option to generate tests with a different viewport size.
|
||||||
|
|
||||||
|
```bash js
|
||||||
|
npx playwright codegen --viewport-size=800,600 playwright.dev
|
||||||
|
```
|
||||||
|
|
||||||
|
```bash java
|
||||||
|
mvn exec:java -e -Dexec.mainClass=com.microsoft.playwright.CLI -Dexec.args="codegen --viewport-size=800,600 playwright.dev"
|
||||||
|
```
|
||||||
|
|
||||||
|
```bash python
|
||||||
|
playwright codegen --viewport-size=800,600 playwright.dev
|
||||||
|
```
|
||||||
|
|
||||||
|
```bash csharp
|
||||||
|
pwsh bin\Debug\netX\playwright.ps1 codegen --viewport-size=800,600 playwright.dev
|
||||||
|
```
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
<img width="1409" alt="Codegen generating code for tests for playwright.dev website with a specific viewport" src="https://user-images.githubusercontent.com/13063165/182360039-6db79ad6-fe82-4fd6-900a-b5e25f7f720f.png" />
|
||||||
|
|
||||||
|
## Emulate devices
|
||||||
|
|
||||||
|
Record scripts and tests while emulating a mobile device using the `--device` option which sets the viewport size and user agent among others.
|
||||||
|
|
||||||
|
```bash js
|
||||||
|
npx playwright codegen --device="iPhone 11" playwright.dev
|
||||||
|
```
|
||||||
|
|
||||||
|
```bash java
|
||||||
|
mvn exec:java -e -Dexec.mainClass=com.microsoft.playwright.CLI -Dexec.args='codegen --device="iPhone 11" playwright.dev'
|
||||||
|
```
|
||||||
|
|
||||||
|
```bash python
|
||||||
|
playwright codegen --device="iPhone 11" playwright.dev
|
||||||
|
```
|
||||||
|
|
||||||
|
```bash csharp
|
||||||
|
pwsh bin\Debug\netX\playwright.ps1 codegen --device="iPhone 11" playwright.dev
|
||||||
|
```
|
||||||
|
|
||||||
|
<img width="1239" alt="Codegen generating code for tests for playwright.dev website emulated for iPhone 11" src="https://user-images.githubusercontent.com/13063165/182360089-9dc6d33d-480e-4bb2-86a3-fec51c1c228e.png" />
|
||||||
|
|
||||||
|
|
||||||
|
## Emulate color scheme
|
||||||
|
|
||||||
|
Record scripts and tests while emulating the color scheme with the `--color-scheme` option.
|
||||||
|
|
||||||
|
```bash js
|
||||||
|
npx playwright codegen --color-scheme=dark playwright.dev
|
||||||
|
```
|
||||||
|
|
||||||
|
```bash java
|
||||||
|
mvn exec:java -e -Dexec.mainClass=com.microsoft.playwright.CLI -Dexec.args="codegen --color-scheme=dark playwright.dev"
|
||||||
|
```
|
||||||
|
|
||||||
|
```bash python
|
||||||
|
playwright codegen --color-scheme=dark playwright.dev
|
||||||
|
```
|
||||||
|
|
||||||
|
```bash csharp
|
||||||
|
pwsh bin\Debug\netX\playwright.ps1 codegen --color-scheme=dark playwright.dev
|
||||||
|
```
|
||||||
|
|
||||||
|
<img width="1258" alt="Codegen generating code for tests for playwright.dev website in dark mode" src="https://user-images.githubusercontent.com/13063165/182359371-0bb4a7a2-abbb-4f73-8550-d67e0101f0ad.png" />
|
||||||
|
|
||||||
|
## Emulate geolocation, language and timezone
|
||||||
|
|
||||||
|
Record scripts and tests while emulating timezone, language & location using the `--timezone`, `--geolocation` and `--lang` options. Once page opens, click the "show your location" icon at them bottom right corner of the map to see geolocation in action.
|
||||||
|
|
||||||
|
```bash js
|
||||||
|
npx playwright codegen --timezone="Europe/Rome" --geolocation="41.890221,12.492348" --lang="it-IT" maps.google.com
|
||||||
|
```
|
||||||
|
|
||||||
|
```bash java
|
||||||
|
mvn exec:java -e -Dexec.mainClass=com.microsoft.playwright.CLI -Dexec.args='codegen --timezone="Europe/Rome" --geolocation="41.890221,12.492348" --lang="it-IT" maps.google.com'
|
||||||
|
```
|
||||||
|
|
||||||
|
```bash python
|
||||||
|
playwright codegen --timezone="Europe/Rome" --geolocation="41.890221,12.492348" --lang="it-IT" maps.google.com
|
||||||
|
```
|
||||||
|
|
||||||
|
```bash csharp
|
||||||
|
pwsh bin\Debug\netX\playwright.ps1 codegen --timezone="Europe/Rome" --geolocation="41.890221,12.492348" --lang="it-IT" maps.google.com
|
||||||
|
```
|
||||||
|
|
||||||
|
<img width="1276" alt="Codegen generating code for tests for google maps showing timezone, geoloation as Rome, Italy and in Italian language" src="https://user-images.githubusercontent.com/13063165/182394434-73e1c2a8-767e-411a-94e4-0912c1c50ecc.png" />
|
||||||
|
|
||||||
## Preserve authenticated state
|
## Preserve authenticated state
|
||||||
|
|
||||||
Run `codegen` with `--save-storage` to save [cookies](https://developer.mozilla.org/en-US/docs/Web/HTTP/Cookies) and [localStorage](https://developer.mozilla.org/en-US/docs/Web/API/Window/localStorage) at the end of the session. This is useful to separately record authentication step and reuse it later in the tests.
|
Run `codegen` with `--save-storage` to save [cookies](https://developer.mozilla.org/en-US/docs/Web/HTTP/Cookies) and [localStorage](https://developer.mozilla.org/en-US/docs/Web/API/Window/localStorage) at the end of the session. This is useful to separately record an authentication step and reuse it later in the tests.
|
||||||
|
|
||||||
|
After performing authentication and closing the browser, `auth.json` will contain the storage state.
|
||||||
|
|
||||||
```bash js
|
```bash js
|
||||||
npx playwright codegen --save-storage=auth.json
|
npx playwright codegen --save-storage=auth.json
|
||||||
# Perform authentication and exit.
|
|
||||||
# auth.json will contain the storage state.
|
|
||||||
```
|
```
|
||||||
|
|
||||||
```bash java
|
```bash java
|
||||||
mvn exec:java -e -Dexec.mainClass=com.microsoft.playwright.CLI -Dexec.args="codegen --save-storage=auth.json"
|
mvn exec:java -e -Dexec.mainClass=com.microsoft.playwright.CLI -Dexec.args="codegen --save-storage=auth.json"
|
||||||
# Perform authentication and exit.
|
|
||||||
# auth.json will contain the storage state.
|
|
||||||
```
|
```
|
||||||
|
|
||||||
```bash python
|
```bash python
|
||||||
playwright codegen --save-storage=auth.json
|
playwright codegen --save-storage=auth.json
|
||||||
# Perform authentication and exit.
|
|
||||||
# auth.json will contain the storage state.
|
|
||||||
```
|
```
|
||||||
|
|
||||||
```bash csharp
|
```bash csharp
|
||||||
pwsh bin\Debug\netX\playwright.ps1 codegen --save-storage=auth.json
|
pwsh bin\Debug\netX\playwright.ps1 codegen --save-storage=auth.json
|
||||||
# Perform authentication and exit.
|
|
||||||
# auth.json will contain the storage state.
|
|
||||||
```
|
```
|
||||||
|
|
||||||
Run with `--load-storage` to consume previously loaded storage. This way, all [cookies](https://developer.mozilla.org/en-US/docs/Web/HTTP/Cookies) and [localStorage](https://developer.mozilla.org/en-US/docs/Web/API/Window/localStorage) will be restored, bringing most web apps to the authenticated state.
|
<img width="1264" alt="Screenshot 2022-08-03 at 13 28 02" src="https://user-images.githubusercontent.com/13063165/182599605-df2fbd05-622b-4cd7-8a32-0abdfea7d38d.png" />
|
||||||
|
|
||||||
|
Run with `--load-storage` to consume previously loaded storage. This way, all [cookies](https://developer.mozilla.org/en-US/docs/Web/HTTP/Cookies) and [localStorage](https://developer.mozilla.org/en-US/docs/Web/API/Window/localStorage) will be restored, bringing most web apps to the authenticated state without the need to login again.
|
||||||
|
|
||||||
```bash js
|
```bash js
|
||||||
npx playwright open --load-storage=auth.json my.web.app
|
npx playwright codegen --load-storage=auth.json github.com/microsoft/playwright
|
||||||
npx playwright codegen --load-storage=auth.json my.web.app
|
|
||||||
# Perform actions in authenticated state.
|
|
||||||
```
|
```
|
||||||
|
|
||||||
```bash java
|
```bash java
|
||||||
mvn exec:java -e -Dexec.mainClass=com.microsoft.playwright.CLI -Dexec.args="open --load-storage=auth.json my.web.app"
|
mvn exec:java -e -Dexec.mainClass=com.microsoft.playwright.CLI -Dexec.args="codegen --load-storage=auth.json github.com/microsoft/playwright"
|
||||||
mvn exec:java -e -Dexec.mainClass=com.microsoft.playwright.CLI -Dexec.args="codegen --load-storage=auth.json my.web.app"
|
|
||||||
# Perform authentication and exit.
|
|
||||||
# auth.json will contain the storage state.
|
|
||||||
```
|
```
|
||||||
|
|
||||||
```bash python
|
```bash python
|
||||||
playwright open --load-storage=auth.json my.web.app
|
playwright codegen --load-storage=auth.json github.com/microsoft/playwright
|
||||||
playwright codegen --load-storage=auth.json my.web.app
|
|
||||||
# Perform actions in authenticated state.
|
|
||||||
```
|
```
|
||||||
|
|
||||||
```bash csharp
|
```bash csharp
|
||||||
pwsh bin\Debug\netX\playwright.ps1 open --load-storage=auth.json my.web.app
|
pwsh bin\Debug\netX\playwright.ps1 codegen --load-storage=auth.json github.com/microsoft/playwright
|
||||||
pwsh bin\Debug\netX\playwright.ps1 codegen --load-storage=auth.json my.web.app
|
|
||||||
# Perform actions in authenticated state.
|
|
||||||
```
|
```
|
||||||
|
|
||||||
|
<img width="1261" alt="Screenshot 2022-08-03 at 13 33 40" src="https://user-images.githubusercontent.com/13063165/182599680-05297b4e-c258-4416-8daa-b8637c1db120.png" />
|
||||||
|
|
||||||
|
Use the `open` command with `--load-storage` to open the saved `auth.json`.
|
||||||
|
|
||||||
|
```bash js
|
||||||
|
npx playwright open --load-storage=auth.json github.com/microsoft/playwright
|
||||||
|
```
|
||||||
|
|
||||||
|
```bash java
|
||||||
|
mvn exec:java -e -Dexec.mainClass=com.microsoft.playwright.CLI -Dexec.args="open --load-storage=auth.json github.com/microsoft/playwright"
|
||||||
|
```
|
||||||
|
|
||||||
|
```bash python
|
||||||
|
playwright open --load-storage=auth.json github.com/microsoft/playwright
|
||||||
|
```
|
||||||
|
|
||||||
|
```bash csharp
|
||||||
|
pwsh bin\Debug\netX\playwright.ps1 open --load-storage=auth.json github.com/microsoft/playwright
|
||||||
|
```
|
||||||
|
|
||||||
|
|
||||||
## Record using custom setup
|
## Record using custom setup
|
||||||
|
|
||||||
If you would like to use codegen in some non-standard setup (for example, use [`method: BrowserContext.route`]), it is possible to call [`method: Page.pause`] that will open a separate window with codegen controls.
|
If you would like to use codegen in some non-standard setup (for example, use [`method: BrowserContext.route`]), it is possible to call [`method: Page.pause`] that will open a separate window with codegen controls.
|
||||||
|
|
||||||
```js
|
```js
|
||||||
const { chromium } = require('playwright');
|
const { chromium } = require('@playwright/test');
|
||||||
|
|
||||||
(async () => {
|
(async () => {
|
||||||
// Make sure to run headed.
|
// Make sure to run headed.
|
||||||
|
|
@ -174,76 +274,6 @@ var page = await context.NewPageAsync();
|
||||||
await page.PauseAsync();
|
await page.PauseAsync();
|
||||||
```
|
```
|
||||||
|
|
||||||
## Emulate devices
|
## What's Next
|
||||||
|
|
||||||
You can record scripts and tests while emulating a device.
|
- [See a trace of your tests](./trace-viewer.md)
|
||||||
|
|
||||||
```bash js
|
|
||||||
# Emulate iPhone 11.
|
|
||||||
npx playwright codegen --device="iPhone 11" wikipedia.org
|
|
||||||
```
|
|
||||||
|
|
||||||
```bash java
|
|
||||||
# Emulate iPhone 11.
|
|
||||||
mvn exec:java -e -Dexec.mainClass=com.microsoft.playwright.CLI -Dexec.args='codegen --device="iPhone 11" wikipedia.org'
|
|
||||||
```
|
|
||||||
|
|
||||||
```bash python
|
|
||||||
# Emulate iPhone 11.
|
|
||||||
playwright codegen --device="iPhone 11" wikipedia.org
|
|
||||||
```
|
|
||||||
|
|
||||||
```bash csharp
|
|
||||||
# Emulate iPhone 11.
|
|
||||||
pwsh bin\Debug\netX\playwright.ps1 codegen --device="iPhone 11" wikipedia.org
|
|
||||||
```
|
|
||||||
|
|
||||||
## Emulate color scheme and viewport size
|
|
||||||
|
|
||||||
You can also record scripts and tests while emulating various browser properties.
|
|
||||||
|
|
||||||
```bash js
|
|
||||||
# Emulate screen size and color scheme.
|
|
||||||
npx playwright codegen --viewport-size=800,600 --color-scheme=dark twitter.com
|
|
||||||
```
|
|
||||||
|
|
||||||
```bash java
|
|
||||||
# Emulate screen size and color scheme.
|
|
||||||
mvn exec:java -e -Dexec.mainClass=com.microsoft.playwright.CLI -Dexec.args="codegen --viewport-size=800,600 --color-scheme=dark twitter.com"
|
|
||||||
```
|
|
||||||
|
|
||||||
```bash python
|
|
||||||
# Emulate screen size and color scheme.
|
|
||||||
playwright codegen --viewport-size=800,600 --color-scheme=dark twitter.com
|
|
||||||
```
|
|
||||||
|
|
||||||
```bash csharp
|
|
||||||
# Emulate screen size and color scheme.
|
|
||||||
pwsh bin\Debug\netX\playwright.ps1 codegen --viewport-size=800,600 --color-scheme=dark twitter.com
|
|
||||||
```
|
|
||||||
|
|
||||||
## Emulate geolocation, language and timezone
|
|
||||||
|
|
||||||
```bash js
|
|
||||||
# Emulate timezone, language & location
|
|
||||||
# Once page opens, click the "my location" button to see geolocation in action
|
|
||||||
npx playwright codegen --timezone="Europe/Rome" --geolocation="41.890221,12.492348" --lang="it-IT" maps.google.com
|
|
||||||
```
|
|
||||||
|
|
||||||
```bash java
|
|
||||||
# Emulate timezone, language & location
|
|
||||||
# Once page opens, click the "my location" button to see geolocation in action
|
|
||||||
mvn exec:java -e -Dexec.mainClass=com.microsoft.playwright.CLI -Dexec.args='codegen --timezone="Europe/Rome" --geolocation="41.890221,12.492348" --lang="it-IT" maps.google.com'
|
|
||||||
```
|
|
||||||
|
|
||||||
```bash python
|
|
||||||
# Emulate timezone, language & location
|
|
||||||
# Once page opens, click the "my location" button to see geolocation in action
|
|
||||||
playwright codegen --timezone="Europe/Rome" --geolocation="41.890221,12.492348" --lang="it-IT" maps.google.com
|
|
||||||
```
|
|
||||||
|
|
||||||
```bash csharp
|
|
||||||
# Emulate timezone, language & location
|
|
||||||
# Once page opens, click the "my location" button to see geolocation in action
|
|
||||||
pwsh bin\Debug\netX\playwright.ps1 codegen --timezone="Europe/Rome" --geolocation="41.890221,12.492348" --lang="it-IT" maps.google.com
|
|
||||||
```
|
|
||||||
|
|
|
||||||
156
docs/src/debug-selectors.md
Normal file
156
docs/src/debug-selectors.md
Normal file
|
|
@ -0,0 +1,156 @@
|
||||||
|
---
|
||||||
|
id: debug-selectors
|
||||||
|
title: "Debugging Selectors"
|
||||||
|
---
|
||||||
|
|
||||||
|
Playwright will throw a timeout exception like `locator.click: Timeout 30000ms exceeded` when an element does not exist on the page. There are multiple ways of debugging selectors:
|
||||||
|
|
||||||
|
- [Playwright Inspector](#using-playwright-inspector) to step over each Playwright API call to inspect the page.
|
||||||
|
- [Browser DevTools](#using-devtools) to inspect selectors with the DevTools element panel.
|
||||||
|
- [Trace Viewer](./trace-viewer.md) to see what the page looked like during the test run.
|
||||||
|
- [Verbose API logs](#verbose-api-logs) shows [actionability checks](./actionability.md) when locating the element.
|
||||||
|
|
||||||
|
## Using Playwright Inspector
|
||||||
|
|
||||||
|
Open the [Playwright Inspector](./debug.md) and click the `Explore` button to hover over elements in the screen and click them to
|
||||||
|
automatically generate selectors for those elements. To verify where selector points, paste it into the inspector input field:
|
||||||
|
|
||||||
|
<img width="602" alt="Selectors toolbar" src="https://user-images.githubusercontent.com/883973/108614696-ad5eaa00-73b1-11eb-81f5-9eebe62543a2.png"></img>
|
||||||
|
|
||||||
|
## Using DevTools
|
||||||
|
|
||||||
|
You can also use the following API inside the Developer Tools Console of any browser.
|
||||||
|
|
||||||
|
When running in Debug Mode with `PWDEBUG=console`, a `playwright` object is available in Developer tools console.
|
||||||
|
|
||||||
|
1. Run with `PWDEBUG=console`
|
||||||
|
1. Setup a breakpoint to pause the execution
|
||||||
|
1. Open the console panel in browser developer tools
|
||||||
|
|
||||||
|
<img src="https://user-images.githubusercontent.com/284612/92536317-37dd9380-f1ee-11ea-875d-daf1b206dd56.png"></img>
|
||||||
|
|
||||||
|
### playwright.$(selector)
|
||||||
|
|
||||||
|
Query Playwright selector, using the actual Playwright query engine, for example:
|
||||||
|
|
||||||
|
```txt
|
||||||
|
> playwright.$('.auth-form >> text=Log in');
|
||||||
|
|
||||||
|
<button>Log in</button>
|
||||||
|
```
|
||||||
|
|
||||||
|
### playwright.$$(selector)
|
||||||
|
|
||||||
|
Same as `playwright.$`, but returns all matching elements.
|
||||||
|
|
||||||
|
```txt
|
||||||
|
> playwright.$$('li >> text=John')
|
||||||
|
|
||||||
|
> [<li>, <li>, <li>, <li>]
|
||||||
|
```
|
||||||
|
|
||||||
|
### playwright.inspect(selector)
|
||||||
|
|
||||||
|
Reveal element in the Elements panel (if DevTools of the respective browser supports it).
|
||||||
|
|
||||||
|
```txt
|
||||||
|
> playwright.inspect('text=Log in')
|
||||||
|
```
|
||||||
|
|
||||||
|
### playwright.locator(selector)
|
||||||
|
|
||||||
|
Query Playwright element using the actual Playwright query engine, for example:
|
||||||
|
|
||||||
|
```txt
|
||||||
|
> playwright.locator('.auth-form', { hasText: 'Log in' });
|
||||||
|
|
||||||
|
> Locator ()
|
||||||
|
> - element: button
|
||||||
|
> - elements: [button]
|
||||||
|
```
|
||||||
|
|
||||||
|
### playwright.highlight(selector)
|
||||||
|
|
||||||
|
Highlight the first occurrence of the locator:
|
||||||
|
|
||||||
|
```txt
|
||||||
|
> playwright.hightlight('.auth-form');
|
||||||
|
```
|
||||||
|
|
||||||
|
### playwright.clear()
|
||||||
|
|
||||||
|
```txt
|
||||||
|
> playwright.clear()
|
||||||
|
```
|
||||||
|
|
||||||
|
Clear existing highlights.
|
||||||
|
|
||||||
|
### playwright.selector(element)
|
||||||
|
|
||||||
|
Generates selector for the given element.
|
||||||
|
|
||||||
|
```txt
|
||||||
|
> playwright.selector($0)
|
||||||
|
|
||||||
|
"div[id="glow-ingress-block"] >> text=/.*Hello.*/"
|
||||||
|
```
|
||||||
|
|
||||||
|
## Verbose API logs
|
||||||
|
|
||||||
|
Playwright supports verbose logging with the `DEBUG` environment variable.
|
||||||
|
|
||||||
|
```bash tab=bash-bash lang=js
|
||||||
|
DEBUG=pw:api npm run test
|
||||||
|
```
|
||||||
|
|
||||||
|
```batch tab=bash-batch lang=js
|
||||||
|
set DEBUG=pw:api
|
||||||
|
npm run test
|
||||||
|
```
|
||||||
|
|
||||||
|
```powershell tab=bash-powershell lang=js
|
||||||
|
$env:DEBUG="pw:api"
|
||||||
|
npm run test
|
||||||
|
```
|
||||||
|
|
||||||
|
```bash tab=bash-bash lang=java
|
||||||
|
DEBUG=pw:api mvn test
|
||||||
|
```
|
||||||
|
|
||||||
|
```batch tab=bash-batch lang=java
|
||||||
|
set DEBUG=pw:api
|
||||||
|
mvn test
|
||||||
|
```
|
||||||
|
|
||||||
|
```powershell tab=bash-powershell lang=java
|
||||||
|
$env:DEBUG="pw:api"
|
||||||
|
mvn test
|
||||||
|
```
|
||||||
|
|
||||||
|
```bash tab=bash-bash lang=python
|
||||||
|
DEBUG=pw:api pytest -s
|
||||||
|
```
|
||||||
|
|
||||||
|
```batch tab=bash-batch lang=python
|
||||||
|
set DEBUG=pw:api
|
||||||
|
pytest -s
|
||||||
|
```
|
||||||
|
|
||||||
|
```powershell tab=bash-powershell lang=python
|
||||||
|
$env:DEBUG="pw:api"
|
||||||
|
pytest -s
|
||||||
|
```
|
||||||
|
|
||||||
|
```bash tab=bash-bash lang=csharp
|
||||||
|
DEBUG=pw:api dotnet run
|
||||||
|
```
|
||||||
|
|
||||||
|
```batch tab=bash-batch lang=csharp
|
||||||
|
set DEBUG=pw:api
|
||||||
|
dotnet run
|
||||||
|
```
|
||||||
|
|
||||||
|
```powershell tab=bash-powershell lang=csharp
|
||||||
|
$env:DEBUG="pw:api"
|
||||||
|
dotnet run
|
||||||
|
```
|
||||||
|
|
@ -3,8 +3,7 @@ id: debug
|
||||||
title: "Debugging Tests"
|
title: "Debugging Tests"
|
||||||
---
|
---
|
||||||
|
|
||||||
The Playwright inspector is a great tool to help with debugging. It opens up a browser window highlighting the selectors as you step through each line of the test. You can also use the explore button to find other available [selectors](./selectors.md) which you can then copy into your test file and rerun your tests to see if it passes.
|
The Playwright inspector is a great tool to help with debugging. It opens up a browser window highlighting the selectors as you step through each line of the test. You can also use the explore button to find other available [selectors](./selectors.md) which you can then copy into your test file and rerun your tests to see if it passes. For debugging selectors, see [here](./debug-selectors.md).
|
||||||
|
|
||||||
|
|
||||||
## Playwright Inspector
|
## Playwright Inspector
|
||||||
|
|
||||||
|
|
@ -14,96 +13,99 @@ Playwright Inspector is a GUI tool that helps authoring and debugging Playwright
|
||||||
|
|
||||||
There are several ways of opening Playwright Inspector:
|
There are several ways of opening Playwright Inspector:
|
||||||
|
|
||||||
### Using --debug
|
### --debug
|
||||||
* langs: js
|
* langs: js
|
||||||
|
|
||||||
- Debugging all Tests
|
* Debugging all Tests
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
npx playwright test --debug
|
npx playwright test --debug
|
||||||
```
|
```
|
||||||
- Debugging one test
|
|
||||||
|
* Debugging one test
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
npx playwright test example --debug
|
npx playwright test example --debug
|
||||||
```
|
```
|
||||||
|
|
||||||
### Using PWDEBUG
|
### PWDEBUG
|
||||||
|
|
||||||
Set the `PWDEBUG` environment variable to run your scripts in debug mode. This
|
Set the `PWDEBUG` environment variable to run your scripts in debug mode. This
|
||||||
configures Playwright for debugging and opens the inspector.
|
configures Playwright for debugging and opens the inspector.
|
||||||
|
|
||||||
```bash tab=bash-bash lang=js
|
```bash tab=bash-bash lang=js
|
||||||
PWDEBUG=1 npm run test
|
PWDEBUG=1 npm run test
|
||||||
```
|
```
|
||||||
|
|
||||||
```batch tab=bash-batch lang=js
|
```batch tab=bash-batch lang=js
|
||||||
set PWDEBUG=1
|
set PWDEBUG=1
|
||||||
npm run test
|
npm run test
|
||||||
```
|
```
|
||||||
|
|
||||||
```powershell tab=bash-powershell lang=js
|
```powershell tab=bash-powershell lang=js
|
||||||
$env:PWDEBUG=1
|
$env:PWDEBUG=1
|
||||||
npm run test
|
npm run test
|
||||||
```
|
```
|
||||||
|
|
||||||
```bash tab=bash-bash lang=java
|
```bash tab=bash-bash lang=java
|
||||||
# Source directories in the list are separated by : on macos and linux and by ; on win.
|
# Source directories in the list are separated by : on macos and linux and by ; on win.
|
||||||
PWDEBUG=1 PLAYWRIGHT_JAVA_SRC=<java source dirs> mvn test
|
PWDEBUG=1 PLAYWRIGHT_JAVA_SRC=<java source dirs> mvn test
|
||||||
```
|
```
|
||||||
|
|
||||||
```batch tab=bash-batch lang=java
|
```batch tab=bash-batch lang=java
|
||||||
# Source directories in the list are separated by : on macos and linux and by ; on win.
|
# Source directories in the list are separated by : on macos and linux and by ; on win.
|
||||||
set PLAYWRIGHT_JAVA_SRC=<java source dirs>
|
set PLAYWRIGHT_JAVA_SRC=<java source dirs>
|
||||||
set PWDEBUG=1
|
set PWDEBUG=1
|
||||||
mvn test
|
mvn test
|
||||||
```
|
```
|
||||||
|
|
||||||
```powershell tab=bash-powershell lang=java
|
```powershell tab=bash-powershell lang=java
|
||||||
# Source directories in the list are separated by : on macos and linux and by ; on win.
|
# Source directories in the list are separated by : on macos and linux and by ; on win.
|
||||||
$env:PLAYWRIGHT_JAVA_SRC="<java source dirs>"
|
$env:PLAYWRIGHT_JAVA_SRC="<java source dirs>"
|
||||||
$env:PWDEBUG=1
|
$env:PWDEBUG=1
|
||||||
mvn test
|
mvn test
|
||||||
```
|
```
|
||||||
|
|
||||||
```bash tab=bash-bash lang=python
|
```bash tab=bash-bash lang=python
|
||||||
PWDEBUG=1 pytest -s
|
PWDEBUG=1 pytest -s
|
||||||
```
|
```
|
||||||
|
|
||||||
```batch tab=bash-batch lang=python
|
```batch tab=bash-batch lang=python
|
||||||
set PWDEBUG=1
|
set PWDEBUG=1
|
||||||
pytest -s
|
pytest -s
|
||||||
```
|
```
|
||||||
|
|
||||||
```powershell tab=bash-powershell lang=python
|
```powershell tab=bash-powershell lang=python
|
||||||
$env:PWDEBUG=1
|
$env:PWDEBUG=1
|
||||||
pytest -s
|
pytest -s
|
||||||
```
|
```
|
||||||
|
|
||||||
```bash tab=bash-bash lang=csharp
|
```bash tab=bash-bash lang=csharp
|
||||||
PWDEBUG=1 dotnet test
|
PWDEBUG=1 dotnet test
|
||||||
```
|
```
|
||||||
|
|
||||||
```batch tab=bash-batch lang=csharp
|
```batch tab=bash-batch lang=csharp
|
||||||
set PWDEBUG=1
|
set PWDEBUG=1
|
||||||
dotnet test
|
dotnet test
|
||||||
```
|
```
|
||||||
|
|
||||||
```powershell tab=bash-powershell lang=csharp
|
```powershell tab=bash-powershell lang=csharp
|
||||||
$env:PWDEBUG=1
|
$env:PWDEBUG=1
|
||||||
dotnet test
|
dotnet test
|
||||||
```
|
```
|
||||||
|
|
||||||
Additional useful defaults are configured when `PWDEBUG=1` is set:
|
Additional useful defaults are configured when `PWDEBUG=1` is set:
|
||||||
- Browsers launch in the headed mode
|
|
||||||
- Default timeout is set to 0 (= no timeout)
|
- Browsers launch in the headed mode
|
||||||
|
- Default timeout is set to 0 (= no timeout)
|
||||||
|
|
||||||
Using `PWDEBUG=console` will configure the browser for debugging in Developer tools console:
|
Using `PWDEBUG=console` will configure the browser for debugging in Developer tools console:
|
||||||
* **Runs headed**: Browsers always launch in headed mode
|
|
||||||
* **Disables timeout**: Sets default timeout to 0 (= no timeout)
|
- **Runs headed**: Browsers always launch in headed mode
|
||||||
* **Console helper**: Configures a `playwright` object in the browser to generate and highlight
|
- **Disables timeout**: Sets default timeout to 0 (= no timeout)
|
||||||
[Playwright selectors](./selectors.md). This can be used to verify text or
|
- **Console helper**: Configures a `playwright` object in the browser to generate and highlight
|
||||||
composite selectors.
|
[Playwright selectors](./selectors.md). This can be used to verify text or
|
||||||
|
composite selectors.
|
||||||
|
|
||||||
```bash tab=bash-bash lang=js
|
```bash tab=bash-bash lang=js
|
||||||
PWDEBUG=console npm run test
|
PWDEBUG=console npm run test
|
||||||
|
|
@ -147,86 +149,71 @@ $env:PWDEBUG="console"
|
||||||
pytest -s
|
pytest -s
|
||||||
```
|
```
|
||||||
|
|
||||||
#### Selectors in Developer Tools Console
|
### page.pause
|
||||||
|
|
||||||
When running in Debug Mode with `PWDEBUG=console`, a `playwright` object is available in Developer tools console.
|
|
||||||
|
|
||||||
1. Run with `PWDEBUG=console`
|
|
||||||
1. Setup a breakpoint to pause the execution
|
|
||||||
1. Open the console panel in browser developer tools
|
|
||||||
1. Use the `playwright` API
|
|
||||||
* `playwright.$(selector)`: Highlight the first occurrence of the selector. This reflects
|
|
||||||
how `page.$` would see the page.
|
|
||||||
* `playwright.$$(selector)`: Highlight all occurrences of the selector. This reflects
|
|
||||||
how `page.$$` would see the page.
|
|
||||||
* `playwright.inspect(selector)`: Inspect the selector in the Elements panel.
|
|
||||||
* `playwright.locator(selector)`: Highlight the first occurrence of the locator.
|
|
||||||
* `playwright.clear()`: Clear existing highlights.
|
|
||||||
* `playwright.selector(element)`: Generate a selector that points to the element.
|
|
||||||
|
|
||||||
<a href="https://user-images.githubusercontent.com/284612/86857345-299abc00-c073-11ea-9e31-02923a9f0d4b.png"><img src="https://user-images.githubusercontent.com/284612/86857345-299abc00-c073-11ea-9e31-02923a9f0d4b.png" width="500" alt="Highlight selectors"></img></a>
|
|
||||||
|
|
||||||
|
|
||||||
### Using page.pause
|
|
||||||
|
|
||||||
Call [`method: Page.pause`] method from your script when running in headed browser.
|
Call [`method: Page.pause`] method from your script when running in headed browser.
|
||||||
|
|
||||||
```js
|
```js
|
||||||
// Pause on the following line.
|
// Pause on the following line.
|
||||||
await page.pause();
|
await page.pause();
|
||||||
```
|
```
|
||||||
|
|
||||||
```java
|
```java
|
||||||
// Pause on the following line.
|
// Pause on the following line.
|
||||||
page.pause();
|
page.pause();
|
||||||
```
|
```
|
||||||
|
|
||||||
```python async
|
```python async
|
||||||
# Pause on the following line.
|
# Pause on the following line.
|
||||||
await page.pause()
|
await page.pause()
|
||||||
```
|
```
|
||||||
|
|
||||||
```python sync
|
```python sync
|
||||||
# Pause on the following line.
|
# Pause on the following line.
|
||||||
page.pause()
|
page.pause()
|
||||||
```
|
```
|
||||||
|
|
||||||
```csharp
|
```csharp
|
||||||
// Pause on the following line.
|
// Pause on the following line.
|
||||||
await page.PauseAsync();
|
await page.PauseAsync();
|
||||||
```
|
```
|
||||||
|
|
||||||
|
Use `open` or `codegen` commands in the Playwright [CLI](./cli.md):
|
||||||
|
|
||||||
- Use `open` or `codegen` commands in the Playwright [CLI](./cli.md):
|
```bash js
|
||||||
```bash js
|
npx playwright codegen wikipedia.org
|
||||||
npx playwright codegen wikipedia.org
|
```
|
||||||
```
|
|
||||||
|
|
||||||
```bash java
|
```bash java
|
||||||
mvn exec:java -e -Dexec.mainClass=com.microsoft.playwright.CLI -Dexec.args="codegen wikipedia.org"
|
mvn exec:java -e -Dexec.mainClass=com.microsoft.playwright.CLI -Dexec.args="codegen wikipedia.org"
|
||||||
```
|
```
|
||||||
|
|
||||||
```bash python
|
```bash python
|
||||||
playwright codegen wikipedia.org
|
playwright codegen wikipedia.org
|
||||||
```
|
```
|
||||||
|
|
||||||
```bash csharp
|
```bash csharp
|
||||||
pwsh bin\Debug\netX\playwright.ps1 codegen wikipedia.org
|
pwsh bin\Debug\netX\playwright.ps1 codegen wikipedia.org
|
||||||
```
|
```
|
||||||
|
|
||||||
## Stepping through the Playwright script
|
### Stepping through the Playwright script
|
||||||
|
|
||||||
When `PWDEBUG=1` is set, Playwright Inspector window will be opened and the script will be
|
The Inspector opens up a browser window highlighting the selectors as you step through each line of the test. Use the explore button to find other available [selectors](./selectors.md) which you can then copy into your test file and rerun your tests to see if they pass.
|
||||||
paused on the first Playwright statement:
|
|
||||||
|
|
||||||
<img width="557" alt="Paused on line" src="https://user-images.githubusercontent.com/883973/108614337-71761580-73ae-11eb-9f61-3d29c52c9520.png"></img>
|
<img width="557" alt="Paused on line" src="https://user-images.githubusercontent.com/883973/108614337-71761580-73ae-11eb-9f61-3d29c52c9520.png"></img>
|
||||||
|
|
||||||
|
Use the toolbar to play the test or step over each action using the "Step over" action (keyboard shortcut: `F10`) or resume script without further pauses (`F8`):
|
||||||
|
|
||||||
|
<center><img width="98" alt="Stepping toolbar" src="https://user-images.githubusercontent.com/883973/108614389-f9f4b600-73ae-11eb-8df2-8d9ce9da5d5c.png"></img></center>
|
||||||
|
|
||||||
Now we know what action is about to be performed and we can look into the details on that
|
Now we know what action is about to be performed and we can look into the details on that
|
||||||
action. For example, when stopped on an input action such as `click`, the exact point Playwright is about to click is highlighted with the large red dot on the inspected page:
|
action. For example, when stopped on an input action such as `click`, the exact point Playwright is about to click is highlighted with the large red dot on the inspected page:
|
||||||
|
|
||||||
<img width="344" alt="Red dot on inspected page" src="https://user-images.githubusercontent.com/883973/108614363-b69a4780-73ae-11eb-8f5e-51f9c91ec9b4.png"></img>
|
<img width="344" alt="Red dot on inspected page" src="https://user-images.githubusercontent.com/883973/108614363-b69a4780-73ae-11eb-8f5e-51f9c91ec9b4.png"></img>
|
||||||
|
|
||||||
By the time Playwright has paused on that click action, it has already performed actionability checks that can be found in the log:
|
### Actionability Logs
|
||||||
|
|
||||||
|
By the time Playwright has paused on that click action, it has already performed [actionability checks](./actionability.md) that can be found in the log:
|
||||||
|
|
||||||
<img width="712" alt="Action log" src="https://user-images.githubusercontent.com/883973/108614564-72a84200-73b0-11eb-9de2-828b28d78b36.png"></img>
|
<img width="712" alt="Action log" src="https://user-images.githubusercontent.com/883973/108614564-72a84200-73b0-11eb-9de2-828b28d78b36.png"></img>
|
||||||
|
|
||||||
|
|
@ -234,26 +221,26 @@ If actionability can't be reached, it'll show action as pending:
|
||||||
|
|
||||||
<img width="712" alt="Pending action" src="https://user-images.githubusercontent.com/883973/108614840-e6e3e500-73b2-11eb-998f-0cf31b2aa9a2.png"></img>
|
<img width="712" alt="Pending action" src="https://user-images.githubusercontent.com/883973/108614840-e6e3e500-73b2-11eb-998f-0cf31b2aa9a2.png"></img>
|
||||||
|
|
||||||
You can step over each action using the "Step over" action (keyboard shortcut: `F10`) or resume script without further pauses (`F8`):
|
### Exploring selectors
|
||||||
|
|
||||||
<center><img width="98" alt="Stepping toolbar" src="https://user-images.githubusercontent.com/883973/108614389-f9f4b600-73ae-11eb-8df2-8d9ce9da5d5c.png"></img></center>
|
|
||||||
|
|
||||||
|
Use the Explore button to hover over an element on the page and explore it's selector by clicking on it. You can then copy this selector into your tests and rerun your tests to see if they now pass with this selector. You can also debug selectors, checkout our [debugging selectors](./debug-selectors.md) guide for more details.
|
||||||
|
|
||||||
## Browser Developer Tools
|
## Browser Developer Tools
|
||||||
|
|
||||||
You can use browser developer tools in Chromium, Firefox and WebKit while running
|
You can use browser developer tools in Chromium, Firefox and WebKit while running
|
||||||
a Playwright script in headed mode. Developer tools help to:
|
a Playwright script in headed mode. Developer tools help to:
|
||||||
|
|
||||||
* Inspect the DOM tree and **find element selectors**
|
- Inspect the DOM tree and **find element selectors**
|
||||||
* **See console logs** during execution (or learn how to [read logs via API](./api/class-page.md#page-event-console))
|
- **See console logs** during execution (or learn how to [read logs via API](./api/class-page.md#page-event-console))
|
||||||
* Check **network activity** and other developer tools features
|
- Check **network activity** and other developer tools features
|
||||||
|
|
||||||
<a href="https://user-images.githubusercontent.com/284612/77234134-5f21a500-6b69-11ea-92ec-1c146e1333ec.png"><img src="https://user-images.githubusercontent.com/284612/77234134-5f21a500-6b69-11ea-92ec-1c146e1333ec.png" width="500" alt="Chromium Developer Tools"></img></a>
|
<a href="https://user-images.githubusercontent.com/284612/77234134-5f21a500-6b69-11ea-92ec-1c146e1333ec.png"><img src="https://user-images.githubusercontent.com/284612/77234134-5f21a500-6b69-11ea-92ec-1c146e1333ec.png" width="500" alt="Chromium Developer Tools"></img></a>
|
||||||
|
|
||||||
Using a [`method: Page.pause`] method is an easy way to pause the Playwright script execution
|
Using a [`method: Page.pause`] method is an easy way to pause the Playwright script execution
|
||||||
and inspect the page in Developer tools. It will also open [Playwright Inspector](./inspector.md) to help with debugging.
|
and inspect the page in Developer tools. It will also open Playwright Inspector to help with debugging.
|
||||||
|
|
||||||
**For Chromium**: you can also open developer tools through a launch option.
|
**For Chromium**: you can also open developer tools through a launch option.
|
||||||
|
|
||||||
```js
|
```js
|
||||||
await chromium.launch({ devtools: true });
|
await chromium.launch({ devtools: true });
|
||||||
```
|
```
|
||||||
|
|
@ -279,82 +266,10 @@ await using var browser = await playwright.Chromium.LaunchAsync(new()
|
||||||
|
|
||||||
:::note
|
:::note
|
||||||
**For WebKit**: launching WebKit Inspector during the execution will
|
**For WebKit**: launching WebKit Inspector during the execution will
|
||||||
prevent the Playwright script from executing any further.
|
prevent the Playwright script from executing any further.
|
||||||
:::
|
:::
|
||||||
|
|
||||||
## Debugging Selectors
|
## Headed mode
|
||||||
|
|
||||||
- Click the Explore button to hover over elements in the screen and click them to
|
|
||||||
automatically generate selectors for those elements.
|
|
||||||
- To verify where selector points, paste it into the inspector input field:
|
|
||||||
|
|
||||||
<img width="602" alt="Selectors toolbar" src="https://user-images.githubusercontent.com/883973/108614696-ad5eaa00-73b1-11eb-81f5-9eebe62543a2.png"></img>
|
|
||||||
|
|
||||||
You can also use the following API inside the Developer Tools Console of any browser.
|
|
||||||
|
|
||||||
<img src="https://user-images.githubusercontent.com/284612/92536317-37dd9380-f1ee-11ea-875d-daf1b206dd56.png"></img>
|
|
||||||
|
|
||||||
#### playwright.$(selector)
|
|
||||||
|
|
||||||
Query Playwright selector, using the actual Playwright query engine, for example:
|
|
||||||
|
|
||||||
```js
|
|
||||||
> playwright.$('.auth-form >> text=Log in');
|
|
||||||
|
|
||||||
<button>Log in</button>
|
|
||||||
```
|
|
||||||
|
|
||||||
#### playwright.$$(selector)
|
|
||||||
|
|
||||||
Same as `playwright.$`, but returns all matching elements.
|
|
||||||
|
|
||||||
```js
|
|
||||||
> playwright.$$('li >> text=John')
|
|
||||||
|
|
||||||
> [<li>, <li>, <li>, <li>]
|
|
||||||
```
|
|
||||||
|
|
||||||
#### playwright.inspect(selector)
|
|
||||||
|
|
||||||
Reveal element in the Elements panel (if DevTools of the respective browser supports it).
|
|
||||||
|
|
||||||
```js
|
|
||||||
> playwright.inspect('text=Log in')
|
|
||||||
```
|
|
||||||
|
|
||||||
#### playwright.locator(selector)
|
|
||||||
|
|
||||||
Query Playwright element using the actual Playwright query engine, for example:
|
|
||||||
|
|
||||||
```js
|
|
||||||
> playwright.locator('.auth-form', { hasText: 'Log in' });
|
|
||||||
|
|
||||||
> Locator ()
|
|
||||||
> - element: button
|
|
||||||
> - elements: [button]
|
|
||||||
```
|
|
||||||
|
|
||||||
#### playwright.selector(element)
|
|
||||||
|
|
||||||
Generates selector for the given element.
|
|
||||||
|
|
||||||
```js
|
|
||||||
> playwright.selector($0)
|
|
||||||
|
|
||||||
"div[id="glow-ingress-block"] >> text=/.*Hello.*/"
|
|
||||||
```
|
|
||||||
|
|
||||||
<!-- ## Recording scripts
|
|
||||||
|
|
||||||
At any moment, clicking Record action enables [codegen mode](./codegen.md).
|
|
||||||
Every action on the target page is turned into the generated script:
|
|
||||||
|
|
||||||
<img width="712" alt="Recorded script" src="https://user-images.githubusercontent.com/883973/108614897-85704600-73b3-11eb-8bcd-f2e129786c49.png"></img>
|
|
||||||
|
|
||||||
You can copy entire generated script or clear it using toolbar actions. -->
|
|
||||||
|
|
||||||
|
|
||||||
## Run Tests in headed mode
|
|
||||||
|
|
||||||
Playwright runs browsers in headless mode by default. To change this behavior,
|
Playwright runs browsers in headless mode by default. To change this behavior,
|
||||||
use `headless: false` as a launch option. You can also use the [`option: slowMo`] option
|
use `headless: false` as a launch option. You can also use the [`option: slowMo`] option
|
||||||
|
|
@ -372,12 +287,10 @@ chromium.launch(new BrowserType.LaunchOptions() // or firefox, webkit
|
||||||
|
|
||||||
```python async
|
```python async
|
||||||
await chromium.launch(headless=False, slow_mo=100) # or firefox, webkit
|
await chromium.launch(headless=False, slow_mo=100) # or firefox, webkit
|
||||||
|
|
||||||
```
|
```
|
||||||
|
|
||||||
```python sync
|
```python sync
|
||||||
chromium.launch(headless=False, slow_mo=100) # or firefox, webkit
|
chromium.launch(headless=False, slow_mo=100) # or firefox, webkit
|
||||||
|
|
||||||
```
|
```
|
||||||
|
|
||||||
```csharp
|
```csharp
|
||||||
|
|
@ -389,67 +302,4 @@ await using var browser = await playwright.Chromium.LaunchAsync(new()
|
||||||
});
|
});
|
||||||
```
|
```
|
||||||
|
|
||||||
## Verbose API logs
|
|
||||||
|
|
||||||
Playwright supports verbose logging with the `DEBUG` environment variable.
|
|
||||||
|
|
||||||
```bash tab=bash-bash lang=js
|
|
||||||
DEBUG=pw:api npm run test
|
|
||||||
```
|
|
||||||
|
|
||||||
```batch tab=bash-batch lang=js
|
|
||||||
set DEBUG=pw:api
|
|
||||||
npm run test
|
|
||||||
```
|
|
||||||
|
|
||||||
```powershell tab=bash-powershell lang=js
|
|
||||||
$env:DEBUG="pw:api"
|
|
||||||
npm run test
|
|
||||||
```
|
|
||||||
|
|
||||||
```bash tab=bash-bash lang=java
|
|
||||||
DEBUG=pw:api mvn test
|
|
||||||
```
|
|
||||||
|
|
||||||
```batch tab=bash-batch lang=java
|
|
||||||
set DEBUG=pw:api
|
|
||||||
mvn test
|
|
||||||
```
|
|
||||||
|
|
||||||
```powershell tab=bash-powershell lang=java
|
|
||||||
$env:DEBUG="pw:api"
|
|
||||||
mvn test
|
|
||||||
```
|
|
||||||
|
|
||||||
```bash tab=bash-bash lang=python
|
|
||||||
DEBUG=pw:api pytest -s
|
|
||||||
```
|
|
||||||
|
|
||||||
```batch tab=bash-batch lang=python
|
|
||||||
set DEBUG=pw:api
|
|
||||||
pytest -s
|
|
||||||
```
|
|
||||||
|
|
||||||
```powershell tab=bash-powershell lang=python
|
|
||||||
$env:DEBUG="pw:api"
|
|
||||||
pytest -s
|
|
||||||
```
|
|
||||||
|
|
||||||
```bash tab=bash-bash lang=csharp
|
|
||||||
DEBUG=pw:api dotnet run
|
|
||||||
```
|
|
||||||
|
|
||||||
```batch tab=bash-batch lang=csharp
|
|
||||||
set DEBUG=pw:api
|
|
||||||
dotnet run
|
|
||||||
```
|
|
||||||
|
|
||||||
```powershell tab=bash-powershell lang=csharp
|
|
||||||
$env:DEBUG="pw:api"
|
|
||||||
dotnet run
|
|
||||||
```
|
|
||||||
|
|
||||||
## What's Next
|
|
||||||
|
|
||||||
- [Generate tests with Codegen](./codegen.md)
|
|
||||||
- [See a trace of your tests](./trace-viewer.md)
|
|
||||||
|
|
@ -14,19 +14,19 @@ This image is published on [Docker Hub].
|
||||||
### Pull the image
|
### Pull the image
|
||||||
|
|
||||||
```bash js
|
```bash js
|
||||||
docker pull mcr.microsoft.com/playwright:v1.24.0-focal
|
docker pull mcr.microsoft.com/playwright:v1.24.2-focal
|
||||||
```
|
```
|
||||||
|
|
||||||
```bash python
|
```bash python
|
||||||
docker pull mcr.microsoft.com/playwright/python:v1.24.0-focal
|
docker pull mcr.microsoft.com/playwright/python:v1.24.2-focal
|
||||||
```
|
```
|
||||||
|
|
||||||
```bash csharp
|
```bash csharp
|
||||||
docker pull mcr.microsoft.com/playwright/dotnet:v1.24.0-focal
|
docker pull mcr.microsoft.com/playwright/dotnet:v1.24.2-focal
|
||||||
```
|
```
|
||||||
|
|
||||||
```bash java
|
```bash java
|
||||||
docker pull mcr.microsoft.com/playwright/java:v1.24.0-focal
|
docker pull mcr.microsoft.com/playwright/java:v1.24.2-focal
|
||||||
```
|
```
|
||||||
|
|
||||||
### Run the image
|
### Run the image
|
||||||
|
|
@ -38,19 +38,19 @@ By default, the Docker image will use the `root` user to run the browsers. This
|
||||||
On trusted websites, you can avoid creating a separate user and use root for it since you trust the code which will run on the browsers.
|
On trusted websites, you can avoid creating a separate user and use root for it since you trust the code which will run on the browsers.
|
||||||
|
|
||||||
```bash js
|
```bash js
|
||||||
docker run -it --rm --ipc=host mcr.microsoft.com/playwright:v1.24.0-focal /bin/bash
|
docker run -it --rm --ipc=host mcr.microsoft.com/playwright:v1.24.2-focal /bin/bash
|
||||||
```
|
```
|
||||||
|
|
||||||
```bash python
|
```bash python
|
||||||
docker run -it --rm --ipc=host mcr.microsoft.com/playwright/python:v1.24.0-focal /bin/bash
|
docker run -it --rm --ipc=host mcr.microsoft.com/playwright/python:v1.24.2-focal /bin/bash
|
||||||
```
|
```
|
||||||
|
|
||||||
```bash csharp
|
```bash csharp
|
||||||
docker run -it --rm --ipc=host mcr.microsoft.com/playwright/dotnet:v1.24.0-focal /bin/bash
|
docker run -it --rm --ipc=host mcr.microsoft.com/playwright/dotnet:v1.24.2-focal /bin/bash
|
||||||
```
|
```
|
||||||
|
|
||||||
```bash java
|
```bash java
|
||||||
docker run -it --rm --ipc=host mcr.microsoft.com/playwright/java:v1.24.0-focal /bin/bash
|
docker run -it --rm --ipc=host mcr.microsoft.com/playwright/java:v1.24.2-focal /bin/bash
|
||||||
```
|
```
|
||||||
|
|
||||||
#### Crawling and scraping
|
#### Crawling and scraping
|
||||||
|
|
@ -58,19 +58,19 @@ docker run -it --rm --ipc=host mcr.microsoft.com/playwright/java:v1.24.0-focal /
|
||||||
On untrusted websites, it's recommended to use a separate user for launching the browsers in combination with the seccomp profile. Inside the container or if you are using the Docker image as a base image you have to use `adduser` for it.
|
On untrusted websites, it's recommended to use a separate user for launching the browsers in combination with the seccomp profile. Inside the container or if you are using the Docker image as a base image you have to use `adduser` for it.
|
||||||
|
|
||||||
```bash js
|
```bash js
|
||||||
docker run -it --rm --ipc=host --user pwuser --security-opt seccomp=seccomp_profile.json mcr.microsoft.com/playwright:v1.24.0-focal /bin/bash
|
docker run -it --rm --ipc=host --user pwuser --security-opt seccomp=seccomp_profile.json mcr.microsoft.com/playwright:v1.24.2-focal /bin/bash
|
||||||
```
|
```
|
||||||
|
|
||||||
```bash python
|
```bash python
|
||||||
docker run -it --rm --ipc=host --user pwuser --security-opt seccomp=seccomp_profile.json mcr.microsoft.com/playwright/python:v1.24.0-focal /bin/bash
|
docker run -it --rm --ipc=host --user pwuser --security-opt seccomp=seccomp_profile.json mcr.microsoft.com/playwright/python:v1.24.2-focal /bin/bash
|
||||||
```
|
```
|
||||||
|
|
||||||
```bash csharp
|
```bash csharp
|
||||||
docker run -it --rm --ipc=host --user pwuser --security-opt seccomp=seccomp_profile.json mcr.microsoft.com/playwright/dotnet:v1.24.0-focal /bin/bash
|
docker run -it --rm --ipc=host --user pwuser --security-opt seccomp=seccomp_profile.json mcr.microsoft.com/playwright/dotnet:v1.24.2-focal /bin/bash
|
||||||
```
|
```
|
||||||
|
|
||||||
```bash java
|
```bash java
|
||||||
docker run -it --rm --ipc=host --user pwuser --security-opt seccomp=seccomp_profile.json mcr.microsoft.com/playwright/java:v1.24.0-focal /bin/bash
|
docker run -it --rm --ipc=host --user pwuser --security-opt seccomp=seccomp_profile.json mcr.microsoft.com/playwright/java:v1.24.2-focal /bin/bash
|
||||||
```
|
```
|
||||||
|
|
||||||
[`seccomp_profile.json`](https://github.com/microsoft/playwright/blob/main/utils/docker/seccomp_profile.json) is needed to run Chromium with sandbox. This is a [default Docker seccomp profile](https://github.com/docker/engine/blob/d0d99b04cf6e00ed3fc27e81fc3d94e7eda70af3/profiles/seccomp/default.json) with extra user namespace cloning permissions:
|
[`seccomp_profile.json`](https://github.com/microsoft/playwright/blob/main/utils/docker/seccomp_profile.json) is needed to run Chromium with sandbox. This is a [default Docker seccomp profile](https://github.com/docker/engine/blob/d0d99b04cf6e00ed3fc27e81fc3d94e7eda70af3/profiles/seccomp/default.json) with extra user namespace cloning permissions:
|
||||||
|
|
@ -121,6 +121,7 @@ It is recommended to always pin your Docker image to a specific version if possi
|
||||||
### Base images
|
### Base images
|
||||||
|
|
||||||
We currently publish images based on the following [Ubuntu](https://hub.docker.com/_/ubuntu) versions:
|
We currently publish images based on the following [Ubuntu](https://hub.docker.com/_/ubuntu) versions:
|
||||||
|
- **Ubuntu 22.04 LTS** (Jammy Jellyfish), image tags include `jammy` (not published for Java and .NET)
|
||||||
- **Ubuntu 20.04 LTS** (Focal Fossa), image tags include `focal`
|
- **Ubuntu 20.04 LTS** (Focal Fossa), image tags include `focal`
|
||||||
- **Ubuntu 18.04 LTS** (Bionic Beaver), image tags include `bionic` (not published for Java and .NET)
|
- **Ubuntu 18.04 LTS** (Bionic Beaver), image tags include `bionic` (not published for Java and .NET)
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -27,7 +27,9 @@ const [ download ] = await Promise.all([
|
||||||
page.locator('button#delayed-download').click(),
|
page.locator('button#delayed-download').click(),
|
||||||
]);
|
]);
|
||||||
// Wait for the download process to complete
|
// Wait for the download process to complete
|
||||||
const path = await download.path();
|
console.log(await download.path());
|
||||||
|
// Save downloaded file somewhere
|
||||||
|
await download.saveAs('/path/to/save/download/at.txt');
|
||||||
```
|
```
|
||||||
|
|
||||||
```java
|
```java
|
||||||
|
|
@ -38,6 +40,9 @@ Download download = page.waitForDownload(() -> {
|
||||||
});
|
});
|
||||||
// Wait for the download process to complete
|
// Wait for the download process to complete
|
||||||
Path path = download.path();
|
Path path = download.path();
|
||||||
|
System.out.println(download.path());
|
||||||
|
// Save downloaded file somewhere
|
||||||
|
download.saveAs(Paths.get("/path/to/save/download/at.txt"));
|
||||||
```
|
```
|
||||||
|
|
||||||
```python async
|
```python async
|
||||||
|
|
@ -47,7 +52,9 @@ async with page.expect_download() as download_info:
|
||||||
await page.locator("button#delayed-download").click()
|
await page.locator("button#delayed-download").click()
|
||||||
download = await download_info.value
|
download = await download_info.value
|
||||||
# Wait for the download process to complete
|
# Wait for the download process to complete
|
||||||
path = await download.path()
|
print(await download.path())
|
||||||
|
# Save downloaded file somewhere
|
||||||
|
download.save_as("/path/to/save/download/at.txt")
|
||||||
```
|
```
|
||||||
|
|
||||||
```python sync
|
```python sync
|
||||||
|
|
@ -57,7 +64,9 @@ with page.expect_download() as download_info:
|
||||||
page.locator("button#delayed-download").click()
|
page.locator("button#delayed-download").click()
|
||||||
download = download_info.value
|
download = download_info.value
|
||||||
# Wait for the download process to complete
|
# Wait for the download process to complete
|
||||||
path = download.path()
|
print(download.path())
|
||||||
|
# Save downloaded file somewhere
|
||||||
|
download.save_as("/path/to/save/download/at.txt")
|
||||||
```
|
```
|
||||||
|
|
||||||
```csharp
|
```csharp
|
||||||
|
|
@ -67,7 +76,9 @@ var waitForDownloadTask = page.WaitForDownloadAsync();
|
||||||
await page.Locator("#downloadButton").ClickAsync();
|
await page.Locator("#downloadButton").ClickAsync();
|
||||||
// Wait for the download process to complete
|
// Wait for the download process to complete
|
||||||
var download = await waitForDownloadTask;
|
var download = await waitForDownloadTask;
|
||||||
var path = await download.PathAsync();
|
Console.WriteLine(await download.PathAsync());
|
||||||
|
// Save downloaded file somewhere
|
||||||
|
await download.SaveAsAsync("/path/to/save/download/at.txt");
|
||||||
```
|
```
|
||||||
|
|
||||||
#### Variations
|
#### Variations
|
||||||
|
|
|
||||||
|
|
@ -5,55 +5,92 @@ title: "Getting started - VS Code"
|
||||||
|
|
||||||
Playwright Test was created specifically to accommodate the needs of end-to-end testing. Playwright supports all modern rendering engines including Chromium, WebKit, and Firefox. Test on Windows, Linux, and macOS, locally or on CI, headless or headed with native mobile emulation of Google Chrome for Android and Mobile Safari.
|
Playwright Test was created specifically to accommodate the needs of end-to-end testing. Playwright supports all modern rendering engines including Chromium, WebKit, and Firefox. Test on Windows, Linux, and macOS, locally or on CI, headless or headed with native mobile emulation of Google Chrome for Android and Mobile Safari.
|
||||||
|
|
||||||
Get started by installing Playwright and generating a test to see it in action.
|
Get started by installing Playwright and generating a test to see it in action. Alternatively you can also get started and run your tests using the [CLI](./intro.md).
|
||||||
|
|
||||||
## Installation
|
## Installation
|
||||||
|
|
||||||
Install the [VS Code extension from the marketplace](https://marketplace.visualstudio.com/items?itemName=ms-playwright.playwright).
|
Install the [VS Code extension from the marketplace](https://marketplace.visualstudio.com/items?itemName=ms-playwright.playwright).
|
||||||
|
|
||||||
<img width="535" alt="image" src="https://user-images.githubusercontent.com/13063165/177198887-de49ec12-a7a9-48c2-8d02-ad53ea312c91.png"></img>
|
<img width="535" alt="image" src="https://user-images.githubusercontent.com/13063165/182146928-b2a46ce5-3008-409c-be10-d2b255bd5e91.jpeg"></img>
|
||||||
|
|
||||||
Once installed, open the command panel and type "Install Playwright" and select "Test: Install Playwright". Choose the browsers you would like to run your tests on. These can be later configured in the [playwright.config file](./test-configuration.md) file.
|
Once installed, open the command panel and type:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
Install Playwright
|
||||||
|
```
|
||||||
|
|
||||||
<img width="538" alt="image" src="https://user-images.githubusercontent.com/13063165/177199115-ce90eb84-f12a-4b95-bd3a-17ff870fcec2.png"></img>
|
<img width="538" alt="image" src="https://user-images.githubusercontent.com/13063165/177199115-ce90eb84-f12a-4b95-bd3a-17ff870fcec2.png"></img>
|
||||||
|
|
||||||
|
Select "Test: Install Playwright" and Choose the browsers you would like to run your tests on. These can be later configured in the [playwright.config](./test-configuration.md) file. You can also choose if you would like to have a GitHub Actions setup to run your tests on CI.
|
||||||
|
|
||||||
## Generating Tests with Codegen
|
|
||||||
|
|
||||||
[CodeGen](./codegen.md) will auto generate your tests for you and is a great way to quickly get started. Click on the Testing icon in the left menu to open the testing sidebar. To record a test click on the record icon. This will create a `test-1.spec.ts` file as well as open up a browser window. As you record your user actions your test code will be generated in the newly created file.
|
|
||||||
|
|
||||||
<img width="810" alt="image" src="https://user-images.githubusercontent.com/13063165/177197869-40b32235-ae7c-4a6e-8b7e-e69aea17ea1b.png"></img>
|
|
||||||
|
|
||||||
As you hover over an element Playwright will highlight the element with the [selector](./selectors.md) shown underneath it. If you click the element [CodeGen](./codegen.md) will generate the test for you in the test file that was created.
|
|
||||||
<img width="958" alt="image" src="https://user-images.githubusercontent.com/13063165/177199982-42dc316f-3438-48b1-a6a6-417be77be658.png"></img>
|
|
||||||
|
|
||||||
|
|
||||||
## Running Tests
|
## Running Tests
|
||||||
|
|
||||||
You can run a single test by clicking the green triangle next to your test block to run your test. Playwright will run through each line of the test and when it finishes you will see a green tick next to your test block as well as the time it took to run the test.
|
You can run a single test by clicking the green triangle next to your test block to run your test. Playwright will run through each line of the test and when it finishes you will see a green tick next to your test block as well as the time it took to run the test.
|
||||||
|
|
||||||
<img width="813" alt="image" src="https://user-images.githubusercontent.com/13063165/177201109-e0a17553-88cc-496e-a717-9a60247db935.png"></img>
|
|
||||||
|
|
||||||
View all tests in the testing sidebar and extend the tests by clicking on each test. Tests that have not been run will not have the green check next to them.
|
|
||||||
|
|
||||||
<img width="812" alt="image" src="https://user-images.githubusercontent.com/13063165/177201231-f26e11da-2860-43fa-9a31-b04bba55d52e.png" />
|
<img width="750" alt="image" src="https://user-images.githubusercontent.com/13063165/182153398-101bf809-deca-40f8-9ac7-314eab2ff119.png" />
|
||||||
|
|
||||||
Run all tests by clicking on the white triangle as you hover over the tests in the testing sidebar.
|
### View and Run All Tests
|
||||||
|
|
||||||
<img width="252" alt="image" src="https://user-images.githubusercontent.com/13063165/178029941-d9555c43-0966-4699-8739-612a9664e604.png" />
|
View all tests in the testing sidebar and extend the tests by clicking on each test. Tests that have not been run will not have the green check next to them. Run all tests by clicking on the white triangle as you hover over the tests in the testing sidebar.
|
||||||
|
|
||||||
|
<img width="755" alt="image" src="https://user-images.githubusercontent.com/13063165/182154055-6ff7af95-3787-475e-b0c0-8aa521aaa31b.png" />
|
||||||
|
|
||||||
|
|
||||||
|
### Run Tests on Specific Browsers
|
||||||
|
|
||||||
The VS Code test runner runs your tests on the default browser of Chrome. To run on other/multiple browsers click the play button's dropdown and choose the option of "Select Default Profile" and select the browsers you wish to run your tests on.
|
The VS Code test runner runs your tests on the default browser of Chrome. To run on other/multiple browsers click the play button's dropdown and choose the option of "Select Default Profile" and select the browsers you wish to run your tests on.
|
||||||
|
|
||||||
<img width="506" alt="image" src="https://user-images.githubusercontent.com/13063165/178030111-3c422349-a501-4190-9ad6-ec0bdc187b9e.png" />
|
<img width="753" alt="image" src="https://user-images.githubusercontent.com/13063165/182154251-89f8d4f1-a9c3-42bc-9659-7db6412e96fe.png" />
|
||||||
|
|
||||||
## Debugging Tests
|
## Debugging Tests
|
||||||
|
|
||||||
With the VS Code extension you can debug your tests right in VS Code see error messages and create breakpoints. Click next to the line number so a red dot appears and then run the tests in debug mode by right clicking on the line next to the test you want to run. A browser window will open and the test will run and pause at where the breakpoint is set.
|
With the VS Code extension you can debug your tests right in VS Code see error messages, create breakpoints and live debug your tests.
|
||||||
|
|
||||||
<img width="1025" alt="image" src="https://user-images.githubusercontent.com/13063165/178027941-0d9d5f88-2426-43fb-b204-62a2add27415.png" />
|
### Error Messages
|
||||||
|
|
||||||
Modify your test right in VS Code while debugging and Playwright will highlight the selector you are modifying in the browser. You can step through the tests, pause the test and rerun the tests from the menu in VS Code.
|
If your test fails VS Code will show you error messages right in the editor showing what was expected, what was received as well as a complete call log.
|
||||||
|
|
||||||
<img width="1044" alt="image" src="https://user-images.githubusercontent.com/13063165/178029249-e0a85f53-b8d4-451f-b3e5-df62b0c57929.png" />
|
<img width="848" alt="image" src="https://user-images.githubusercontent.com/13063165/182155225-d91ec237-f69e-4ace-9a5f-a149800aba75.png" />
|
||||||
|
|
||||||
|
### Run in Debug Mode
|
||||||
|
|
||||||
|
To set a breakpoint click next to the line number where you want the breakpoint to be until a red dot appears. Run the tests in debug mode by right clicking on the line next to the test you want to run. A browser window will open and the test will run and pause at where the breakpoint is set.
|
||||||
|
|
||||||
|
<img width="847" alt="image" src="https://user-images.githubusercontent.com/13063165/182156149-f683f62d-9555-4ce2-93d2-e80de8087411.png" />
|
||||||
|
|
||||||
|
|
||||||
|
### Live Debugging
|
||||||
|
|
||||||
|
You can modify your test right in VS Code while debugging and Playwright will highlight the selector in the browser. This is a great way of seeing if the selector exits or if there is more than one result. You can step through the tests, pause the test and rerun the tests from the menu in VS Code.
|
||||||
|
|
||||||
|
<img width="858" alt="image" src="https://user-images.githubusercontent.com/13063165/182157241-c8da5eff-edbc-4ae1-80e3-8e42fa5fe659.png" />
|
||||||
|
|
||||||
|
|
||||||
|
## Generating Tests
|
||||||
|
|
||||||
|
CodeGen will auto generate your tests for you as you perform actions in the browser and is a great way to quickly get started. The viewport for the browser window is set to a specific width and height. See the [configuration guide](./test-configuration.md) to change the viewport or emulate different environments.
|
||||||
|
|
||||||
|
### Recording a Test
|
||||||
|
|
||||||
|
To record a test click on the record icon. This will create a `test-1.spec.ts` file as well as open up a browser window.
|
||||||
|
|
||||||
|
|
||||||
|
<img width="798" alt="image" src="https://user-images.githubusercontent.com/13063165/182149486-a30fbd3f-5e88-4ac2-b1df-4e33d4a893c7.png" />
|
||||||
|
|
||||||
|
### Selector Highlighting
|
||||||
|
|
||||||
|
As you interact with the page Codegen will generate the test for you in the newly created file in VS Code. When you hover over an element Playwright will highlight the element and show the [selector](./selectors.md) underneath it.
|
||||||
|
|
||||||
|
<img width="860" alt="image" src="https://user-images.githubusercontent.com/13063165/182151374-03273172-38cd-4f27-add5-cb3d3cdc7bcd.png" />
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
## What's next
|
||||||
|
|
||||||
|
- [Write tests using web first assertions, page fixtures and locators](./writing-tests.md)
|
||||||
|
- [See test reports](./running-tests.md#test-reports)
|
||||||
|
- [See a trace of your tests](./trace-viewer.md)
|
||||||
|
|
|
||||||
|
|
@ -1,236 +0,0 @@
|
||||||
---
|
|
||||||
id: inspector
|
|
||||||
title: "Inspector"
|
|
||||||
---
|
|
||||||
|
|
||||||
Playwright Inspector is a GUI tool that helps authoring and debugging Playwright scripts.
|
|
||||||
|
|
||||||
<img width="712" alt="Playwright Inspector" src="https://user-images.githubusercontent.com/883973/108614092-8c478a80-73ac-11eb-9597-67dfce110e00.png"></img>
|
|
||||||
|
|
||||||
<!-- TOC -->
|
|
||||||
|
|
||||||
## Open Playwright Inspector
|
|
||||||
|
|
||||||
There are several ways of opening Playwright Inspector:
|
|
||||||
|
|
||||||
- Set the `PWDEBUG` environment variable to run your scripts in debug mode. This
|
|
||||||
configures Playwright for debugging and opens the inspector.
|
|
||||||
|
|
||||||
```bash tab=bash-bash lang=js
|
|
||||||
PWDEBUG=1 npm run test
|
|
||||||
```
|
|
||||||
|
|
||||||
```batch tab=bash-batch lang=js
|
|
||||||
set PWDEBUG=1
|
|
||||||
npm run test
|
|
||||||
```
|
|
||||||
|
|
||||||
```powershell tab=bash-powershell lang=js
|
|
||||||
$env:PWDEBUG=1
|
|
||||||
npm run test
|
|
||||||
```
|
|
||||||
|
|
||||||
```bash tab=bash-bash lang=java
|
|
||||||
# Source directories in the list are separated by : on macos and linux and by ; on win.
|
|
||||||
PWDEBUG=1 PLAYWRIGHT_JAVA_SRC=<java source dirs> mvn test
|
|
||||||
```
|
|
||||||
|
|
||||||
```batch tab=bash-batch lang=java
|
|
||||||
# Source directories in the list are separated by : on macos and linux and by ; on win.
|
|
||||||
set PLAYWRIGHT_JAVA_SRC=<java source dirs>
|
|
||||||
set PWDEBUG=1
|
|
||||||
mvn test
|
|
||||||
```
|
|
||||||
|
|
||||||
```powershell tab=bash-powershell lang=java
|
|
||||||
# Source directories in the list are separated by : on macos and linux and by ; on win.
|
|
||||||
$env:PLAYWRIGHT_JAVA_SRC="<java source dirs>"
|
|
||||||
$env:PWDEBUG=1
|
|
||||||
mvn test
|
|
||||||
```
|
|
||||||
|
|
||||||
```bash tab=bash-bash lang=python
|
|
||||||
PWDEBUG=1 pytest -s
|
|
||||||
```
|
|
||||||
|
|
||||||
```batch tab=bash-batch lang=python
|
|
||||||
set PWDEBUG=1
|
|
||||||
pytest -s
|
|
||||||
```
|
|
||||||
|
|
||||||
```powershell tab=bash-powershell lang=python
|
|
||||||
$env:PWDEBUG=1
|
|
||||||
pytest -s
|
|
||||||
```
|
|
||||||
|
|
||||||
```bash tab=bash-bash lang=csharp
|
|
||||||
PWDEBUG=1 dotnet test
|
|
||||||
```
|
|
||||||
|
|
||||||
```batch tab=bash-batch lang=csharp
|
|
||||||
set PWDEBUG=1
|
|
||||||
dotnet test
|
|
||||||
```
|
|
||||||
|
|
||||||
```powershell tab=bash-powershell lang=csharp
|
|
||||||
$env:PWDEBUG=1
|
|
||||||
dotnet test
|
|
||||||
```
|
|
||||||
|
|
||||||
Additional useful defaults are configured when `PWDEBUG=1` is set:
|
|
||||||
- Browsers launch in the headed mode
|
|
||||||
- Default timeout is set to 0 (= no timeout)
|
|
||||||
|
|
||||||
- Call [`method: Page.pause`] method from your script when running in headed browser.
|
|
||||||
|
|
||||||
```js
|
|
||||||
// Pause on the following line.
|
|
||||||
await page.pause();
|
|
||||||
```
|
|
||||||
|
|
||||||
```java
|
|
||||||
// Pause on the following line.
|
|
||||||
page.pause();
|
|
||||||
```
|
|
||||||
|
|
||||||
```python async
|
|
||||||
# Pause on the following line.
|
|
||||||
await page.pause()
|
|
||||||
```
|
|
||||||
|
|
||||||
```python sync
|
|
||||||
# Pause on the following line.
|
|
||||||
page.pause()
|
|
||||||
```
|
|
||||||
|
|
||||||
```csharp
|
|
||||||
// Pause on the following line.
|
|
||||||
await page.PauseAsync();
|
|
||||||
```
|
|
||||||
|
|
||||||
|
|
||||||
- Use `open` or `codegen` commands in the Playwright [CLI](./cli.md):
|
|
||||||
```bash js
|
|
||||||
npx playwright codegen wikipedia.org
|
|
||||||
```
|
|
||||||
|
|
||||||
```bash java
|
|
||||||
mvn exec:java -e -Dexec.mainClass=com.microsoft.playwright.CLI -Dexec.args="codegen wikipedia.org"
|
|
||||||
```
|
|
||||||
|
|
||||||
```bash python
|
|
||||||
playwright codegen wikipedia.org
|
|
||||||
```
|
|
||||||
|
|
||||||
```bash csharp
|
|
||||||
pwsh bin\Debug\netX\playwright.ps1 codegen wikipedia.org
|
|
||||||
```
|
|
||||||
|
|
||||||
## Stepping through the Playwright script
|
|
||||||
|
|
||||||
When `PWDEBUG=1` is set, Playwright Inspector window will be opened and the script will be
|
|
||||||
paused on the first Playwright statement:
|
|
||||||
|
|
||||||
<img width="557" alt="Paused on line" src="https://user-images.githubusercontent.com/883973/108614337-71761580-73ae-11eb-9f61-3d29c52c9520.png"></img>
|
|
||||||
|
|
||||||
Now we know what action is about to be performed and we can look into the details on that
|
|
||||||
action. For example, when stopped on an input action such as `click`, the exact point Playwright is about to click is highlighted with the large red dot on the inspected page:
|
|
||||||
|
|
||||||
<img width="344" alt="Red dot on inspected page" src="https://user-images.githubusercontent.com/883973/108614363-b69a4780-73ae-11eb-8f5e-51f9c91ec9b4.png"></img>
|
|
||||||
|
|
||||||
By the time Playwright has paused on that click action, it has already performed actionability checks that can be found in the log:
|
|
||||||
|
|
||||||
<img width="712" alt="Action log" src="https://user-images.githubusercontent.com/883973/108614564-72a84200-73b0-11eb-9de2-828b28d78b36.png"></img>
|
|
||||||
|
|
||||||
If actionability can't be reached, it'll show action as pending:
|
|
||||||
|
|
||||||
<img width="712" alt="Pending action" src="https://user-images.githubusercontent.com/883973/108614840-e6e3e500-73b2-11eb-998f-0cf31b2aa9a2.png"></img>
|
|
||||||
|
|
||||||
You can step over each action using the "Step over" action (keyboard shortcut: `F10`) or resume script without further pauses (`F8`):
|
|
||||||
|
|
||||||
<center><img width="98" alt="Stepping toolbar" src="https://user-images.githubusercontent.com/883973/108614389-f9f4b600-73ae-11eb-8df2-8d9ce9da5d5c.png"></img></center>
|
|
||||||
|
|
||||||
## Using Browser Developer Tools
|
|
||||||
|
|
||||||
You can use browser developer tools in Chromium, Firefox and WebKit while running
|
|
||||||
a Playwright script, with or without Playwright inspector. Developer tools help to:
|
|
||||||
|
|
||||||
* Inspect the DOM tree
|
|
||||||
* **See console logs** during execution (or learn how to [read logs via API](./api/class-page.md#page-event-console))
|
|
||||||
* Check **network activity** and other developer tools features
|
|
||||||
|
|
||||||
:::note
|
|
||||||
**For WebKit**: launching WebKit Inspector during the execution will
|
|
||||||
prevent the Playwright script from executing any further.
|
|
||||||
:::
|
|
||||||
|
|
||||||
## Debugging Selectors
|
|
||||||
|
|
||||||
- Click the Explore button to hover over elements in the screen and click them to
|
|
||||||
automatically generate selectors for those elements.
|
|
||||||
- To verify where selector points, paste it into the inspector input field:
|
|
||||||
|
|
||||||
<img width="602" alt="Selectors toolbar" src="https://user-images.githubusercontent.com/883973/108614696-ad5eaa00-73b1-11eb-81f5-9eebe62543a2.png"></img>
|
|
||||||
|
|
||||||
You can also use the following API inside the Developer Tools Console of any browser.
|
|
||||||
|
|
||||||
<img src="https://user-images.githubusercontent.com/284612/92536317-37dd9380-f1ee-11ea-875d-daf1b206dd56.png"></img>
|
|
||||||
|
|
||||||
#### playwright.$(selector)
|
|
||||||
|
|
||||||
Query Playwright selector, using the actual Playwright query engine, for example:
|
|
||||||
|
|
||||||
```js
|
|
||||||
> playwright.$('.auth-form >> text=Log in');
|
|
||||||
|
|
||||||
<button>Log in</button>
|
|
||||||
```
|
|
||||||
|
|
||||||
#### playwright.$$(selector)
|
|
||||||
|
|
||||||
Same as `playwright.$`, but returns all matching elements.
|
|
||||||
|
|
||||||
```js
|
|
||||||
> playwright.$$('li >> text=John')
|
|
||||||
|
|
||||||
> [<li>, <li>, <li>, <li>]
|
|
||||||
```
|
|
||||||
|
|
||||||
#### playwright.inspect(selector)
|
|
||||||
|
|
||||||
Reveal element in the Elements panel (if DevTools of the respective browser supports it).
|
|
||||||
|
|
||||||
```js
|
|
||||||
> playwright.inspect('text=Log in')
|
|
||||||
```
|
|
||||||
|
|
||||||
#### playwright.locator(selector)
|
|
||||||
|
|
||||||
Query Playwright element using the actual Playwright query engine, for example:
|
|
||||||
|
|
||||||
```js
|
|
||||||
> playwright.locator('.auth-form', { hasText: 'Log in' });
|
|
||||||
|
|
||||||
> Locator ()
|
|
||||||
> - element: button
|
|
||||||
> - elements: [button]
|
|
||||||
```
|
|
||||||
|
|
||||||
#### playwright.selector(element)
|
|
||||||
|
|
||||||
Generates selector for the given element.
|
|
||||||
|
|
||||||
```js
|
|
||||||
> playwright.selector($0)
|
|
||||||
|
|
||||||
"div[id="glow-ingress-block"] >> text=/.*Hello.*/"
|
|
||||||
```
|
|
||||||
|
|
||||||
## Recording scripts
|
|
||||||
|
|
||||||
At any moment, clicking Record action enables [codegen mode](./codegen.md).
|
|
||||||
Every action on the target page is turned into the generated script:
|
|
||||||
|
|
||||||
<img width="712" alt="Recorded script" src="https://user-images.githubusercontent.com/883973/108614897-85704600-73b3-11eb-8bcd-f2e129786c49.png"></img>
|
|
||||||
|
|
||||||
You can copy entire generated script or clear it using toolbar actions.
|
|
||||||
|
|
@ -1,82 +1,94 @@
|
||||||
---
|
---
|
||||||
id: intro
|
id: intro
|
||||||
title: "Getting started"
|
title: "Installation"
|
||||||
---
|
---
|
||||||
|
|
||||||
<!-- TOC -->
|
Playwright was created specifically to accommodate the needs of end-to-end testing. Playwright supports all modern rendering engines including Chromium, WebKit, and Firefox. Test on Windows, Linux, and macOS, locally or on CI, headless or headed with native mobile emulation.
|
||||||
- [Release notes](./release-notes.md)
|
|
||||||
|
|
||||||
## First project
|
You can choose to use [NUnit base classes](./test-runners.md#nunit) or [MSTest base classes](./test-runners.md#nunit) that Playwright provides to write end-to-end tests. These classes support running tests on multiple browser engines, parallelizing tests, adjusting launch/context options and getting a [Page]/[BrowserContext] instance per test out of the box. Alternatively you can use the [library](./library.md) to manually write the testing infrastructure.
|
||||||
|
|
||||||
Create a console project and add the Playwright dependency.
|
1. Start by creating a new project with `dotnet new`. This will create the `PlaywrightTests` directory which includes a `UnitTest1.cs` file:
|
||||||
|
|
||||||
|
<Tabs
|
||||||
|
groupId="test-runners"
|
||||||
|
defaultValue="nunit"
|
||||||
|
values={[
|
||||||
|
{label: 'NUnit', value: 'nunit'},
|
||||||
|
{label: 'MSTest', value: 'mstest'}
|
||||||
|
]
|
||||||
|
}>
|
||||||
|
<TabItem value="nunit">
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
# Create project
|
|
||||||
dotnet new console -n PlaywrightDemo
|
|
||||||
cd PlaywrightDemo
|
|
||||||
|
|
||||||
# Add project dependency
|
|
||||||
dotnet add package Microsoft.Playwright
|
|
||||||
# Build the project
|
|
||||||
dotnet build
|
|
||||||
# Install required browsers - replace netX with actual output folder name, f.ex. net6.0.
|
|
||||||
pwsh bin\Debug\netX\playwright.ps1 install
|
|
||||||
|
|
||||||
# If the pwsh command does not work (throws TypeNotFound), make sure to use an up-to-date version of PowerShell.
|
|
||||||
dotnet tool update --global PowerShell
|
|
||||||
```
|
|
||||||
|
|
||||||
Create a `Program.cs` that will navigate to `https://playwright.dev/dotnet` and take a screenshot in Chromium.
|
|
||||||
|
|
||||||
```csharp
|
|
||||||
using Microsoft.Playwright;
|
|
||||||
|
|
||||||
using var playwright = await Playwright.CreateAsync();
|
|
||||||
await using var browser = await playwright.Chromium.LaunchAsync();
|
|
||||||
var page = await browser.NewPageAsync();
|
|
||||||
await page.GotoAsync("https://playwright.dev/dotnet");
|
|
||||||
await page.ScreenshotAsync(new PageScreenshotOptions { Path = "screenshot.png" });
|
|
||||||
```
|
|
||||||
|
|
||||||
Now run it.
|
|
||||||
|
|
||||||
```bash
|
|
||||||
dotnet run
|
|
||||||
```
|
|
||||||
|
|
||||||
By default, Playwright runs the browsers in headless mode. To see the browser UI, pass the `Headless = false` flag while launching the browser. You can also use [`option: slowMo`] to slow down execution. Learn more in the debugging tools [section](./debug.md).
|
|
||||||
|
|
||||||
```csharp
|
|
||||||
await playwright.Firefox.LaunchAsync(new()
|
|
||||||
{
|
|
||||||
Headless = false,
|
|
||||||
SlowMo = 50,
|
|
||||||
});
|
|
||||||
```
|
|
||||||
|
|
||||||
## First test
|
|
||||||
|
|
||||||
You can choose to use NUnit test fixtures that come bundled with Playwright. These fixtures support running tests on multiple browser engines in parallel, out of the box. Learn more about [Playwright with NUnit](./test-runners.md).
|
|
||||||
|
|
||||||
```bash
|
|
||||||
# Create new project.
|
|
||||||
dotnet new nunit -n PlaywrightTests
|
dotnet new nunit -n PlaywrightTests
|
||||||
cd PlaywrightTests
|
cd PlaywrightTests
|
||||||
```
|
```
|
||||||
|
|
||||||
Install dependencies, build project and download necessary browsers. This is only done once per project.
|
</TabItem>
|
||||||
|
<TabItem value="mstest">
|
||||||
|
|
||||||
|
```bash
|
||||||
|
dotnet new mstest -n PlaywrightTests
|
||||||
|
cd PlaywrightTests
|
||||||
|
```
|
||||||
|
|
||||||
|
</TabItem>
|
||||||
|
</Tabs>
|
||||||
|
|
||||||
|
2. Install the necessary Playwright dependencies:
|
||||||
|
|
||||||
|
<Tabs
|
||||||
|
groupId="test-runners"
|
||||||
|
defaultValue="nunit"
|
||||||
|
values={[
|
||||||
|
{label: 'NUnit', value: 'nunit'},
|
||||||
|
{label: 'MSTest', value: 'mstest'}
|
||||||
|
]
|
||||||
|
}>
|
||||||
|
<TabItem value="nunit">
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
# Add project dependency
|
|
||||||
dotnet add package Microsoft.Playwright.NUnit
|
dotnet add package Microsoft.Playwright.NUnit
|
||||||
# Build the project
|
```
|
||||||
|
|
||||||
|
</TabItem>
|
||||||
|
<TabItem value="mstest">
|
||||||
|
|
||||||
|
```bash
|
||||||
|
dotnet add package Microsoft.Playwright.MSTest
|
||||||
|
```
|
||||||
|
|
||||||
|
</TabItem>
|
||||||
|
</Tabs>
|
||||||
|
|
||||||
|
3. Build the project so the `playwright.ps1` is available inside the `bin` directory:
|
||||||
|
|
||||||
|
```bash
|
||||||
dotnet build
|
dotnet build
|
||||||
# Install required browsers - replace netX with actual output folder name, f.ex. net6.0.
|
```
|
||||||
|
|
||||||
|
4. Install required browsers by replacing `netX` with the actual output folder name, e.g. `net6.0`:
|
||||||
|
|
||||||
|
```bash
|
||||||
pwsh bin\Debug\netX\playwright.ps1 install
|
pwsh bin\Debug\netX\playwright.ps1 install
|
||||||
```
|
```
|
||||||
|
|
||||||
Edit UnitTest1.cs file.
|
## Add Example Tests
|
||||||
|
|
||||||
|
Edit the `UnitTest1.cs` file with the code below to create an example end-to-end test:
|
||||||
|
|
||||||
|
<Tabs
|
||||||
|
groupId="test-runners"
|
||||||
|
defaultValue="nunit"
|
||||||
|
values={[
|
||||||
|
{label: 'NUnit', value: 'nunit'},
|
||||||
|
{label: 'MSTest', value: 'mstest'}
|
||||||
|
]
|
||||||
|
}>
|
||||||
|
<TabItem value="nunit">
|
||||||
|
|
||||||
```csharp
|
```csharp
|
||||||
|
using System.Text.RegularExpressions;
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
using Microsoft.Playwright.NUnit;
|
using Microsoft.Playwright.NUnit;
|
||||||
using NUnit.Framework;
|
using NUnit.Framework;
|
||||||
|
|
@ -87,93 +99,100 @@ namespace PlaywrightTests;
|
||||||
public class Tests : PageTest
|
public class Tests : PageTest
|
||||||
{
|
{
|
||||||
[Test]
|
[Test]
|
||||||
async public Task ShouldHaveTheCorrectSlogan()
|
async public Task HomepageHasPlaywrightInTitleAndGetStartedLinkLinkingtoTheIntroPage()
|
||||||
{
|
{
|
||||||
await Page.GotoAsync("https://playwright.dev");
|
await Page.GotoAsync("https://playwright.dev");
|
||||||
await Expect(Page.Locator("text=enables reliable end-to-end testing for modern web apps")).ToBeVisibleAsync();
|
|
||||||
}
|
|
||||||
|
|
||||||
[Test]
|
// Expect a title "to contain" a substring.
|
||||||
public async Task ShouldHaveTheCorrectTitle()
|
await Expect(Page).ToHaveTitleAsync(new Regex("Playwright"));
|
||||||
{
|
|
||||||
await Page.GotoAsync("https://playwright.dev");
|
|
||||||
var title = Page.Locator(".navbar__inner .navbar__title");
|
|
||||||
await Expect(title).ToHaveTextAsync("Playwright");
|
|
||||||
}
|
|
||||||
|
|
||||||
[Test]
|
// create a locator
|
||||||
public async Task ShouldAdd()
|
var getStarted = Page.Locator("text=Get Started");
|
||||||
{
|
|
||||||
var result = await Page.EvaluateAsync<int>("() => 7 + 3");
|
// Expect an attribute "to be strictly equal" to the value.
|
||||||
Assert.AreEqual(10, result);
|
await Expect(getStarted).ToHaveAttributeAsync("href", "/docs/intro");
|
||||||
|
|
||||||
|
// Click the get started link.
|
||||||
|
await getStarted.ClickAsync();
|
||||||
|
|
||||||
|
// Expects the URL to contain intro.
|
||||||
|
await Expect(Page).ToHaveURLAsync(new Regex(".*intro"));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
|
</TabItem>
|
||||||
|
<TabItem value="mstest">
|
||||||
|
|
||||||
|
```csharp
|
||||||
|
using System.Text.RegularExpressions;
|
||||||
|
using Microsoft.Playwright.MSTest;
|
||||||
|
|
||||||
|
namespace PlaywrightTests;
|
||||||
|
|
||||||
|
public class UnitTest1 : PageTest
|
||||||
|
{
|
||||||
|
[TestMethod]
|
||||||
|
async public Task HomepageHasPlaywrightInTitleAndGetStartedLinkLinkingtoTheIntroPage()
|
||||||
|
{
|
||||||
|
await Page.GotoAsync("https://playwright.dev");
|
||||||
|
|
||||||
|
// Expect a title "to contain" a substring.
|
||||||
|
await Expect(Page).ToHaveTitleAsync(new Regex("Playwright"));
|
||||||
|
|
||||||
|
// create a locator
|
||||||
|
var getStarted = Page.Locator("text=Get Started");
|
||||||
|
|
||||||
|
// Expect an attribute "to be strictly equal" to the value.
|
||||||
|
await Expect(getStarted).ToHaveAttributeAsync("href", "/docs/intro");
|
||||||
|
|
||||||
|
// Click the get started link.
|
||||||
|
await getStarted.ClickAsync();
|
||||||
|
|
||||||
|
// Expects the URL to contain intro.
|
||||||
|
await Expect(Page).ToHaveURLAsync(new Regex(".*intro"));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
</TabItem>
|
||||||
|
</Tabs>
|
||||||
|
|
||||||
|
## Running the Example Tests
|
||||||
|
|
||||||
|
By default tests will be run on Chromium. This can be configured via the `BROWSER` environment variable, or by adjusting the [launch configuration options](./test-runners.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
|
||||||
|
groupId="test-runners"
|
||||||
|
defaultValue="nunit"
|
||||||
|
values={[
|
||||||
|
{label: 'NUnit', value: 'nunit'},
|
||||||
|
{label: 'MSTest', value: 'mstest'}
|
||||||
|
]
|
||||||
|
}>
|
||||||
|
<TabItem value="nunit">
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
dotnet test -- NUnit.NumberOfTestWorkers=5
|
dotnet test -- NUnit.NumberOfTestWorkers=5
|
||||||
```
|
```
|
||||||
|
|
||||||
## Record scripts
|
</TabItem>
|
||||||
|
<TabItem value="mstest">
|
||||||
[Command line tools](./cli.md) can be used to record user interactions and generate C# code.
|
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
pwsh bin\Debug\netX\playwright.ps1 codegen
|
dotnet test -- MSTest.Parallelize.Workers=5
|
||||||
```
|
```
|
||||||
|
|
||||||
## Install browsers via API
|
</TabItem>
|
||||||
|
</Tabs>
|
||||||
|
|
||||||
It's possible to run [Command line tools](./cli.md) commands via the .NET API:
|
See our doc on [Test Runners](./test-runners.md) to learn more about running tests in headed mode, running multiple tests, running specific configurations etc.
|
||||||
|
|
||||||
```csharp
|
## What's next
|
||||||
var exitCode = Microsoft.Playwright.Program.Main(new[] {"install"});
|
|
||||||
if (exitCode != 0)
|
|
||||||
{
|
|
||||||
throw new Exception($"Playwright exited with code {exitCode}");
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
## Bundle drivers for different platforms
|
- [Write tests using web first assertions, page fixtures and locators](./writing-tests.md)
|
||||||
|
- [Run single tests, multiple tests, headed mode](./running-tests.md)
|
||||||
Playwright by default does bundle only the driver for the .NET publish target runtime. If you want to bundle for additional platforms, you can
|
- [Learn more about the NUnit and MSTest base classes](./test-runners.md)
|
||||||
override this behavior by using either `all`, `none` or `linux`, `win`, `osx` in your project file.
|
- [Generate tests with Codegen](./codegen.md)
|
||||||
|
- [See a trace of your tests](./trace-viewer.md)
|
||||||
```xml
|
- [Using Playwright as library](./library.md)
|
||||||
<PropertyGroup>
|
|
||||||
<PlaywrightPlatform>all</PlaywrightPlatform>
|
|
||||||
</PropertyGroup>
|
|
||||||
```
|
|
||||||
|
|
||||||
or:
|
|
||||||
|
|
||||||
```xml
|
|
||||||
<PropertyGroup>
|
|
||||||
<PlaywrightPlatform>osx;linux</PlaywrightPlatform>
|
|
||||||
</PropertyGroup>
|
|
||||||
```
|
|
||||||
|
|
||||||
## System requirements
|
|
||||||
|
|
||||||
The browser binaries for Chromium, Firefox and WebKit work across the 3 platforms (Windows, macOS, Linux):
|
|
||||||
|
|
||||||
### Windows
|
|
||||||
|
|
||||||
Works with Windows and Windows Subsystem for Linux (WSL).
|
|
||||||
|
|
||||||
### macOS
|
|
||||||
|
|
||||||
Requires 11 (Big Sur) or above.
|
|
||||||
|
|
||||||
### Linux
|
|
||||||
|
|
||||||
Depending on your Linux distribution, you might need to install additional
|
|
||||||
dependencies to run the browsers.
|
|
||||||
|
|
||||||
:::note
|
|
||||||
Only Ubuntu 18.04, 20.04, and 22.04 are officially supported.
|
|
||||||
:::
|
|
||||||
|
|
||||||
See also in the [Command line tools](./cli.md#install-system-dependencies)
|
|
||||||
which has a command to install all necessary dependencies automatically for Ubuntu
|
|
||||||
LTS releases.
|
|
||||||
|
|
|
||||||
|
|
@ -5,7 +5,7 @@ title: "Installation"
|
||||||
|
|
||||||
Playwright Test was created specifically to accommodate the needs of end-to-end testing. Playwright supports all modern rendering engines including Chromium, WebKit, and Firefox. Test on Windows, Linux, and macOS, locally or on CI, headless or headed with native mobile emulation of Google Chrome for Android and Mobile Safari.
|
Playwright Test was created specifically to accommodate the needs of end-to-end testing. Playwright supports all modern rendering engines including Chromium, WebKit, and Firefox. Test on Windows, Linux, and macOS, locally or on CI, headless or headed with native mobile emulation of Google Chrome for Android and Mobile Safari.
|
||||||
|
|
||||||
Get started by installing Playwright and running the example test to see it in action.
|
Get started by installing Playwright and running the example test to see it in action. Alternatively you can also get started and run your tests using the [VS Code Extension](./getting-started-vscode.md).
|
||||||
|
|
||||||
<Tabs
|
<Tabs
|
||||||
defaultValue="npm"
|
defaultValue="npm"
|
||||||
|
|
@ -76,13 +76,12 @@ Once your test has finished running a [HTML Reporter](./html-reporter.md) will h
|
||||||
npx playwright show-report
|
npx playwright show-report
|
||||||
```
|
```
|
||||||
|
|
||||||
<img width="739" alt="image" src="https://user-images.githubusercontent.com/13063165/178003817-3bd2f088-4173-406c-a9e9-74c89181f381.png" />
|
<img width="739" alt="image" src="https://user-images.githubusercontent.com/13063165/181803518-1f554349-f72a-4ad3-a7aa-4d3d1b4cad13.png" />
|
||||||
|
|
||||||
|
|
||||||
## What's next
|
## What's next
|
||||||
|
|
||||||
- [Write tests using web first assertions, page fixtures and locators](./writing-tests.md)
|
- [Write tests using web first assertions, page fixtures and locators](./writing-tests.md)
|
||||||
- [Run single tests, multiple tests, headed mode](./running-tests.md)
|
- [Run single tests, multiple tests, headed mode](./running-tests.md)
|
||||||
- [Debug tests with the Playwright Debugger](./debug.md)
|
|
||||||
- [Generate tests with Codegen](./codegen.md)
|
- [Generate tests with Codegen](./codegen.md)
|
||||||
- [See a trace of your tests](./trace-viewer.md)
|
- [See a trace of your tests](./trace-viewer.md)
|
||||||
|
|
@ -1,222 +1,67 @@
|
||||||
---
|
---
|
||||||
id: intro
|
id: intro
|
||||||
title: "Getting started"
|
title: "Installation"
|
||||||
---
|
---
|
||||||
|
|
||||||
<!-- TOC -->
|
Playwright was created specifically to accommodate the needs of end-to-end testing. Playwright supports all modern rendering engines including Chromium, WebKit, and Firefox. Test on Windows, Linux, and macOS, locally or on CI, headless or headed with native mobile emulation.
|
||||||
|
|
||||||
- [Release notes](./release-notes.md)
|
Playwright recommends using the official [Playwright Pytest plugin](./test-runners.md) to write end-to-end tests. It provides context isolation, running it on multiple browser configurations out of the box. Alternatively you can use the [library](./library.md) to manually write the testing infrastructure with your preferred test-runner. The Pytest plugin utilizes the sync version of Playwright, there is also an async version accessible via the library.
|
||||||
|
|
||||||
## Installation
|
Get started by installing Playwright and running the example test to see it in action.
|
||||||
|
|
||||||
See [system requirements](#system-requirements).
|
Install the [Pytest plugin](https://pypi.org/project/pytest-playwright/):
|
||||||
|
|
||||||
### Pip
|
```bash
|
||||||
|
pip install pytest-playwright
|
||||||
[](https://pypi.python.org/pypi/playwright/)
|
```
|
||||||
|
|
||||||
|
Install the required browsers:
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
pip install --upgrade pip
|
|
||||||
pip install playwright
|
|
||||||
playwright install
|
playwright install
|
||||||
```
|
```
|
||||||
|
|
||||||
### Conda
|
## Add Example Test
|
||||||
|
|
||||||
[](https://anaconda.org/Microsoft/playwright)
|
Create a `test_my_application.py` file inside the current working directory or in a sub-directory with the code below:
|
||||||
|
|
||||||
|
```py
|
||||||
|
import re
|
||||||
|
from playwright.sync_api import Page, expect
|
||||||
|
|
||||||
|
|
||||||
|
def test_homepage_has_Playwright_in_title_and_get_started_link_linking_to_the_intro_page(page: Page):
|
||||||
|
page.goto("https://playwright.dev/")
|
||||||
|
|
||||||
|
# Expect a title "to contain" a substring.
|
||||||
|
expect(page).to_have_title(re.compile("Playwright"))
|
||||||
|
|
||||||
|
# create a locator
|
||||||
|
get_started = page.locator("text=Get Started")
|
||||||
|
|
||||||
|
# Expect an attribute "to be strictly equal" to the value.
|
||||||
|
expect(get_started).to_have_attribute("href", "/docs/intro")
|
||||||
|
|
||||||
|
# Click the get started link.
|
||||||
|
get_started.click()
|
||||||
|
|
||||||
|
# Expects the URL to contain intro.
|
||||||
|
expect(page).to_have_url(re.compile(".*intro"))
|
||||||
|
```
|
||||||
|
|
||||||
|
## Running the Example Test
|
||||||
|
|
||||||
|
By default tests will be run on chromium. This can be configured via the CLI options. Tests are run in headless mode meaning no browser UI will open up when running the tests. Results of the tests and test logs will be shown in the terminal.
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
conda config --add channels conda-forge
|
pytest
|
||||||
conda config --add channels microsoft
|
|
||||||
conda install playwright
|
|
||||||
playwright install
|
|
||||||
```
|
```
|
||||||
|
|
||||||
These commands download the Playwright package and install browser binaries for Chromium, Firefox and WebKit. To modify this behavior see [installation parameters](./browsers.md#installing-browsers).
|
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.
|
||||||
|
|
||||||
## Usage
|
## What's next
|
||||||
|
|
||||||
Once installed, you can `import` Playwright in a Python script, and launch any of the 3 browsers (`chromium`, `firefox` and `webkit`).
|
- [Write tests using web first assertions, page fixtures and locators](./writing-tests.md)
|
||||||
|
- [Run single tests, multiple tests, headed mode](./running-tests.md)
|
||||||
```py
|
- [Generate tests with Codegen](./codegen.md)
|
||||||
from playwright.sync_api import sync_playwright
|
- [See a trace of your tests](./trace-viewer.md)
|
||||||
|
|
||||||
with sync_playwright() as p:
|
|
||||||
browser = p.chromium.launch()
|
|
||||||
page = browser.new_page()
|
|
||||||
page.goto("http://playwright.dev")
|
|
||||||
print(page.title())
|
|
||||||
browser.close()
|
|
||||||
```
|
|
||||||
|
|
||||||
Playwright supports two variations of the API: synchronous and asynchronous. If your modern project uses [asyncio](https://docs.python.org/3/library/asyncio.html), you should use async API:
|
|
||||||
|
|
||||||
```py
|
|
||||||
import asyncio
|
|
||||||
from playwright.async_api import async_playwright
|
|
||||||
|
|
||||||
async def main():
|
|
||||||
async with async_playwright() as p:
|
|
||||||
browser = await p.chromium.launch()
|
|
||||||
page = await browser.new_page()
|
|
||||||
await page.goto("http://playwright.dev")
|
|
||||||
print(await page.title())
|
|
||||||
await browser.close()
|
|
||||||
|
|
||||||
asyncio.run(main())
|
|
||||||
```
|
|
||||||
|
|
||||||
## First script
|
|
||||||
|
|
||||||
In our first script, we will navigate to `whatsmyuseragent.org` and take a screenshot in WebKit.
|
|
||||||
|
|
||||||
```py
|
|
||||||
from playwright.sync_api import sync_playwright
|
|
||||||
|
|
||||||
with sync_playwright() as p:
|
|
||||||
browser = p.webkit.launch()
|
|
||||||
page = browser.new_page()
|
|
||||||
page.goto("http://whatsmyuseragent.org/")
|
|
||||||
page.screenshot(path="example.png")
|
|
||||||
browser.close()
|
|
||||||
```
|
|
||||||
|
|
||||||
By default, Playwright runs the browsers in headless mode. To see the browser UI, pass the `headless=False` flag while launching the browser. You can also use [`option: slowMo`] to slow down execution. Learn more in the debugging tools [section](./debug.md).
|
|
||||||
|
|
||||||
```py
|
|
||||||
firefox.launch(headless=False, slow_mo=50)
|
|
||||||
```
|
|
||||||
|
|
||||||
## Record scripts
|
|
||||||
|
|
||||||
[Command line tools](./cli.md) can be used to record user interactions and generate Python code.
|
|
||||||
|
|
||||||
```bash
|
|
||||||
playwright codegen wikipedia.org
|
|
||||||
```
|
|
||||||
|
|
||||||
## With Pytest
|
|
||||||
|
|
||||||
See [here](./test-runners.md) for Pytest instructions and examples.
|
|
||||||
|
|
||||||
## Interactive mode (REPL)
|
|
||||||
|
|
||||||
Blocking REPL, as in CLI via Python directly:
|
|
||||||
|
|
||||||
```bash
|
|
||||||
python
|
|
||||||
```
|
|
||||||
|
|
||||||
```py
|
|
||||||
>>> from playwright.sync_api import sync_playwright
|
|
||||||
>>> playwright = sync_playwright().start()
|
|
||||||
# Use playwright.chromium, playwright.firefox or playwright.webkit
|
|
||||||
# Pass headless=False to launch() to see the browser UI
|
|
||||||
>>> browser = playwright.chromium.launch()
|
|
||||||
>>> page = browser.new_page()
|
|
||||||
>>> page.goto("http://whatsmyuseragent.org/")
|
|
||||||
>>> page.screenshot(path="example.png")
|
|
||||||
>>> browser.close()
|
|
||||||
>>> playwright.stop()
|
|
||||||
```
|
|
||||||
|
|
||||||
Async REPL such as `asyncio` REPL:
|
|
||||||
|
|
||||||
```bash
|
|
||||||
python -m asyncio
|
|
||||||
```
|
|
||||||
|
|
||||||
```py
|
|
||||||
>>> from playwright.async_api import async_playwright
|
|
||||||
>>> playwright = await async_playwright().start()
|
|
||||||
>>> browser = await playwright.chromium.launch()
|
|
||||||
>>> page = await browser.new_page()
|
|
||||||
>>> await page.goto("http://whatsmyuseragent.org/")
|
|
||||||
>>> await page.screenshot(path="example.png")
|
|
||||||
>>> await browser.close()
|
|
||||||
>>> await playwright.stop()
|
|
||||||
```
|
|
||||||
|
|
||||||
## Pyinstaller
|
|
||||||
|
|
||||||
You can use Playwright with [Pyinstaller](https://www.pyinstaller.org/) to create standalone executables.
|
|
||||||
|
|
||||||
```py
|
|
||||||
# main.py
|
|
||||||
from playwright.sync_api import sync_playwright
|
|
||||||
|
|
||||||
with sync_playwright() as p:
|
|
||||||
browser = p.chromium.launch()
|
|
||||||
page = browser.new_page()
|
|
||||||
page.goto("http://whatsmyuseragent.org/")
|
|
||||||
page.screenshot(path="example.png")
|
|
||||||
browser.close()
|
|
||||||
```
|
|
||||||
|
|
||||||
If you want to bundle browsers with the executables:
|
|
||||||
|
|
||||||
```bash tab=bash-bash
|
|
||||||
PLAYWRIGHT_BROWSERS_PATH=0 playwright install chromium
|
|
||||||
pyinstaller -F main.py
|
|
||||||
```
|
|
||||||
|
|
||||||
```batch tab=bash-batch
|
|
||||||
set PLAYWRIGHT_BROWSERS_PATH=0
|
|
||||||
playwright install chromium
|
|
||||||
pyinstaller -F main.py
|
|
||||||
```
|
|
||||||
|
|
||||||
```powershell tab=bash-powershell
|
|
||||||
$env:PLAYWRIGHT_BROWSERS_PATH="0"
|
|
||||||
playwright install chromium
|
|
||||||
pyinstaller -F main.py
|
|
||||||
```
|
|
||||||
|
|
||||||
:::note
|
|
||||||
Bundling the browsers with the executables will generate bigger binaries.
|
|
||||||
It is recommended to only bundle the browsers you use.
|
|
||||||
:::
|
|
||||||
|
|
||||||
## Known issues
|
|
||||||
|
|
||||||
### `time.sleep()` leads to outdated state
|
|
||||||
|
|
||||||
You should use `page.wait_for_timeout(5000)` instead of `time.sleep(5)` and it is better to not wait for a timeout at all, but sometimes it is useful for debugging. In these cases, use our wait method instead of the `time` module. This is because we internally rely on asynchronous operations and when using `time.sleep(5)` they can't get processed correctly.
|
|
||||||
|
|
||||||
|
|
||||||
### incompatible with `SelectorEventLoop` of `asyncio` on Windows
|
|
||||||
|
|
||||||
Playwright runs the driver in a subprocess, so it requires `ProactorEventLoop` of `asyncio` on Windows because `SelectorEventLoop` does not supports async subprocesses.
|
|
||||||
|
|
||||||
On Windows Python 3.7, Playwright sets the default event loop to `ProactorEventLoop` as it is default on Python 3.8+.
|
|
||||||
|
|
||||||
### Threading
|
|
||||||
|
|
||||||
Playwright's API is not thread-safe. If you are using Playwright in a multi-threaded environment, you should create a playwright instance per thread. See [threading issue](https://github.com/microsoft/playwright-python/issues/623) for more details.
|
|
||||||
|
|
||||||
|
|
||||||
## System requirements
|
|
||||||
|
|
||||||
Playwright requires Python 3.7 or above. The browser binaries for Chromium,
|
|
||||||
Firefox and WebKit work across the 3 platforms (Windows, macOS, Linux):
|
|
||||||
|
|
||||||
### Windows
|
|
||||||
|
|
||||||
Works with Windows and Windows Subsystem for Linux (WSL).
|
|
||||||
|
|
||||||
### macOS
|
|
||||||
|
|
||||||
Requires 11 (Big Sur) or above.
|
|
||||||
|
|
||||||
### Linux
|
|
||||||
|
|
||||||
Depending on your Linux distribution, you might need to install additional
|
|
||||||
dependencies to run the browsers.
|
|
||||||
|
|
||||||
:::note
|
|
||||||
Only Ubuntu 18.04, 20.04, and 22.04 are officially supported.
|
|
||||||
:::
|
|
||||||
|
|
||||||
See also in the [Command line tools](./cli.md#install-system-dependencies)
|
|
||||||
which has a command to install all necessary dependencies automatically for Ubuntu
|
|
||||||
LTS releases.
|
|
||||||
|
|
|
||||||
73
docs/src/library-csharp.md
Normal file
73
docs/src/library-csharp.md
Normal file
|
|
@ -0,0 +1,73 @@
|
||||||
|
---
|
||||||
|
id: library
|
||||||
|
title: "Getting started - Library"
|
||||||
|
---
|
||||||
|
|
||||||
|
Playwright can either be used with the [NUnit](./test-runners.md#nunit) or [MSTest](./test-runners.md#mstest), or as a Playwright Library (this guide). If you are working on an application that utilizes Playwright capabilities or you are using Playwright with another test runner, read on.
|
||||||
|
|
||||||
|
## Usage
|
||||||
|
|
||||||
|
Create a console project and add the Playwright dependency.
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# Create project
|
||||||
|
dotnet new console -n PlaywrightDemo
|
||||||
|
cd PlaywrightDemo
|
||||||
|
|
||||||
|
# Add project dependency
|
||||||
|
dotnet add package Microsoft.Playwright
|
||||||
|
# Build the project
|
||||||
|
dotnet build
|
||||||
|
# Install required browsers - replace netX with actual output folder name, e.g. net6.0.
|
||||||
|
pwsh bin\Debug\netX\playwright.ps1 install
|
||||||
|
|
||||||
|
# If the pwsh command does not work (throws TypeNotFound), make sure to use an up-to-date version of PowerShell.
|
||||||
|
dotnet tool update --global PowerShell
|
||||||
|
```
|
||||||
|
|
||||||
|
Create a `Program.cs` that will navigate to `https://playwright.dev/dotnet` and take a screenshot in Chromium.
|
||||||
|
|
||||||
|
```csharp
|
||||||
|
using Microsoft.Playwright;
|
||||||
|
|
||||||
|
using var playwright = await Playwright.CreateAsync();
|
||||||
|
await using var browser = await playwright.Chromium.LaunchAsync();
|
||||||
|
var page = await browser.NewPageAsync();
|
||||||
|
await page.GotoAsync("https://playwright.dev/dotnet");
|
||||||
|
await page.ScreenshotAsync(new PageScreenshotOptions { Path = "screenshot.png" });
|
||||||
|
```
|
||||||
|
|
||||||
|
Now run it.
|
||||||
|
|
||||||
|
```bash
|
||||||
|
dotnet run
|
||||||
|
```
|
||||||
|
|
||||||
|
By default, Playwright runs the browsers in headless mode. To see the browser UI, pass the `Headless = false` flag while launching the browser. You can also use [`option: slowMo`] to slow down execution. Learn more in the debugging tools [section](./debug.md).
|
||||||
|
|
||||||
|
```csharp
|
||||||
|
await using var browser = await playwright.Firefox.LaunchAsync(new()
|
||||||
|
{
|
||||||
|
Headless = false,
|
||||||
|
SlowMo = 50,
|
||||||
|
});
|
||||||
|
```
|
||||||
|
|
||||||
|
## Bundle drivers for different platforms
|
||||||
|
|
||||||
|
Playwright by default does bundle only the driver for the .NET publish target runtime. If you want to bundle for additional platforms, you can
|
||||||
|
override this behavior by using either `all`, `none` or `linux`, `win`, `osx` in your project file.
|
||||||
|
|
||||||
|
```xml
|
||||||
|
<PropertyGroup>
|
||||||
|
<PlaywrightPlatform>all</PlaywrightPlatform>
|
||||||
|
</PropertyGroup>
|
||||||
|
```
|
||||||
|
|
||||||
|
or:
|
||||||
|
|
||||||
|
```xml
|
||||||
|
<PropertyGroup>
|
||||||
|
<PlaywrightPlatform>osx;linux</PlaywrightPlatform>
|
||||||
|
</PropertyGroup>
|
||||||
|
```
|
||||||
|
|
@ -3,14 +3,139 @@ id: library
|
||||||
title: "Library"
|
title: "Library"
|
||||||
---
|
---
|
||||||
|
|
||||||
Playwright can either be used as a part of the [Playwright Test](./intro.md), or as a Playwright Library (this guide). If you are working on an application that utilizes Playwright capabilities or you are using Playwright with another test runner, read on.
|
Playwright Library provides unified APIs for launching and interacting with browsers, while Playwright Test provides all this plus a fully managed end-to-end Test Runner and experience.
|
||||||
|
|
||||||
|
Under most circumstances, for end-to-end testing, you'll want to use `@playwright/test` (Playwright Test), and not `playwright` (Playwright Library) directly. To get started with Playwright Test, follow its [Getting Started Guide](./intro.md).
|
||||||
|
|
||||||
<!-- TOC -->
|
<!-- TOC -->
|
||||||
- [Release notes](./release-notes.md)
|
- [Release notes](./release-notes.md)
|
||||||
|
|
||||||
|
## When Should Playwright Library Be Used Directly?
|
||||||
|
|
||||||
|
- creating an integration for a third party test runner (e.g. the third-party runner plugins listed [here](./test-runners.md) are built on top of Playwright Library)
|
||||||
|
- automation and scraping
|
||||||
|
|
||||||
|
## Differences
|
||||||
|
|
||||||
|
### Library Example
|
||||||
|
|
||||||
|
The following is an example of using the Playwright Library directly to launch Chromium, go to a page, and check its title:
|
||||||
|
|
||||||
|
|
||||||
|
```js tab=js-ts
|
||||||
|
import playwright, { devices } from 'playwright';
|
||||||
|
|
||||||
|
(async () => {
|
||||||
|
// Setup
|
||||||
|
const browser = await playwright.chromium.launch();
|
||||||
|
const context = await browser.newContext(devices['iPhone 11']);
|
||||||
|
const page = await context.newPage();
|
||||||
|
|
||||||
|
// The actual interesting bit
|
||||||
|
await context.route('**.jpg', route => route.abort());
|
||||||
|
await page.goto('https://example.com/');
|
||||||
|
|
||||||
|
assert(await page.title() === 'Example'); // 👎 not a Web First assertion
|
||||||
|
|
||||||
|
// Teardown
|
||||||
|
await context.close();
|
||||||
|
await browser.close();
|
||||||
|
})()
|
||||||
|
```
|
||||||
|
|
||||||
|
```js tab=js-js
|
||||||
|
const playwright = require('playwright');
|
||||||
|
|
||||||
|
(async () => {
|
||||||
|
// Setup
|
||||||
|
const browser = await playwright.chromium.launch();
|
||||||
|
const context = await browser.newContext(devices['iPhone 11']);
|
||||||
|
const page = await context.newPage();
|
||||||
|
|
||||||
|
// The actual interesting bit
|
||||||
|
await context.route('**.jpg', route => route.abort());
|
||||||
|
await page.goto('https://example.com/');
|
||||||
|
|
||||||
|
assert(await page.title() === 'Example'); // 👎 not a Web First assertion
|
||||||
|
|
||||||
|
// Teardown
|
||||||
|
await context.close();
|
||||||
|
await browser.close();
|
||||||
|
})()
|
||||||
|
```
|
||||||
|
|
||||||
|
Run via:
|
||||||
|
|
||||||
|
```bash tab=js-ts
|
||||||
|
node ./my-script.ts
|
||||||
|
```
|
||||||
|
|
||||||
|
```bash tab=js-js
|
||||||
|
node ./my-script.js
|
||||||
|
```
|
||||||
|
|
||||||
|
### Test Example
|
||||||
|
|
||||||
|
A test to achieve similar behavior, would look like:
|
||||||
|
|
||||||
|
```js tab=js-ts
|
||||||
|
import { expect, test, devices } from '@playwright/test';
|
||||||
|
|
||||||
|
test.use(devices['iPhone 11']);
|
||||||
|
|
||||||
|
test('should be titled', async ({ page, context }) => {
|
||||||
|
await context.route('**.jpg', route => route.abort());
|
||||||
|
await page.goto('https://example.com/');
|
||||||
|
|
||||||
|
await expect(page).toHaveTitle('Example');
|
||||||
|
});
|
||||||
|
```
|
||||||
|
|
||||||
|
```js tab=js-js
|
||||||
|
const { expect, test, devices } = require('@playwright/test');
|
||||||
|
|
||||||
|
test.use(devices['iPhone 11']);
|
||||||
|
|
||||||
|
test('should be titled', async ({ page, context }) => {
|
||||||
|
await context.route('**.jpg', route => route.abort());
|
||||||
|
await page.goto('https://example.com/');
|
||||||
|
|
||||||
|
await expect(page).toHaveTitle('Example');
|
||||||
|
});
|
||||||
|
```
|
||||||
|
|
||||||
|
Run via:
|
||||||
|
|
||||||
|
```
|
||||||
|
npx playwright test
|
||||||
|
```
|
||||||
|
|
||||||
|
### Key Differences
|
||||||
|
|
||||||
|
The key differences to note are as follows:
|
||||||
|
|
||||||
|
| | Library | Test |
|
||||||
|
| - | - | - |
|
||||||
|
| Installation | `npm install playwright` | `npm init playwright@latest` (note `install` vs. `init`) |
|
||||||
|
| `import`/`require` name | `playwright` | `@playwright/test` |
|
||||||
|
| Initialization | Explicitly need to: <ol><li>Pick a browser to use (e.g. `chromium`)</li><li>Create `browser` ([`method: BrowserType.launch`])</li><li>Create a `context` ([`method: Browser.newContext`]), <em>and</em> pass any context options explcitly (e.g. `devices['iPhone 11']`</li><li>Create a `page` ([`method: BrowserContext.newPage`])</li></ol> | An isolated `page` and `context` are provided to each test out-of the box (along with any other [built-in fixtures](./test-fixtures.md#built-in-fixtures)). No explicit creation. If referenced by the test in it's arguments, the Test Runner will create them for the test. (i.e. lazy-initialization) |
|
||||||
|
| Assertions | No built-in Web-First Assertions | [Web-First assertions](./test-assertions.md) like: <ul><li>[`method: PageAssertions.toHaveTitle`]</li><li>[`method: PageAssertions.toHaveScreenshot#1`]</li></ul> which auto-wait and retry for the condition to be met.|
|
||||||
|
| Cleanup | Explicitly need to: <ol><li>Close `context` ([`method: BrowserContext.close`])</li><li>Close `browser` ([`method: Browser.close`])</li></ol> | No explicit close of [built-in fixtures](./test-fixtures.md#built-in-fixtures); the Test Runner will take care of it.
|
||||||
|
| Running | When using the Library, you run the code as a node script (possibly with some compilation first). | When using the Test Runner, you use the `npx playwright test` command. Along with your [config](./test-configuration.md)), the Test Runner handles any compilation and choosing what to run and how to run it. |
|
||||||
|
|
||||||
|
In addition to the above, Playwright Test—as a full-featured Test Runner—includes:
|
||||||
|
|
||||||
|
- [Configuration Matrix and Projects](./test-configuration.md): In the above example, in the Playwright Library version, if we wanted to run with a different device or browser, we'd have to modify the script and plumb the information through. With Playwright Test, we can just specify the [matrix of configurations](./test-configuration.md) in one place, and it will create run the one test under each of these configurations.
|
||||||
|
- [Parallelization](./test-parallel.md)
|
||||||
|
- [Web-First Assertions](./test-assertions.md)
|
||||||
|
- [Reporting](./test-reporters.md)
|
||||||
|
- [Retries](./test-retries.md)
|
||||||
|
- [Easily Enabled Tracing](./test-configuration.md#record-test-trace)
|
||||||
|
- and more…
|
||||||
|
|
||||||
## Usage
|
## Usage
|
||||||
|
|
||||||
Use npm or Yarn to install Playwright library in your Node.js project. See [system requirements](#system-requirements).
|
Use npm or Yarn to install Playwright library in your Node.js project. See [system requirements](./troubleshooting.md#system-requirements).
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
npm i -D playwright
|
npm i -D playwright
|
||||||
|
|
@ -94,29 +219,3 @@ TypeScript support will work out-of-the-box. Types can also be imported explicit
|
||||||
```js
|
```js
|
||||||
let page: import('playwright').Page;
|
let page: import('playwright').Page;
|
||||||
```
|
```
|
||||||
|
|
||||||
## System requirements
|
|
||||||
|
|
||||||
Playwright requires Node.js version 14 or above. The browser binaries for Chromium,
|
|
||||||
Firefox and WebKit work across the 3 platforms (Windows, macOS, Linux):
|
|
||||||
|
|
||||||
### Windows
|
|
||||||
|
|
||||||
Works with Windows and Windows Subsystem for Linux (WSL).
|
|
||||||
|
|
||||||
### macOS
|
|
||||||
|
|
||||||
Requires 11 (Big Sur) or above.
|
|
||||||
|
|
||||||
### Linux
|
|
||||||
|
|
||||||
Depending on your Linux distribution, you might need to install additional
|
|
||||||
dependencies to run the browsers.
|
|
||||||
|
|
||||||
:::note
|
|
||||||
Only Ubuntu 18.04, 20.04, and 22.04 are officially supported.
|
|
||||||
:::
|
|
||||||
|
|
||||||
See also in the [Command line tools](./cli.md#install-system-dependencies)
|
|
||||||
which has a command to install all necessary dependencies automatically for Ubuntu
|
|
||||||
LTS releases.
|
|
||||||
|
|
|
||||||
179
docs/src/library-python.md
Normal file
179
docs/src/library-python.md
Normal file
|
|
@ -0,0 +1,179 @@
|
||||||
|
---
|
||||||
|
id: library
|
||||||
|
title: "Getting started - Library"
|
||||||
|
---
|
||||||
|
|
||||||
|
## Installation
|
||||||
|
|
||||||
|
### Pip
|
||||||
|
|
||||||
|
[](https://pypi.python.org/pypi/playwright/)
|
||||||
|
|
||||||
|
```bash
|
||||||
|
pip install --upgrade pip
|
||||||
|
pip install playwright
|
||||||
|
playwright install
|
||||||
|
```
|
||||||
|
|
||||||
|
### Conda
|
||||||
|
|
||||||
|
[](https://anaconda.org/Microsoft/playwright)
|
||||||
|
|
||||||
|
```bash
|
||||||
|
conda config --add channels conda-forge
|
||||||
|
conda config --add channels microsoft
|
||||||
|
conda install playwright
|
||||||
|
playwright install
|
||||||
|
```
|
||||||
|
|
||||||
|
These commands download the Playwright package and install browser binaries for Chromium, Firefox and WebKit. To modify this behavior see [installation parameters](./browsers.md#installing-browsers).
|
||||||
|
|
||||||
|
## Usage
|
||||||
|
|
||||||
|
Once installed, you can `import` Playwright in a Python script, and launch any of the 3 browsers (`chromium`, `firefox` and `webkit`).
|
||||||
|
|
||||||
|
```py
|
||||||
|
from playwright.sync_api import sync_playwright
|
||||||
|
|
||||||
|
with sync_playwright() as p:
|
||||||
|
browser = p.chromium.launch()
|
||||||
|
page = browser.new_page()
|
||||||
|
page.goto("http://playwright.dev")
|
||||||
|
print(page.title())
|
||||||
|
browser.close()
|
||||||
|
```
|
||||||
|
|
||||||
|
Playwright supports two variations of the API: synchronous and asynchronous. If your modern project uses [asyncio](https://docs.python.org/3/library/asyncio.html), you should use async API:
|
||||||
|
|
||||||
|
```py
|
||||||
|
import asyncio
|
||||||
|
from playwright.async_api import async_playwright
|
||||||
|
|
||||||
|
async def main():
|
||||||
|
async with async_playwright() as p:
|
||||||
|
browser = await p.chromium.launch()
|
||||||
|
page = await browser.new_page()
|
||||||
|
await page.goto("http://playwright.dev")
|
||||||
|
print(await page.title())
|
||||||
|
await browser.close()
|
||||||
|
|
||||||
|
asyncio.run(main())
|
||||||
|
```
|
||||||
|
|
||||||
|
## First script
|
||||||
|
|
||||||
|
In our first script, we will navigate to `whatsmyuseragent.org` and take a screenshot in WebKit.
|
||||||
|
|
||||||
|
```py
|
||||||
|
from playwright.sync_api import sync_playwright
|
||||||
|
|
||||||
|
with sync_playwright() as p:
|
||||||
|
browser = p.webkit.launch()
|
||||||
|
page = browser.new_page()
|
||||||
|
page.goto("http://whatsmyuseragent.org/")
|
||||||
|
page.screenshot(path="example.png")
|
||||||
|
browser.close()
|
||||||
|
```
|
||||||
|
|
||||||
|
By default, Playwright runs the browsers in headless mode. To see the browser UI, pass the `headless=False` flag while launching the browser. You can also use [`option: slowMo`] to slow down execution. Learn more in the debugging tools [section](./debug.md).
|
||||||
|
|
||||||
|
```py
|
||||||
|
firefox.launch(headless=False, slow_mo=50)
|
||||||
|
```
|
||||||
|
|
||||||
|
## Interactive mode (REPL)
|
||||||
|
|
||||||
|
You can launch the interactive python REPL:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
python
|
||||||
|
```
|
||||||
|
|
||||||
|
and then launch Playwright within it for quick experimentation:
|
||||||
|
|
||||||
|
```py
|
||||||
|
>>> from playwright.sync_api import sync_playwright
|
||||||
|
>>> playwright = sync_playwright().start()
|
||||||
|
# Use playwright.chromium, playwright.firefox or playwright.webkit
|
||||||
|
# Pass headless=False to launch() to see the browser UI
|
||||||
|
>>> browser = playwright.chromium.launch()
|
||||||
|
>>> page = browser.new_page()
|
||||||
|
>>> page.goto("http://whatsmyuseragent.org/")
|
||||||
|
>>> page.screenshot(path="example.png")
|
||||||
|
>>> browser.close()
|
||||||
|
>>> playwright.stop()
|
||||||
|
```
|
||||||
|
|
||||||
|
Async REPL such as `asyncio` REPL:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
python -m asyncio
|
||||||
|
```
|
||||||
|
|
||||||
|
```py
|
||||||
|
>>> from playwright.async_api import async_playwright
|
||||||
|
>>> playwright = await async_playwright().start()
|
||||||
|
>>> browser = await playwright.chromium.launch()
|
||||||
|
>>> page = await browser.new_page()
|
||||||
|
>>> await page.goto("http://whatsmyuseragent.org/")
|
||||||
|
>>> await page.screenshot(path="example.png")
|
||||||
|
>>> await browser.close()
|
||||||
|
>>> await playwright.stop()
|
||||||
|
```
|
||||||
|
|
||||||
|
## Pyinstaller
|
||||||
|
|
||||||
|
You can use Playwright with [Pyinstaller](https://www.pyinstaller.org/) to create standalone executables.
|
||||||
|
|
||||||
|
```py
|
||||||
|
# main.py
|
||||||
|
from playwright.sync_api import sync_playwright
|
||||||
|
|
||||||
|
with sync_playwright() as p:
|
||||||
|
browser = p.chromium.launch()
|
||||||
|
page = browser.new_page()
|
||||||
|
page.goto("http://whatsmyuseragent.org/")
|
||||||
|
page.screenshot(path="example.png")
|
||||||
|
browser.close()
|
||||||
|
```
|
||||||
|
|
||||||
|
If you want to bundle browsers with the executables:
|
||||||
|
|
||||||
|
```bash tab=bash-bash
|
||||||
|
PLAYWRIGHT_BROWSERS_PATH=0 playwright install chromium
|
||||||
|
pyinstaller -F main.py
|
||||||
|
```
|
||||||
|
|
||||||
|
```batch tab=bash-batch
|
||||||
|
set PLAYWRIGHT_BROWSERS_PATH=0
|
||||||
|
playwright install chromium
|
||||||
|
pyinstaller -F main.py
|
||||||
|
```
|
||||||
|
|
||||||
|
```powershell tab=bash-powershell
|
||||||
|
$env:PLAYWRIGHT_BROWSERS_PATH="0"
|
||||||
|
playwright install chromium
|
||||||
|
pyinstaller -F main.py
|
||||||
|
```
|
||||||
|
|
||||||
|
:::note
|
||||||
|
Bundling the browsers with the executables will generate bigger binaries.
|
||||||
|
It is recommended to only bundle the browsers you use.
|
||||||
|
:::
|
||||||
|
|
||||||
|
## Known issues
|
||||||
|
|
||||||
|
### `time.sleep()` leads to outdated state
|
||||||
|
|
||||||
|
Most likely you don't need to wait manually, since Playwright has [auto-waiting](./actionability.md). If you still rely on it, you should use `page.wait_for_timeout(5000)` instead of `time.sleep(5)` and it is better to not wait for a timeout at all, but sometimes it is useful for debugging. In these cases, use our wait (`wait_for_timeout`) method instead of the `time` module. This is because we internally rely on asynchronous operations and when using `time.sleep(5)` they can't get processed correctly.
|
||||||
|
|
||||||
|
|
||||||
|
### incompatible with `SelectorEventLoop` of `asyncio` on Windows
|
||||||
|
|
||||||
|
Playwright runs the driver in a subprocess, so it requires `ProactorEventLoop` of `asyncio` on Windows because `SelectorEventLoop` does not supports async subprocesses.
|
||||||
|
|
||||||
|
On Windows Python 3.7, Playwright sets the default event loop to `ProactorEventLoop` as it is default on Python 3.8+.
|
||||||
|
|
||||||
|
### Threading
|
||||||
|
|
||||||
|
Playwright's API is not thread-safe. If you are using Playwright in a multi-threaded environment, you should create a playwright instance per thread. See [threading issue](https://github.com/microsoft/playwright-python/issues/623) for more details.
|
||||||
|
|
@ -137,7 +137,7 @@ given selector.
|
||||||
await page.locator('button').click();
|
await page.locator('button').click();
|
||||||
|
|
||||||
// Works because we explicitly tell locator to pick the first element:
|
// Works because we explicitly tell locator to pick the first element:
|
||||||
await page.locator('button').first().click();
|
await page.locator('button').first().click(); // ⚠️ using first disables strictness
|
||||||
|
|
||||||
// Works because count knows what to do with multiple matches:
|
// Works because count knows what to do with multiple matches:
|
||||||
await page.locator('button').count();
|
await page.locator('button').count();
|
||||||
|
|
@ -148,7 +148,7 @@ await page.locator('button').count();
|
||||||
await page.locator('button').click()
|
await page.locator('button').click()
|
||||||
|
|
||||||
# Works because we explicitly tell locator to pick the first element:
|
# Works because we explicitly tell locator to pick the first element:
|
||||||
await page.locator('button').first.click()
|
await page.locator('button').first.click() # ⚠️ using first disables strictness
|
||||||
|
|
||||||
# Works because count knows what to do with multiple matches:
|
# Works because count knows what to do with multiple matches:
|
||||||
await page.locator('button').count()
|
await page.locator('button').count()
|
||||||
|
|
@ -159,7 +159,7 @@ await page.locator('button').count()
|
||||||
page.locator('button').click()
|
page.locator('button').click()
|
||||||
|
|
||||||
# Works because we explicitly tell locator to pick the first element:
|
# Works because we explicitly tell locator to pick the first element:
|
||||||
page.locator('button').first.click()
|
page.locator('button').first.click() # ⚠️ using first disables strictness
|
||||||
|
|
||||||
# Works because count knows what to do with multiple matches:
|
# Works because count knows what to do with multiple matches:
|
||||||
page.locator('button').count()
|
page.locator('button').count()
|
||||||
|
|
@ -170,7 +170,7 @@ page.locator('button').count()
|
||||||
page.locator("button").click();
|
page.locator("button").click();
|
||||||
|
|
||||||
// Works because we explicitly tell locator to pick the first element:
|
// Works because we explicitly tell locator to pick the first element:
|
||||||
page.locator("button").first().click();
|
page.locator("button").first().click(); // ⚠️ using first disables strictness
|
||||||
|
|
||||||
// Works because count knows what to do with multiple matches:
|
// Works because count knows what to do with multiple matches:
|
||||||
page.locator("button").count();
|
page.locator("button").count();
|
||||||
|
|
@ -181,12 +181,16 @@ page.locator("button").count();
|
||||||
await page.Locator("button").ClickAsync();
|
await page.Locator("button").ClickAsync();
|
||||||
|
|
||||||
// Works because we explicitly tell locator to pick the first element:
|
// Works because we explicitly tell locator to pick the first element:
|
||||||
await page.Locator("button").First.ClickAsync();
|
await page.Locator("button").First.ClickAsync(); // ⚠️ using First disables strictness
|
||||||
|
|
||||||
// Works because Count knows what to do with multiple matches:
|
// Works because Count knows what to do with multiple matches:
|
||||||
await page.Locator("button").CountAsync();
|
await page.Locator("button").CountAsync();
|
||||||
```
|
```
|
||||||
|
|
||||||
|
:::caution
|
||||||
|
Using [`method: Locator.first`], [`method: Locator.last`], and [`method: Locator.nth`] is discouraged since it disables the concept of strictness, and as your page changes, Playwright may click on an element you did not intend. It's better to make your locator more specific. Learn more below in [Filtering Locators](#filtering-locators) and the [selectors guide](./selectors.md).
|
||||||
|
:::
|
||||||
|
|
||||||
## Lists
|
## Lists
|
||||||
|
|
||||||
You can also use locators to work with the element lists.
|
You can also use locators to work with the element lists.
|
||||||
|
|
|
||||||
|
|
@ -871,3 +871,13 @@ page.WebSocket += (_, ws) =>
|
||||||
- [`event: WebSocket.close`]
|
- [`event: WebSocket.close`]
|
||||||
|
|
||||||
<br/>
|
<br/>
|
||||||
|
|
||||||
|
## Missing Network Events and Service Workers
|
||||||
|
|
||||||
|
Playwright's built-in [`method: BrowserContext.route`] and [`method: Page.route`] allow your tests to natively route requests and perform mocking and interception.
|
||||||
|
|
||||||
|
1. If you're using Playwright's native [`method: BrowserContext.route`] and [`method: Page.route`], and it appears network events are missing, disable Service Workers by setting [`option: Browser.newContext.serviceWorkers`] to `'block'`.
|
||||||
|
1. It might be that you are using a mock tool such as Mock Service Worker (MSW). While this tool works out of the box for mocking responses, it adds its own Service Worker that takes over the network requests, hence making them invisible to [`method: BrowserContext.route`] and [`method: Page.route`]. If you are interested in both network testing and mocking, consider using built-in [`method: BrowserContext.route`] and [`method: Page.route`] for [response mocking](#handle-requests).
|
||||||
|
1. If you're interested in not solely using Service Workers for testing and network mocking, but in routing and listening for requests made by Service Workers themselves, please see [this experimental feature](https://github.com/microsoft/playwright/issues/15684).
|
||||||
|
|
||||||
|
<br/>
|
||||||
|
|
|
||||||
|
|
@ -152,7 +152,7 @@ Once you're on Playwright Test, you get a lot!
|
||||||
- Built-in test artifact collection: [video recording](./test-configuration#record-video), [screenshots](./test-configuration#automatic-screenshots) and [playwright traces](./test-configuration#record-test-trace)
|
- Built-in test artifact collection: [video recording](./test-configuration#record-video), [screenshots](./test-configuration#automatic-screenshots) and [playwright traces](./test-configuration#record-test-trace)
|
||||||
|
|
||||||
Also you get all these ✨ awesome tools ✨ that come bundled with Playwright Test:
|
Also you get all these ✨ awesome tools ✨ that come bundled with Playwright Test:
|
||||||
- [Playwright Inspector](./inspector)
|
- [Playwright Inspector](./debug.md)
|
||||||
- [Playwright Test Code generation](./auth#code-generation)
|
- [Playwright Test Code generation](./auth#code-generation)
|
||||||
- [Playwright Tracing](./trace-viewer) for post-mortem debugging
|
- [Playwright Tracing](./trace-viewer) for post-mortem debugging
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -5,6 +5,25 @@ title: "Release notes"
|
||||||
|
|
||||||
<!-- TOC -->
|
<!-- TOC -->
|
||||||
|
|
||||||
|
## Version 1.24
|
||||||
|
|
||||||
|
### 🐂 Debian 11 Bullseye Support
|
||||||
|
|
||||||
|
Playwright now supports Debian 11 Bullseye on x86_64 for Chromium, Firefox and WebKit. Let us know
|
||||||
|
if you encounter any issues!
|
||||||
|
|
||||||
|
Linux support looks like this:
|
||||||
|
|
||||||
|
| | Ubuntu 18.04 | Ubuntu 20.04 | Ubuntu 22.04 | Debian 11
|
||||||
|
| :--- | :---: | :---: | :---: | :---: |
|
||||||
|
| Chromium | ✅ | ✅ | ✅ | ✅ |
|
||||||
|
| WebKit | ✅ | ✅ | ✅ | ✅ |
|
||||||
|
| Firefox | ✅ | ✅ | ✅ | ✅ |
|
||||||
|
|
||||||
|
### New introduction docs
|
||||||
|
|
||||||
|
We rewrote our Getting Started docs to be more end-to-end testing focused. Check them out on [playwright.dev](https://playwright.dev/dotnet/docs/intro).
|
||||||
|
|
||||||
## Version 1.23
|
## Version 1.23
|
||||||
|
|
||||||
### API Testing
|
### API Testing
|
||||||
|
|
@ -190,7 +209,7 @@ using System.Threading.Tasks;
|
||||||
using Microsoft.Playwright.NUnit;
|
using Microsoft.Playwright.NUnit;
|
||||||
using NUnit.Framework;
|
using NUnit.Framework;
|
||||||
|
|
||||||
namespace Playwright.TestingHarnessTest.NUnit;
|
namespace PlaywrightTests;
|
||||||
|
|
||||||
public class ExampleTests : PageTest
|
public class ExampleTests : PageTest
|
||||||
{
|
{
|
||||||
|
|
|
||||||
|
|
@ -5,6 +5,21 @@ title: "Release notes"
|
||||||
|
|
||||||
<!-- TOC -->
|
<!-- TOC -->
|
||||||
|
|
||||||
|
## Version 1.24
|
||||||
|
|
||||||
|
### 🐂 Debian 11 Bullseye Support
|
||||||
|
|
||||||
|
Playwright now supports Debian 11 Bullseye on x86_64 for Chromium, Firefox and WebKit. Let us know
|
||||||
|
if you encounter any issues!
|
||||||
|
|
||||||
|
Linux support looks like this:
|
||||||
|
|
||||||
|
| | Ubuntu 18.04 | Ubuntu 20.04 | Ubuntu 22.04 | Debian 11
|
||||||
|
| :--- | :---: | :---: | :---: | :---: |
|
||||||
|
| Chromium | ✅ | ✅ | ✅ | ✅ |
|
||||||
|
| WebKit | ✅ | ✅ | ✅ | ✅ |
|
||||||
|
| Firefox | ✅ | ✅ | ✅ | ✅ |
|
||||||
|
|
||||||
## Version 1.23
|
## Version 1.23
|
||||||
|
|
||||||
### Network Replay
|
### Network Replay
|
||||||
|
|
@ -615,13 +630,13 @@ This version of Playwright was also tested against the following stable channels
|
||||||
|
|
||||||
## Version 1.9
|
## Version 1.9
|
||||||
|
|
||||||
- [Playwright Inspector](./inspector.md) is a **new GUI tool** to author and debug your tests.
|
- [Playwright Inspector](./debug.md) is a **new GUI tool** to author and debug your tests.
|
||||||
- **Line-by-line debugging** of your Playwright scripts, with play, pause and step-through.
|
- **Line-by-line debugging** of your Playwright scripts, with play, pause and step-through.
|
||||||
- Author new scripts by **recording user actions**.
|
- Author new scripts by **recording user actions**.
|
||||||
- **Generate element selectors** for your script by hovering over elements.
|
- **Generate element selectors** for your script by hovering over elements.
|
||||||
- Set the `PWDEBUG=1` environment variable to launch the Inspector
|
- Set the `PWDEBUG=1` environment variable to launch the Inspector
|
||||||
|
|
||||||
- **Pause script execution** with [`method: Page.pause`] in headed mode. Pausing the page launches [Playwright Inspector](./inspector.md) for debugging.
|
- **Pause script execution** with [`method: Page.pause`] in headed mode. Pausing the page launches [Playwright Inspector](./debug.md) for debugging.
|
||||||
|
|
||||||
- **New has-text pseudo-class** for CSS selectors. `:has-text("example")` matches any element containing `"example"` somewhere inside, possibly in a child or a descendant element. See [more examples](./selectors.md#text-selector).
|
- **New has-text pseudo-class** for CSS selectors. `:has-text("example")` matches any element containing `"example"` somewhere inside, possibly in a child or a descendant element. See [more examples](./selectors.md#text-selector).
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -5,6 +5,130 @@ title: "Release notes"
|
||||||
|
|
||||||
<!-- TOC -->
|
<!-- TOC -->
|
||||||
|
|
||||||
|
## Version 1.24
|
||||||
|
|
||||||
|
### 🌍 Multiple Web Servers in `playwright.config.ts`
|
||||||
|
|
||||||
|
Launch multiple web servers, databases, or other processes by passing an array of configurations:
|
||||||
|
|
||||||
|
```ts
|
||||||
|
// playwright.config.ts
|
||||||
|
import type { PlaywrightTestConfig } from '@playwright/test';
|
||||||
|
const config: PlaywrightTestConfig = {
|
||||||
|
webServer: [
|
||||||
|
{
|
||||||
|
command: 'npm run start',
|
||||||
|
port: 3000,
|
||||||
|
timeout: 120 * 1000,
|
||||||
|
reuseExistingServer: !process.env.CI,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
command: 'npm run backend',
|
||||||
|
port: 3333,
|
||||||
|
timeout: 120 * 1000,
|
||||||
|
reuseExistingServer: !process.env.CI,
|
||||||
|
}
|
||||||
|
],
|
||||||
|
use: {
|
||||||
|
baseURL: 'http://localhost:3000/',
|
||||||
|
},
|
||||||
|
};
|
||||||
|
export default config;
|
||||||
|
```
|
||||||
|
|
||||||
|
### 🐂 Debian 11 Bullseye Support
|
||||||
|
|
||||||
|
Playwright now supports Debian 11 Bullseye on x86_64 for Chromium, Firefox and WebKit. Let us know
|
||||||
|
if you encounter any issues!
|
||||||
|
|
||||||
|
Linux support looks like this:
|
||||||
|
|
||||||
|
| | Ubuntu 18.04 | Ubuntu 20.04 | Ubuntu 22.04 | Debian 11
|
||||||
|
| :--- | :---: | :---: | :---: | :---: |
|
||||||
|
| Chromium | ✅ | ✅ | ✅ | ✅ |
|
||||||
|
| WebKit | ✅ | ✅ | ✅ | ✅ |
|
||||||
|
| Firefox | ✅ | ✅ | ✅ | ✅ |
|
||||||
|
|
||||||
|
### 🕵️ Anonymous Describe
|
||||||
|
|
||||||
|
It is now possible to call [`method: Test.describe#2`] to create suites without a title. This is useful for giving a group of tests a common option with [`method: Test.use`].
|
||||||
|
|
||||||
|
```ts
|
||||||
|
test.describe(() => {
|
||||||
|
test.use({ colorScheme: 'dark' });
|
||||||
|
|
||||||
|
test('one', async ({ page }) => {
|
||||||
|
// ...
|
||||||
|
});
|
||||||
|
|
||||||
|
test('two', async ({ page }) => {
|
||||||
|
// ...
|
||||||
|
});
|
||||||
|
});
|
||||||
|
```
|
||||||
|
|
||||||
|
### 🧩 Component Tests Update
|
||||||
|
|
||||||
|
Playwright 1.24 Component Tests introduce `beforeMount` and `afterMount` hooks.
|
||||||
|
Use these to configure your app for tests.
|
||||||
|
|
||||||
|
For example, this could be used to setup App router in Vue.js:
|
||||||
|
|
||||||
|
```js
|
||||||
|
// src/component.spec.ts
|
||||||
|
import { test } from '@playwright/experimental-ct-vue';
|
||||||
|
import { Component } from './mycomponent';
|
||||||
|
|
||||||
|
test('should work', async ({ mount }) => {
|
||||||
|
const component = await mount(Component, {
|
||||||
|
hooksConfig: {
|
||||||
|
/* anything to configure your app */
|
||||||
|
}
|
||||||
|
});
|
||||||
|
});
|
||||||
|
```
|
||||||
|
|
||||||
|
```js
|
||||||
|
// playwright/index.ts
|
||||||
|
import { router } from '../router';
|
||||||
|
import { beforeMount } from '@playwright/experimental-ct-vue/hooks';
|
||||||
|
|
||||||
|
beforeMount(async ({ app, hooksConfig }) => {
|
||||||
|
app.use(router);
|
||||||
|
});
|
||||||
|
```
|
||||||
|
|
||||||
|
A similar configuration in Next.js would look like this:
|
||||||
|
|
||||||
|
```js
|
||||||
|
// src/component.spec.jsx
|
||||||
|
import { test } from '@playwright/experimental-ct-react';
|
||||||
|
import { Component } from './mycomponent';
|
||||||
|
|
||||||
|
test('should work', async ({ mount }) => {
|
||||||
|
const component = await mount(<Component></Component>, {
|
||||||
|
// Pass mock value from test into `beforeMount`.
|
||||||
|
hooksConfig: {
|
||||||
|
router: {
|
||||||
|
query: { page: 1, per_page: 10 },
|
||||||
|
asPath: '/posts'
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
});
|
||||||
|
```
|
||||||
|
|
||||||
|
```js
|
||||||
|
// playwright/index.js
|
||||||
|
import router from 'next/router';
|
||||||
|
import { beforeMount } from '@playwright/experimental-ct-react/hooks';
|
||||||
|
|
||||||
|
beforeMount(async ({ hooksConfig }) => {
|
||||||
|
// Before mount, redefine useRouter to return mock value from test.
|
||||||
|
router.useRouter = () => hooksConfig.router;
|
||||||
|
});
|
||||||
|
```
|
||||||
|
|
||||||
## Version 1.23
|
## Version 1.23
|
||||||
|
|
||||||
<div className="embed-youtube">
|
<div className="embed-youtube">
|
||||||
|
|
@ -110,7 +234,7 @@ Read more about [component testing with Playwright](./test-components).
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
```
|
```
|
||||||
* Playwright now runs on Ubuntu 22 amd64 and Ubuntu 22 arm64. We also publish new docker image `mcr.microsoft.com/playwright:v1.24.0-jammy`.
|
* Playwright now runs on Ubuntu 22 amd64 and Ubuntu 22 arm64. We also publish new docker image `mcr.microsoft.com/playwright:v1.24.2-jammy`.
|
||||||
|
|
||||||
### ⚠️ Breaking Changes ⚠️
|
### ⚠️ Breaking Changes ⚠️
|
||||||
|
|
||||||
|
|
@ -1115,13 +1239,13 @@ This version of Playwright was also tested against the following stable channels
|
||||||
|
|
||||||
## Version 1.9
|
## Version 1.9
|
||||||
|
|
||||||
- [Playwright Inspector](./inspector.md) is a **new GUI tool** to author and debug your tests.
|
- [Playwright Inspector](./debug.md) is a **new GUI tool** to author and debug your tests.
|
||||||
- **Line-by-line debugging** of your Playwright scripts, with play, pause and step-through.
|
- **Line-by-line debugging** of your Playwright scripts, with play, pause and step-through.
|
||||||
- Author new scripts by **recording user actions**.
|
- Author new scripts by **recording user actions**.
|
||||||
- **Generate element selectors** for your script by hovering over elements.
|
- **Generate element selectors** for your script by hovering over elements.
|
||||||
- Set the `PWDEBUG=1` environment variable to launch the Inspector
|
- Set the `PWDEBUG=1` environment variable to launch the Inspector
|
||||||
|
|
||||||
- **Pause script execution** with [`method: Page.pause`] in headed mode. Pausing the page launches [Playwright Inspector](./inspector.md) for debugging.
|
- **Pause script execution** with [`method: Page.pause`] in headed mode. Pausing the page launches [Playwright Inspector](./debug.md) for debugging.
|
||||||
|
|
||||||
- **New has-text pseudo-class** for CSS selectors. `:has-text("example")` matches any element containing `"example"` somewhere inside, possibly in a child or a descendant element. See [more examples](./selectors.md#text-selector).
|
- **New has-text pseudo-class** for CSS selectors. `:has-text("example")` matches any element containing `"example"` somewhere inside, possibly in a child or a descendant element. See [more examples](./selectors.md#text-selector).
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -5,6 +5,25 @@ title: "Release notes"
|
||||||
|
|
||||||
<!-- TOC -->
|
<!-- TOC -->
|
||||||
|
|
||||||
|
## Version 1.24
|
||||||
|
|
||||||
|
### 🐂 Debian 11 Bullseye Support
|
||||||
|
|
||||||
|
Playwright now supports Debian 11 Bullseye on x86_64 for Chromium, Firefox and WebKit. Let us know
|
||||||
|
if you encounter any issues!
|
||||||
|
|
||||||
|
Linux support looks like this:
|
||||||
|
|
||||||
|
| | Ubuntu 18.04 | Ubuntu 20.04 | Ubuntu 22.04 | Debian 11
|
||||||
|
| :--- | :---: | :---: | :---: | :---: |
|
||||||
|
| Chromium | ✅ | ✅ | ✅ | ✅ |
|
||||||
|
| WebKit | ✅ | ✅ | ✅ | ✅ |
|
||||||
|
| Firefox | ✅ | ✅ | ✅ | ✅ |
|
||||||
|
|
||||||
|
### New introduction docs
|
||||||
|
|
||||||
|
We rewrote our Getting Started docs to be more end-to-end testing focused. Check them out on [playwright.dev](https://playwright.dev/python/docs/intro).
|
||||||
|
|
||||||
## Version 1.23
|
## Version 1.23
|
||||||
|
|
||||||
### Network Replay
|
### Network Replay
|
||||||
|
|
@ -685,13 +704,13 @@ This version of Playwright was also tested against the following stable channels
|
||||||
|
|
||||||
## Version 1.9
|
## Version 1.9
|
||||||
|
|
||||||
- [Playwright Inspector](./inspector.md) is a **new GUI tool** to author and debug your tests.
|
- [Playwright Inspector](./debug.md) is a **new GUI tool** to author and debug your tests.
|
||||||
- **Line-by-line debugging** of your Playwright scripts, with play, pause and step-through.
|
- **Line-by-line debugging** of your Playwright scripts, with play, pause and step-through.
|
||||||
- Author new scripts by **recording user actions**.
|
- Author new scripts by **recording user actions**.
|
||||||
- **Generate element selectors** for your script by hovering over elements.
|
- **Generate element selectors** for your script by hovering over elements.
|
||||||
- Set the `PWDEBUG=1` environment variable to launch the Inspector
|
- Set the `PWDEBUG=1` environment variable to launch the Inspector
|
||||||
|
|
||||||
- **Pause script execution** with [`method: Page.pause`] in headed mode. Pausing the page launches [Playwright Inspector](./inspector.md) for debugging.
|
- **Pause script execution** with [`method: Page.pause`] in headed mode. Pausing the page launches [Playwright Inspector](./debug.md) for debugging.
|
||||||
|
|
||||||
- **New has-text pseudo-class** for CSS selectors. `:has-text("example")` matches any element containing `"example"` somewhere inside, possibly in a child or a descendant element. See [more examples](./selectors.md#text-selector).
|
- **New has-text pseudo-class** for CSS selectors. `:has-text("example")` matches any element containing `"example"` somewhere inside, possibly in a child or a descendant element. See [more examples](./selectors.md#text-selector).
|
||||||
|
|
||||||
|
|
|
||||||
96
docs/src/running-tests-csharp.md
Normal file
96
docs/src/running-tests-csharp.md
Normal file
|
|
@ -0,0 +1,96 @@
|
||||||
|
---
|
||||||
|
id: running-tests
|
||||||
|
title: "Running Tests"
|
||||||
|
---
|
||||||
|
|
||||||
|
You can run a single test, a set of tests or all tests. Tests can be run on different browsers. By default tests are run in a headless manner meaning no browser window will be opened while running the tests and results will be seen in the terminal. If you prefer you can run your tests in headed mode by using the `headless` test run parameter.
|
||||||
|
|
||||||
|
- Running all tests
|
||||||
|
|
||||||
|
```bash
|
||||||
|
dotnet test
|
||||||
|
```
|
||||||
|
|
||||||
|
- Running a single test file
|
||||||
|
|
||||||
|
```bash
|
||||||
|
dotnet test --filter "MyClassName"
|
||||||
|
```
|
||||||
|
|
||||||
|
- Run a set of test files
|
||||||
|
|
||||||
|
```bash
|
||||||
|
dotnet test --filter "MyClassName1|MyClassName2"
|
||||||
|
```
|
||||||
|
|
||||||
|
- Run the test with the title
|
||||||
|
|
||||||
|
```bash
|
||||||
|
dotnet test --filter "Name~TestMethod1"
|
||||||
|
```
|
||||||
|
|
||||||
|
- Running Tests on specific browsers
|
||||||
|
|
||||||
|
```bash tab=bash-bash
|
||||||
|
BROWSER=webkit dotnet test
|
||||||
|
```
|
||||||
|
|
||||||
|
```batch tab=bash-batch
|
||||||
|
set BROWSER=webkit
|
||||||
|
dotnet test
|
||||||
|
```
|
||||||
|
|
||||||
|
```powershell tab=bash-powershell
|
||||||
|
$env:BROWSER="webkit"
|
||||||
|
dotnet test
|
||||||
|
```
|
||||||
|
|
||||||
|
- Running Tests on multiple browsers
|
||||||
|
|
||||||
|
To run your test on multiple browsers or configurations you need to invoke the `dotnet test` command multiple times. There you can then either specify the `BROWSER` environment variable (like the previous) or pass the `browser` via the runsettings file:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
dotnet test --settings:chromium.runsettings
|
||||||
|
dotnet test --settings:firefox.runsettings
|
||||||
|
dotnet test --settings:webkit.runsettings
|
||||||
|
```
|
||||||
|
|
||||||
|
```xml
|
||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<RunSettings>
|
||||||
|
<TestRunParameters>
|
||||||
|
<Parameter name="browser" value="chromium" />
|
||||||
|
<Parameter name="headless" value="false" />
|
||||||
|
</TestRunParameters>
|
||||||
|
</RunSettings>
|
||||||
|
```
|
||||||
|
|
||||||
|
For more information see [selective unit tests](https://docs.microsoft.com/en-us/dotnet/core/testing/selective-unit-tests?pivots=mstest) in the Microsoft docs.
|
||||||
|
|
||||||
|
## Debugging Tests
|
||||||
|
|
||||||
|
Since Playwright runs in .NET, you can debug it with your debugger of choice in e.g. Visual Studio Code or Visual Studio. Playwright comes with the Playwright Inspector which allows you to step through Playwright API calls, see their debug logs and explore [selectors](./selectors.md).
|
||||||
|
|
||||||
|
```bash tab=bash-bash lang=csharp
|
||||||
|
PWDEBUG=1 dotnet test
|
||||||
|
```
|
||||||
|
|
||||||
|
```batch tab=bash-batch lang=csharp
|
||||||
|
set PWDEBUG=1
|
||||||
|
dotnet test
|
||||||
|
```
|
||||||
|
|
||||||
|
```powershell tab=bash-powershell lang=csharp
|
||||||
|
$env:PWDEBUG=1
|
||||||
|
dotnet test
|
||||||
|
```
|
||||||
|
|
||||||
|
<img width="712" alt="Playwright Inspector" src="https://user-images.githubusercontent.com/883973/108614092-8c478a80-73ac-11eb-9597-67dfce110e00.png"></img>
|
||||||
|
|
||||||
|
Check out our [debugging guide](./debug.md) to learn more about the [Playwright Inspector](./debug.md#playwright-inspector) as well as debugging with [Browser Developer tools](./debug.md#browser-developer-tools).
|
||||||
|
|
||||||
|
|
||||||
|
## What's Next
|
||||||
|
|
||||||
|
- [Generate tests with Codegen](./codegen.md)
|
||||||
|
- [See a trace of your tests](./trace-viewer.md)
|
||||||
|
|
@ -3,7 +3,13 @@ id: running-tests
|
||||||
title: "Running Tests"
|
title: "Running Tests"
|
||||||
---
|
---
|
||||||
|
|
||||||
You can run a single test, a set of tests or all tests. Tests can be run on one browser or multiple browsers. By default tests are run in a headless manner meaning no browser window will be opened while running the tests and results will be seen in the terminal. If you prefer you can run your tests in headed mode by using the `--headed` flag.
|
You can run a single test, a set of tests or all tests. Tests can be run on one browser or multiple browsers. By default tests are run in a headless manner meaning no browser window will be opened while running the tests and results will be seen in the terminal.
|
||||||
|
|
||||||
|
:::note
|
||||||
|
For a better debugging experience check out the [VS Code Extension](./getting-started-vscode.md) for Playwright where you can run tests, add breakpoints and debug your tests right from the VS Code editor.
|
||||||
|
:::
|
||||||
|
|
||||||
|
## Command Line
|
||||||
|
|
||||||
- Running all tests
|
- Running all tests
|
||||||
|
|
||||||
|
|
@ -14,7 +20,7 @@ You can run a single test, a set of tests or all tests. Tests can be run on one
|
||||||
- Running a single test file
|
- Running a single test file
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
npx playwright test test-1
|
npx playwright test landing-page.spec.ts
|
||||||
```
|
```
|
||||||
|
|
||||||
- Run a set of test files
|
- Run a set of test files
|
||||||
|
|
@ -23,10 +29,10 @@ You can run a single test, a set of tests or all tests. Tests can be run on one
|
||||||
npx playwright test tests/todo-page/ tests/landing-page/
|
npx playwright test tests/todo-page/ tests/landing-page/
|
||||||
```
|
```
|
||||||
|
|
||||||
- Run files that have `my-spec` or `my-spec-2` in the file name
|
- Run files that have `landing` or `login` in the file name
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
npx playwright test my-spec my-spec-2
|
npx playwright test landing login
|
||||||
```
|
```
|
||||||
|
|
||||||
- Run the test with the title
|
- Run the test with the title
|
||||||
|
|
@ -38,28 +44,59 @@ You can run a single test, a set of tests or all tests. Tests can be run on one
|
||||||
- Running tests in headed mode
|
- Running tests in headed mode
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
npx playwright test test-1 --headed
|
npx playwright test landing-page.spec.ts --headed
|
||||||
```
|
```
|
||||||
|
|
||||||
- Running Tests on specific browsers
|
- Running Tests on specific browsers
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
npx playwright test test-1.spec.ts --project=chromium
|
npx playwright test landing-page.ts --project=chromium
|
||||||
```
|
```
|
||||||
|
|
||||||
|
## Debugging Tests
|
||||||
|
|
||||||
|
Since Playwright runs in Node.js, you can debug it with your debugger of choice e.g. using `console.log` or inside your IDE or directly in VS Code with the [VS Code Extension](./getting-started-vscode.md). Playwright comes with the [Playwright Inspector](./debug.md#playwright-inspector) which allows you to step through Playwright API calls, see their debug logs and explore [selectors](./selectors.md).
|
||||||
|
|
||||||
|
|
||||||
|
- Debugging all tests:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
npx playwright test --debug
|
||||||
|
```
|
||||||
|
|
||||||
|
- Debugging one test file:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
npx playwright test example.spec.ts --debug
|
||||||
|
```
|
||||||
|
|
||||||
|
- Debugging a test from the line number where the `test(..` is defined:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
npx playwright test example.spec.ts:42 --debug
|
||||||
|
```
|
||||||
|
|
||||||
|
<img width="1188" alt="Screenshot 2022-07-29 at 23 50 13" src="https://user-images.githubusercontent.com/13063165/181847661-7ec5fb6c-7c21-4db0-9931-a593b21bafc2.png" />
|
||||||
|
|
||||||
|
|
||||||
|
Check out our [debugging guide](./debug.md) to learn more about the [Playwright Inspector](./debug.md#playwright-inspector) as well as debugging with [Browser Developer tools](./debug.md#browser-developer-tools).
|
||||||
|
|
||||||
|
|
||||||
## Test Reports
|
## Test Reports
|
||||||
|
|
||||||
The [HTML Reporter](./html-reporter.md) 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 tests errors as well as each step of the test. By default, the HTML report is opened automatically if some of the tests failed.
|
The [HTML Reporter](./html-reporter.md) 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. By default, the HTML report is opened automatically if some of the tests failed.
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
npx playwright show-report
|
npx playwright show-report
|
||||||
```
|
```
|
||||||
|
|
||||||
<img width="739" alt="image" src="https://user-images.githubusercontent.com/13063165/178003817-3bd2f088-4173-406c-a9e9-74c89181f381.png" />
|
<img width="739" alt="image" src="https://user-images.githubusercontent.com/13063165/181803518-1f554349-f72a-4ad3-a7aa-4d3d1b4cad13.png" />
|
||||||
|
|
||||||
|
You can click on each test and explore the tests errors as well as each step of the test.
|
||||||
|
|
||||||
|
<img width="739" alt="image" src="https://user-images.githubusercontent.com/13063165/181814327-a597109f-6f24-44a1-b47c-0de9dc7f5912.png" />
|
||||||
|
|
||||||
## What's Next
|
## What's Next
|
||||||
|
|
||||||
- [Debug tests with the Playwright Debugger](./debug.md)
|
|
||||||
- [Generate tests with Codegen](./codegen.md)
|
- [Generate tests with Codegen](./codegen.md)
|
||||||
- [See a trace of your tests](./trace-viewer.md)
|
- [See a trace of your tests](./trace-viewer.md)
|
||||||
78
docs/src/running-tests-python.md
Normal file
78
docs/src/running-tests-python.md
Normal file
|
|
@ -0,0 +1,78 @@
|
||||||
|
---
|
||||||
|
id: running-tests
|
||||||
|
title: "Running Tests"
|
||||||
|
---
|
||||||
|
|
||||||
|
You can run a single test, a set of tests or all tests. Tests can be run on one browser or multiple browsers. By default tests are run in a headless manner meaning no browser window will be opened while running the tests and results will be seen in the terminal. If you prefer you can run your tests in headed mode by using the `--headed` flag.
|
||||||
|
|
||||||
|
- Running tests on Chromium
|
||||||
|
|
||||||
|
```bash
|
||||||
|
pytest
|
||||||
|
```
|
||||||
|
|
||||||
|
- Running a single test file
|
||||||
|
|
||||||
|
```bash
|
||||||
|
pytest test_login.py
|
||||||
|
```
|
||||||
|
|
||||||
|
- Run a set of test files
|
||||||
|
|
||||||
|
```bash
|
||||||
|
pytest tests/todo-page/ tests/landing-page/
|
||||||
|
```
|
||||||
|
|
||||||
|
- Run the test with the function name
|
||||||
|
|
||||||
|
```bash
|
||||||
|
pytest -k "test_add_a_todo_item"
|
||||||
|
```
|
||||||
|
|
||||||
|
- Running tests in headed mode
|
||||||
|
|
||||||
|
```bash
|
||||||
|
pytest --headed test_login.py
|
||||||
|
```
|
||||||
|
|
||||||
|
- Running Tests on specific browsers
|
||||||
|
|
||||||
|
```bash
|
||||||
|
pytest test_login.py --browser webkit
|
||||||
|
```
|
||||||
|
|
||||||
|
- Running Tests on multiple browsers
|
||||||
|
|
||||||
|
```bash
|
||||||
|
pytest test_login.py --browser webkit --browser firefox
|
||||||
|
```
|
||||||
|
|
||||||
|
For more information see [Playwright Pytest usage](./test-runners.md) or the Pytest documentation for [general CLI usage](https://docs.pytest.org/en/stable/usage.html).
|
||||||
|
|
||||||
|
## Running Tests
|
||||||
|
|
||||||
|
Since Playwright runs in Python, you can debug it with your debugger of choice with e.g. the [Python extension](https://code.visualstudio.com/docs/python/python-tutorial) in Visual Studio Code. Playwright comes with the Playwright Inspector which allows you to step through Playwright API calls, see their debug logs and explore [selectors](./selectors.md).
|
||||||
|
|
||||||
|
|
||||||
|
```bash tab=bash-bash lang=python
|
||||||
|
PWDEBUG=1 pytest -s
|
||||||
|
```
|
||||||
|
|
||||||
|
```batch tab=bash-batch lang=python
|
||||||
|
set PWDEBUG=1
|
||||||
|
pytest -s
|
||||||
|
```
|
||||||
|
|
||||||
|
```powershell tab=bash-powershell lang=python
|
||||||
|
$env:PWDEBUG=1
|
||||||
|
pytest -s
|
||||||
|
```
|
||||||
|
<img width="712" alt="Playwright Inspector" src="https://user-images.githubusercontent.com/883973/108614092-8c478a80-73ac-11eb-9597-67dfce110e00.png"></img>
|
||||||
|
|
||||||
|
Check out our [debugging guide](./debug.md) to learn more about the [Playwright Inspector](./debug.md#playwright-inspector) as well as debugging with [Browser Developer tools](./debug.md#browser-developer-tools).
|
||||||
|
|
||||||
|
|
||||||
|
## What's Next
|
||||||
|
|
||||||
|
- [Generate tests with Codegen](./codegen.md)
|
||||||
|
- [See a trace of your tests](./trace-viewer.md)
|
||||||
|
|
@ -3,7 +3,9 @@ id: selectors
|
||||||
title: "Selectors"
|
title: "Selectors"
|
||||||
---
|
---
|
||||||
|
|
||||||
Selectors are strings that are used to create [Locator]s. Locators are used to perform actions on the elements by means of methods such as [`method: Locator.click`], [`method: Locator.fill`] and alike.
|
Selectors are strings that are used to create [Locator]s. Locators are used to perform actions on the elements by means of methods such as [`method: Locator.click`], [`method: Locator.fill`] and alike. For debugging selectors, see [here](./debug-selectors).
|
||||||
|
|
||||||
|
Writing good selectors is part art, part science so be sure to checkout the [Best Practices](#best-practices) section.
|
||||||
|
|
||||||
<!-- TOC -->
|
<!-- TOC -->
|
||||||
|
|
||||||
|
|
@ -1321,7 +1323,7 @@ await page.Locator("data-test-id=directions").ClickAsync();
|
||||||
### Avoid selectors tied to implementation
|
### Avoid selectors tied to implementation
|
||||||
|
|
||||||
[xpath] and [css] can be tied to the DOM structure or implementation. These selectors can break when
|
[xpath] and [css] can be tied to the DOM structure or implementation. These selectors can break when
|
||||||
the DOM structure changes.
|
the DOM structure changes. Similarly, [`method: Locator.nth`], [`method: Locator.first`], and [`method: Locator.last`] are tied to implementation and the structure of the DOM, and will target the incorrect element if the DOM changes.
|
||||||
|
|
||||||
```js
|
```js
|
||||||
// avoid long css or xpath chains
|
// avoid long css or xpath chains
|
||||||
|
|
|
||||||
|
|
@ -1,8 +1,11 @@
|
||||||
---
|
---
|
||||||
id: service-workers
|
id: service-workers-experimental
|
||||||
title: "Service Workers Guide"
|
title: "(Experimental) Service Worker Network Events"
|
||||||
---
|
---
|
||||||
|
|
||||||
|
:::warning
|
||||||
|
If you're looking to do general network mocking, routing, and interception, please see the [Network Guide](./network.md) first. Playwright provides built-in APIs for this use case that don't require the information below. However, if you're interested in requests made by Service Workers themselves, please read below.
|
||||||
|
:::
|
||||||
|
|
||||||
[Service Workers](https://developer.mozilla.org/en-US/docs/Web/API/Service_Worker_API) provide a browser-native method of handling requests made by a page with the native [Fetch API (`fetch`)](https://developer.mozilla.org/en-US/docs/Web/API/Fetch_API) along with other network-requested assets (like scripts, css, and images).
|
[Service Workers](https://developer.mozilla.org/en-US/docs/Web/API/Service_Worker_API) provide a browser-native method of handling requests made by a page with the native [Fetch API (`fetch`)](https://developer.mozilla.org/en-US/docs/Web/API/Fetch_API) along with other network-requested assets (like scripts, css, and images).
|
||||||
|
|
||||||
|
|
@ -10,35 +13,31 @@ They can act as a **network proxy** between the page and the external network to
|
||||||
|
|
||||||
Many sites that use Service Workers simply use them as a transparent optimization technique. While users might notice a faster experience, the app's implementation is unaware of their existence. Running the app with or without Service Workers enabled appears functionally equivalent.
|
Many sites that use Service Workers simply use them as a transparent optimization technique. While users might notice a faster experience, the app's implementation is unaware of their existence. Running the app with or without Service Workers enabled appears functionally equivalent.
|
||||||
|
|
||||||
**If your app uses Service Workers**, here's the scenarios that Playwright supports:
|
## How to Enable
|
||||||
|
|
||||||
1. Testing the page exactly like a user would experience it. This works out of the box in all supported browsers.
|
Playwright's inspection and routing of requests made by Service Workers are **experimental** and disabled by default.
|
||||||
1. Test your page without a Service Worker. Set [`option: Browser.newContext.serviceWorkers`] to `'block'`. You can test your page as if no Service Worker was registered.
|
|
||||||
1. Listen for and route network traffic via Playwright, whether it comes from a Service Worker or not. In Firefox and WebKit, set [`option: Browser.newContext.serviceWorkers`] to `'block'` to avoid Service Worker network traffic entirely. In Chromium, either block Service Workers or use [`method: BrowserContext.route`].
|
|
||||||
1. (Chromium-only) Test your Service Worker implementation itself. Use [`method: BrowserContext.serviceWorkers`] to get access to the Service Worker and evaluate there.
|
|
||||||
|
|
||||||
|
Set the `PW_EXPERIMENTAL_SERVICE_WORKER_NETWORK_EVENTS` environment variable to `1` (or any other value) to enable the feature. Only Chrome/Chromium are currently supported.
|
||||||
|
|
||||||
|
If you're using (or are interested in using this this feature), please comment on [this issue](https://github.com/microsoft/playwright/issues/15684) letting us know your use case.
|
||||||
|
|
||||||
## Service Worker Fetch
|
## Service Worker Fetch
|
||||||
|
|
||||||
:::note
|
|
||||||
The next sections are only currently supported when using Playwright with Chrome/Chromium. In Firefox and WebKit, if a Service Worker has a FetchEvent handler, Playwright will **not** emit Network events for all network traffic.
|
|
||||||
:::
|
|
||||||
|
|
||||||
### Accessing Service Workers and Waiting for Activation
|
### Accessing Service Workers and Waiting for Activation
|
||||||
|
|
||||||
You can use [`method: BrowserContext.serviceWorkers`] to list the Service [Worker]s, or specifically watch for the Service [Worker] if you anticipate a page will trigger its [registration](https://developer.mozilla.org/en-US/docs/Web/API/ServiceWorkerContainer/register):
|
You can use [`method: BrowserContext.serviceWorkers`] to list the Service [Worker]s, or specifically watch for the Service [Worker] if you anticipate a page will trigger its [registration](https://developer.mozilla.org/en-US/docs/Web/API/ServiceWorkerContainer/register):
|
||||||
|
|
||||||
```js tab=js-ts
|
```js tab=js-ts
|
||||||
const [ serviceworker ] = await Promise.all([
|
const [serviceworker] = await Promise.all([
|
||||||
context.waitForEvent('serviceworker'),
|
context.waitForEvent("serviceworker"),
|
||||||
page.goto('/example-with-a-service-worker.html'),
|
page.goto("/example-with-a-service-worker.html"),
|
||||||
]);
|
]);
|
||||||
```
|
```
|
||||||
|
|
||||||
```js tab=js-js
|
```js tab=js-js
|
||||||
const [ serviceworker ] = await Promise.all([
|
const [serviceworker] = await Promise.all([
|
||||||
context.waitForEvent('serviceworker'),
|
context.waitForEvent("serviceworker"),
|
||||||
page.goto('/example-with-a-service-worker.html'),
|
page.goto("/example-with-a-service-worker.html"),
|
||||||
]);
|
]);
|
||||||
```
|
```
|
||||||
|
|
||||||
|
|
@ -66,25 +65,27 @@ Worker serviceWorker = page.waitForRequest(() -> {
|
||||||
});
|
});
|
||||||
```
|
```
|
||||||
|
|
||||||
[`event: BrowserContext.serviceWorker`] is fired ***before*** the Service Worker's main script has been evaluated, so ***before*** calling service[`method: Worker.evaluate`] you should wait on its activation.
|
[`event: BrowserContext.serviceWorker`] is fired **_before_** the Service Worker's main script has been evaluated, so **_before_** calling service[`method: Worker.evaluate`] you should wait on its activation.
|
||||||
|
|
||||||
There are more iodiomatic methods of waiting for a Service Worker to be activated, but the following is an implementation agnostic method:
|
There are more iodiomatic methods of waiting for a Service Worker to be activated, but the following is an implementation agnostic method:
|
||||||
|
|
||||||
```js tab=js-ts
|
```js tab=js-ts
|
||||||
await page.evaluate(async () => {
|
await page.evaluate(async () => {
|
||||||
const registration = await window.navigator.serviceWorker.getRegistration();
|
const registration = await window.navigator.serviceWorker.getRegistration();
|
||||||
if (registration.active?.state === 'activated')
|
if (registration.active?.state === "activated") return;
|
||||||
return;
|
await new Promise((res) =>
|
||||||
await new Promise(res => window.navigator.serviceWorker.addEventListener('controllerchange', res));
|
window.navigator.serviceWorker.addEventListener("controllerchange", res)
|
||||||
|
);
|
||||||
});
|
});
|
||||||
```
|
```
|
||||||
|
|
||||||
```js tab=js-js
|
```js tab=js-js
|
||||||
await page.evaluate(async () => {
|
await page.evaluate(async () => {
|
||||||
const registration = await window.navigator.serviceWorker.getRegistration();
|
const registration = await window.navigator.serviceWorker.getRegistration();
|
||||||
if (registration.active?.state === 'activated')
|
if (registration.active?.state === "activated") return;
|
||||||
return;
|
await new Promise((res) =>
|
||||||
await new Promise(res => window.navigator.serviceWorker.addEventListener('controllerchange', res));
|
window.navigator.serviceWorker.addEventListener("controllerchange", res)
|
||||||
|
);
|
||||||
});
|
});
|
||||||
```
|
```
|
||||||
|
|
||||||
|
|
@ -130,18 +131,18 @@ page.evaluate(
|
||||||
|
|
||||||
Any network request made by the **Service Worker** will have:
|
Any network request made by the **Service Worker** will have:
|
||||||
|
|
||||||
* [`event: BrowserContext.request`] and its correponding events ([`event: BrowserContext.requestFinished`] and [`event: BrowserContext.response`], or [`event: BrowserContext.requestFailed`])
|
- [`event: BrowserContext.request`] and its correponding events ([`event: BrowserContext.requestFinished`] and [`event: BrowserContext.response`], or [`event: BrowserContext.requestFailed`])
|
||||||
* [`method: BrowserContext.route`] will see the request
|
- [`method: BrowserContext.route`] will see the request
|
||||||
* [`method: Request.serviceWorker`] will be set to the Service [Worker] instance, and [`method: Request.frame`] will **throw**
|
- [`method: Request.serviceWorker`] will be set to the Service [Worker] instance, and [`method: Request.frame`] will **throw**
|
||||||
* [`method: Response.fromServiceWorker`] will return `false`
|
- [`method: Response.fromServiceWorker`] will return `false`
|
||||||
|
|
||||||
Additionally, any network request made by the **Page** (including its sub-[Frame]s) will have:
|
Additionally, any network request made by the **Page** (including its sub-[Frame]s) will have:
|
||||||
|
|
||||||
* [`event: BrowserContext.request`] and its correponding events ([`event: BrowserContext.requestFinished`] and [`event: BrowserContext.response`], or [`event: BrowserContext.requestFailed`])
|
- [`event: BrowserContext.request`] and its correponding events ([`event: BrowserContext.requestFinished`] and [`event: BrowserContext.response`], or [`event: BrowserContext.requestFailed`])
|
||||||
* [`event: Page.request`] and its correponding events ([`event: Page.requestFinished`] and [`event: Page.response`], or [`event: Page.requestFailed`])
|
- [`event: Page.request`] and its correponding events ([`event: Page.requestFinished`] and [`event: Page.response`], or [`event: Page.requestFailed`])
|
||||||
* [`method: Page.route`] and [`method: Page.route`] will **not** see the request (if a Service Worker's fetch handler was registered)
|
- [`method: Page.route`] and [`method: Page.route`] will **not** see the request (if a Service Worker's fetch handler was registered)
|
||||||
* [`method: Request.serviceWorker`] will be set to `null`, and [`method: Request.frame`] will return the [Frame]
|
- [`method: Request.serviceWorker`] will be set to `null`, and [`method: Request.frame`] will return the [Frame]
|
||||||
* [`method: Response.fromServiceWorker`] will return `true` (if a Service Worker's fetch handler was registered)
|
- [`method: Response.fromServiceWorker`] will return `true` (if a Service Worker's fetch handler was registered)
|
||||||
|
|
||||||
Many Service Worker implementations simply execute the request from the page (possibly with some custom caching/offline logic omitted for simplicity):
|
Many Service Worker implementations simply execute the request from the page (possibly with some custom caching/offline logic omitted for simplicity):
|
||||||
|
|
||||||
|
|
@ -164,14 +165,16 @@ If a page registers the above Service Worker:
|
||||||
```html
|
```html
|
||||||
<!-- filename: index.html -->
|
<!-- filename: index.html -->
|
||||||
<script>
|
<script>
|
||||||
window.registrationPromise = navigator.serviceWorker.register('/transparent-service-worker.js');
|
window.registrationPromise = navigator.serviceWorker.register(
|
||||||
|
"/transparent-service-worker.js"
|
||||||
|
);
|
||||||
</script>
|
</script>
|
||||||
```
|
```
|
||||||
|
|
||||||
On the first visit to the page via [`method: Page.goto`], the following Request/Response events would be emitted (along with the corresponding network lifecycle events):
|
On the first visit to the page via [`method: Page.goto`], the following Request/Response events would be emitted (along with the corresponding network lifecycle events):
|
||||||
|
|
||||||
| Event | Owner | URL | Routed | [`method: Response.fromServiceWorker`] |
|
| Event | Owner | URL | Routed | [`method: Response.fromServiceWorker`] |
|
||||||
| - | - | - | - | - |
|
| --------------------------------- | ---------------- | ----------------------------- | ------ | -------------------------------------- |
|
||||||
| [`event: BrowserContext.request`] | [Frame] | index.html | Yes | |
|
| [`event: BrowserContext.request`] | [Frame] | index.html | Yes | |
|
||||||
| [`event: Page.request`] | [Frame] | index.html | Yes | |
|
| [`event: Page.request`] | [Frame] | index.html | Yes | |
|
||||||
| [`event: BrowserContext.request`] | Service [Worker] | transparent-service-worker.js | Yes | |
|
| [`event: BrowserContext.request`] | Service [Worker] | transparent-service-worker.js | Yes | |
|
||||||
|
|
@ -179,25 +182,21 @@ On the first visit to the page via [`method: Page.goto`], the following Request/
|
||||||
| [`event: BrowserContext.request`] | [Frame] | data.json | | Yes |
|
| [`event: BrowserContext.request`] | [Frame] | data.json | | Yes |
|
||||||
| [`event: Page.request`] | [Frame] | data.json | | Yes |
|
| [`event: Page.request`] | [Frame] | data.json | | Yes |
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
Since the example Service Worker just acts a basic transparent "proxy":
|
Since the example Service Worker just acts a basic transparent "proxy":
|
||||||
|
|
||||||
* There's 2 [`event: BrowserContext.request`] events for `data.json`; one [Frame]-owned, the other Service [Worker]-owned.
|
- There's 2 [`event: BrowserContext.request`] events for `data.json`; one [Frame]-owned, the other Service [Worker]-owned.
|
||||||
* Only the Service [Worker]-owned request for the resource was routable via [`method: BrowserContext.route`]; the [Frame]-owned events for `data.json` are not routeable, as they would not have even had the possibility to hit the external network since the Service Worker has a fetch handler registered.
|
- Only the Service [Worker]-owned request for the resource was routable via [`method: BrowserContext.route`]; the [Frame]-owned events for `data.json` are not routeable, as they would not have even had the possibility to hit the external network since the Service Worker has a fetch handler registered.
|
||||||
|
|
||||||
:::caution
|
:::caution
|
||||||
It's important to note: calling [`method: Request.frame`] or [`method: Response.frame`] will **throw** an exception, if called on a [Request]/[Response] that has a non-null [`method: Request.serviceWorker`].
|
It's important to note: calling [`method: Request.frame`] or [`method: Response.frame`] will **throw** an exception, if called on a [Request]/[Response] that has a non-null [`method: Request.serviceWorker`].
|
||||||
:::
|
:::
|
||||||
|
|
||||||
|
|
||||||
#### Advanced Example
|
#### Advanced Example
|
||||||
|
|
||||||
When a Service Worker handles a page's request, the Service Worker can make 0 to n requests to the external network. The Service Worker might respond directly from a cache, generate a reponse in memory, rewrite the request, make two requests and then combine into 1, etc.
|
When a Service Worker handles a page's request, the Service Worker can make 0 to n requests to the external network. The Service Worker might respond directly from a cache, generate a reponse in memory, rewrite the request, make two requests and then combine into 1, etc.
|
||||||
|
|
||||||
Consider the code snippets below to understand Playwright's view into the Request/Responses and how it impacts routing in some of these cases.
|
Consider the code snippets below to understand Playwright's view into the Request/Responses and how it impacts routing in some of these cases.
|
||||||
|
|
||||||
|
|
||||||
```js
|
```js
|
||||||
// filename: complex-service-worker.js
|
// filename: complex-service-worker.js
|
||||||
self.addEventListener("install", function (event) {
|
self.addEventListener("install", function (event) {
|
||||||
|
|
@ -243,14 +242,16 @@ And a page that simply registers the Service Worker:
|
||||||
```html
|
```html
|
||||||
<!-- filename: index.html -->
|
<!-- filename: index.html -->
|
||||||
<script>
|
<script>
|
||||||
window.registrationPromise = navigator.serviceWorker.register('/complex-service-worker.js');
|
window.registrationPromise = navigator.serviceWorker.register(
|
||||||
|
"/complex-service-worker.js"
|
||||||
|
);
|
||||||
</script>
|
</script>
|
||||||
```
|
```
|
||||||
|
|
||||||
On the first visit to the page via [`method: Page.goto`], the following Request/Response events would be emitted:
|
On the first visit to the page via [`method: Page.goto`], the following Request/Response events would be emitted:
|
||||||
|
|
||||||
| Event | Owner | URL | Routed | [`method: Response.fromServiceWorker`] |
|
| Event | Owner | URL | Routed | [`method: Response.fromServiceWorker`] |
|
||||||
| - | - | - | - | - |
|
| --------------------------------- | ---------------- | ------------------------- | ------ | -------------------------------------- |
|
||||||
| [`event: BrowserContext.request`] | [Frame] | index.html | Yes | |
|
| [`event: BrowserContext.request`] | [Frame] | index.html | Yes | |
|
||||||
| [`event: Page.request`] | [Frame] | index.html | Yes | |
|
| [`event: Page.request`] | [Frame] | index.html | Yes | |
|
||||||
| [`event: BrowserContext.request`] | Service [Worker] | complex-service-worker.js | Yes | |
|
| [`event: BrowserContext.request`] | Service [Worker] | complex-service-worker.js | Yes | |
|
||||||
|
|
@ -261,17 +262,17 @@ It's important to note that [`cache.add`](https://developer.mozilla.org/en-US/do
|
||||||
Once the Service Worker is activated and handling FetchEvents, if the page makes the following requests:
|
Once the Service Worker is activated and handling FetchEvents, if the page makes the following requests:
|
||||||
|
|
||||||
```js tab=js-ts
|
```js tab=js-ts
|
||||||
await page.evaluate(() => fetch('/addressbook.json'));
|
await page.evaluate(() => fetch("/addressbook.json"));
|
||||||
await page.evaluate(() => fetch('/foo'));
|
await page.evaluate(() => fetch("/foo"));
|
||||||
await page.evaluate(() => fetch('/tracker.js'));
|
await page.evaluate(() => fetch("/tracker.js"));
|
||||||
await page.evaluate(() => fetch('/fallthrough.txt'));
|
await page.evaluate(() => fetch("/fallthrough.txt"));
|
||||||
```
|
```
|
||||||
|
|
||||||
```js tab=js-js
|
```js tab=js-js
|
||||||
await page.evaluate(() => fetch('/addressbook.json'));
|
await page.evaluate(() => fetch("/addressbook.json"));
|
||||||
await page.evaluate(() => fetch('/foo'));
|
await page.evaluate(() => fetch("/foo"));
|
||||||
await page.evaluate(() => fetch('/tracker.js'));
|
await page.evaluate(() => fetch("/tracker.js"));
|
||||||
await page.evaluate(() => fetch('/fallthrough.txt'));
|
await page.evaluate(() => fetch("/fallthrough.txt"));
|
||||||
```
|
```
|
||||||
|
|
||||||
```python async
|
```python async
|
||||||
|
|
@ -305,7 +306,7 @@ page.evaluate("fetch('/fallthrough.txt')")
|
||||||
The following Request/Response events would be emitted:
|
The following Request/Response events would be emitted:
|
||||||
|
|
||||||
| Event | Owner | URL | Routed | [`method: Response.fromServiceWorker`] |
|
| Event | Owner | URL | Routed | [`method: Response.fromServiceWorker`] |
|
||||||
| - | - | - | - | - |
|
| --------------------------------- | ---------------- | ---------------- | ------ | -------------------------------------- |
|
||||||
| [`event: BrowserContext.request`] | [Frame] | addressbook.json | | Yes |
|
| [`event: BrowserContext.request`] | [Frame] | addressbook.json | | Yes |
|
||||||
| [`event: Page.request`] | [Frame] | addressbook.json | | Yes |
|
| [`event: Page.request`] | [Frame] | addressbook.json | | Yes |
|
||||||
| [`event: BrowserContext.request`] | Service [Worker] | bar | Yes | |
|
| [`event: BrowserContext.request`] | Service [Worker] | bar | Yes | |
|
||||||
|
|
@ -319,19 +320,19 @@ The following Request/Response events would be emitted:
|
||||||
|
|
||||||
It's important to note:
|
It's important to note:
|
||||||
|
|
||||||
* The page requested `/foo`, but the Service Worker requested `/bar`, so there are only [Frame]-owned events for `/foo`, but not `/bar`.
|
- The page requested `/foo`, but the Service Worker requested `/bar`, so there are only [Frame]-owned events for `/foo`, but not `/bar`.
|
||||||
* Likewise, the Service Worker never hit the network for `tracker.js`, so ony [Frame]-owned events were emitted for that request.
|
- Likewise, the Service Worker never hit the network for `tracker.js`, so ony [Frame]-owned events were emitted for that request.
|
||||||
|
|
||||||
## Routing Service Worker Requests Only
|
## Routing Service Worker Requests Only
|
||||||
|
|
||||||
```js tab=js-ts
|
```js tab=js-ts
|
||||||
await context.route('**', async route => {
|
await context.route("**", async (route) => {
|
||||||
if (route.request().serviceWorker()) {
|
if (route.request().serviceWorker()) {
|
||||||
// NB: calling route.request().frame() here would THROW
|
// NB: calling route.request().frame() here would THROW
|
||||||
return route.fulfill({
|
return route.fulfill({
|
||||||
contentType: 'text/plain',
|
contentType: "text/plain",
|
||||||
status: 200,
|
status: 200,
|
||||||
body: 'from sw',
|
body: "from sw",
|
||||||
});
|
});
|
||||||
} else {
|
} else {
|
||||||
return route.continue();
|
return route.continue();
|
||||||
|
|
@ -340,13 +341,13 @@ await context.route('**', async route => {
|
||||||
```
|
```
|
||||||
|
|
||||||
```js tab=js-js
|
```js tab=js-js
|
||||||
await context.route('**', async route => {
|
await context.route("**", async (route) => {
|
||||||
if (route.request().serviceWorker()) {
|
if (route.request().serviceWorker()) {
|
||||||
// NB: calling route.request().frame() here would THROW
|
// NB: calling route.request().frame() here would THROW
|
||||||
return route.fulfill({
|
return route.fulfill({
|
||||||
contentType: 'text/plain',
|
contentType: "text/plain",
|
||||||
status: 200,
|
status: 200,
|
||||||
body: 'from sw',
|
body: "from sw",
|
||||||
});
|
});
|
||||||
} else {
|
} else {
|
||||||
return route.continue();
|
return route.continue();
|
||||||
|
|
@ -406,4 +407,3 @@ browserContext.route("**", route -> {
|
||||||
## Known Limitations
|
## Known Limitations
|
||||||
|
|
||||||
Requests for updated Service Worker main script code currently cannot be routed (https://github.com/microsoft/playwright/issues/14711).
|
Requests for updated Service Worker main script code currently cannot be routed (https://github.com/microsoft/playwright/issues/14711).
|
||||||
|
|
||||||
|
|
@ -59,7 +59,7 @@ Here are the most common options available in the command line.
|
||||||
npx playwright test --reporter=dot
|
npx playwright test --reporter=dot
|
||||||
```
|
```
|
||||||
|
|
||||||
- Run in debug mode with [Playwright Inspector](./inspector.md)
|
- Run in debug mode with [Playwright Inspector](./debug.md)
|
||||||
```bash
|
```bash
|
||||||
npx playwright test --debug
|
npx playwright test --debug
|
||||||
```
|
```
|
||||||
|
|
|
||||||
|
|
@ -545,6 +545,72 @@ export default config;
|
||||||
|
|
||||||
However, most common ones like `headless` or `viewport` are available directly in the `use` section - see [basic options](#basic-options), [emulation](#emulation) or [network](#network).
|
However, most common ones like `headless` or `viewport` are available directly in the `use` section - see [basic options](#basic-options), [emulation](#emulation) or [network](#network).
|
||||||
|
|
||||||
|
## Explicit Context Creation and Option Inheritance
|
||||||
|
|
||||||
|
If using the built-in `browser` fixture, calling [`method: Browser.newContext`] will create a context with options inherted from the config:
|
||||||
|
|
||||||
|
```js tab=js-ts
|
||||||
|
// playwright.config.ts
|
||||||
|
import type { PlaywrightTestConfig } from "@playwright/test";
|
||||||
|
|
||||||
|
const config: PlaywrightTestConfig = {
|
||||||
|
use: {
|
||||||
|
userAgent: 'some custom ua',
|
||||||
|
viewport: { width: 100, height: 100 },
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
export default config;
|
||||||
|
```
|
||||||
|
|
||||||
|
```js tab=js-js
|
||||||
|
// @ts-check
|
||||||
|
// example.spec.js
|
||||||
|
|
||||||
|
/** @type {import('@playwright/test').PlaywrightTestConfig} */
|
||||||
|
const config = {
|
||||||
|
use: {
|
||||||
|
userAgent: 'some custom ua',
|
||||||
|
viewport: { width: 100, height: 100 },
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
module.exports = config;
|
||||||
|
```
|
||||||
|
|
||||||
|
An example test illustrating the initial context options are set:
|
||||||
|
|
||||||
|
```js tab=js-ts
|
||||||
|
// example.spec.ts
|
||||||
|
import { test, expect } from "@playwright/test";
|
||||||
|
|
||||||
|
test('should inherit use options on context when using built-in browser fixture', async ({
|
||||||
|
browser,
|
||||||
|
}) => {
|
||||||
|
const context = await browser.newContext();
|
||||||
|
const page = await context.newPage();
|
||||||
|
expect(await page.evaluate(() => navigator.userAgent)).toBe('some custom ua');
|
||||||
|
expect(await page.evaluate(() => window.innerWidth)).toBe(100);
|
||||||
|
await context.close();
|
||||||
|
});
|
||||||
|
```
|
||||||
|
|
||||||
|
```js tab=js-js
|
||||||
|
// @ts-check
|
||||||
|
// example.spec.ts
|
||||||
|
const { test, expect } = require("@playwright/test");
|
||||||
|
|
||||||
|
test('should inherit use options on context when using built-in browser fixture', async ({
|
||||||
|
browser,
|
||||||
|
}) => {
|
||||||
|
const context = await browser.newContext();
|
||||||
|
const page = await context.newPage();
|
||||||
|
expect(await page.evaluate(() => navigator.userAgent)).toBe('some custom ua');
|
||||||
|
expect(await page.evaluate(() => window.innerWidth)).toBe(100);
|
||||||
|
await context.close();
|
||||||
|
});
|
||||||
|
```
|
||||||
|
|
||||||
## Testing options
|
## Testing options
|
||||||
|
|
||||||
In addition to configuring [Browser] or [BrowserContext], videos or screenshots, Playwright Test has many options to configure how your tests are run. Below are the most common ones, see [TestConfig] for the full list.
|
In addition to configuring [Browser] or [BrowserContext], videos or screenshots, Playwright Test has many options to configure how your tests are run. Below are the most common ones, see [TestConfig] for the full list.
|
||||||
|
|
|
||||||
|
|
@ -237,68 +237,76 @@ There is no guarantee about the order of test execution across the files, becaus
|
||||||
|
|
||||||
### Sort test files alphabetically
|
### Sort test files alphabetically
|
||||||
|
|
||||||
When you **disable parallel test execution**, Playwright Test runs test files in alphabetical order. You can use some naming convention to control the test order, for example `test001.spec.ts`, `test002.spec.ts` and so on.
|
When you **disable parallel test execution**, Playwright Test runs test files in alphabetical order. You can use some naming convention to control the test order, for example `001-user-signin-flow.spec.ts`, `002-create-new-document.spec.ts` and so on.
|
||||||
|
|
||||||
### Use a "test list" file
|
### Use a "test list" file
|
||||||
|
|
||||||
Suppose we have two test files.
|
You can put your tests in helper functions in multiple files. Consider the following example where tests are not defined directly in the file, but rather in a wrapper function.
|
||||||
|
|
||||||
```js tab=js-js
|
```js tab=js-js
|
||||||
// feature-a.spec.js
|
// feature-a.spec.js
|
||||||
const { test, expect } = require('@playwright/test');
|
const { test, expect } = require('@playwright/test');
|
||||||
|
|
||||||
test.describe('feature-a', () => {
|
module.exports = function createTests() {
|
||||||
test('example test', async ({ page }) => {
|
test('feature-a example test', async ({ page }) => {
|
||||||
// ... test goes here
|
// ... test goes here
|
||||||
});
|
});
|
||||||
});
|
};
|
||||||
|
|
||||||
|
|
||||||
// feature-b.spec.js
|
// feature-b.spec.js
|
||||||
const { test, expect } = require('@playwright/test');
|
const { test, expect } = require('@playwright/test');
|
||||||
|
|
||||||
test.describe('feature-b', () => {
|
module.exports = function createTests() {
|
||||||
test.use({ viewport: { width: 500, height: 500 } });
|
test.use({ viewport: { width: 500, height: 500 } });
|
||||||
test('example test', async ({ page }) => {
|
|
||||||
|
test('feature-b example test', async ({ page }) => {
|
||||||
// ... test goes here
|
// ... test goes here
|
||||||
});
|
});
|
||||||
});
|
};
|
||||||
```
|
```
|
||||||
|
|
||||||
```js tab=js-ts
|
```js tab=js-ts
|
||||||
// feature-a.spec.ts
|
// feature-a.spec.ts
|
||||||
import { test, expect } from '@playwright/test';
|
import { test, expect } from '@playwright/test';
|
||||||
|
|
||||||
test.describe('feature-a', () => {
|
export default function createTests() {
|
||||||
test('example test', async ({ page }) => {
|
test('feature-a example test', async ({ page }) => {
|
||||||
// ... test goes here
|
// ... test goes here
|
||||||
});
|
});
|
||||||
});
|
}
|
||||||
|
|
||||||
|
|
||||||
// feature-b.spec.ts
|
// feature-b.spec.ts
|
||||||
import { test, expect } from '@playwright/test';
|
import { test, expect } from '@playwright/test';
|
||||||
|
|
||||||
test.describe('feature-b', () => {
|
export default function createTests() {
|
||||||
test.use({ viewport: { width: 500, height: 500 } });
|
test.use({ viewport: { width: 500, height: 500 } });
|
||||||
test('example test', async ({ page }) => {
|
|
||||||
|
test('feature-b example test', async ({ page }) => {
|
||||||
// ... test goes here
|
// ... test goes here
|
||||||
});
|
});
|
||||||
});
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
We can create a test list file that will control the order of tests - first run `feature-b` tests, then `feature-a` tests.
|
You can create a test list file that will control the order of tests - first run `feature-b` tests, then `feature-a` tests. Note how each test file is wrapped in a `test.describe()` block that calls the function where tests are defined. This way `test.use()` calls only affect tests from a single file.
|
||||||
|
|
||||||
|
|
||||||
```js tab=js-js
|
```js tab=js-js
|
||||||
// test.list.js
|
// test.list.js
|
||||||
require('./feature-b.spec.js');
|
const { test } = require('@playwright/test');
|
||||||
require('./feature-a.spec.js');
|
|
||||||
|
test.describe(require('./feature-b.spec.js'));
|
||||||
|
test.describe(require('./feature-a.spec.js'));
|
||||||
```
|
```
|
||||||
|
|
||||||
```js tab=js-ts
|
```js tab=js-ts
|
||||||
// test.list.ts
|
// test.list.ts
|
||||||
import './feature-b.spec.ts';
|
import { test } from '@playwright/test';
|
||||||
import './feature-a.spec.ts';
|
import featureBTests from './feature-b.spec.ts';
|
||||||
|
import featureATests from './feature-a.spec.ts';
|
||||||
|
|
||||||
|
test.describe(featureBTests);
|
||||||
|
test.describe(featureATests);
|
||||||
```
|
```
|
||||||
|
|
||||||
Now **disable parallel execution** by setting workers to one, and specify your test list file.
|
Now **disable parallel execution** by setting workers to one, and specify your test list file.
|
||||||
|
|
@ -328,5 +336,6 @@ export default config;
|
||||||
```
|
```
|
||||||
|
|
||||||
:::note
|
:::note
|
||||||
Make sure to wrap tests with `test.describe()` blocks so that any `test.use()` calls only affect tests from a single file.
|
Do not define your tests directly in a helper file. This could lead to unexpected results because your
|
||||||
|
tests are now dependent on the order of `import`/`require` statements. Instead, wrap tests in a function that will be explicitly called by a test list file, as in the example above.
|
||||||
:::
|
:::
|
||||||
|
|
|
||||||
|
|
@ -108,6 +108,69 @@ By default NUnit will run all test files in parallel, while running tests inside
|
||||||
|
|
||||||
For CPU-bound tests, we recommend using as many workers as there are cores on your system, divided by 2. For IO-bound tests you can use as many workers as you have cores.
|
For CPU-bound tests, we recommend using as many workers as there are cores on your system, divided by 2. For IO-bound tests you can use as many workers as you have cores.
|
||||||
|
|
||||||
|
### Customizing [BrowserContext] options
|
||||||
|
|
||||||
|
To customize context options, you can override the `ContextOptions` method of your test class derived from `Microsoft.Playwright.MSTest.PageTest` or `Microsoft.Playwright.MSTest.ContextTest`. See the following example:
|
||||||
|
|
||||||
|
```csharp
|
||||||
|
using Microsoft.Playwright.NUnit;
|
||||||
|
|
||||||
|
namespace PlaywrightTests;
|
||||||
|
|
||||||
|
[Parallelizable(ParallelScope.Self)]
|
||||||
|
public class MyTest : PageTest
|
||||||
|
{
|
||||||
|
[Test]
|
||||||
|
public async Task TestWithCustomContextOptions()
|
||||||
|
{
|
||||||
|
// The following Page (and BrowserContext) instance has the custom colorScheme, viewport and baseURL set:
|
||||||
|
await Page.GotoAsync("/login");
|
||||||
|
}
|
||||||
|
|
||||||
|
public override BrowserNewContextOptions ContextOptions()
|
||||||
|
{
|
||||||
|
return new BrowserNewContextOptions()
|
||||||
|
{
|
||||||
|
ColorScheme = ColorScheme.Light,
|
||||||
|
ViewportSize = new()
|
||||||
|
{
|
||||||
|
Width = 1920,
|
||||||
|
Height = 1080
|
||||||
|
},
|
||||||
|
BaseURL = "https://github.com",
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### Customizing [Browser]/launch options
|
||||||
|
|
||||||
|
[Browser]/launch options can be override either using a run settings file or by setting the run settings options directly via the
|
||||||
|
CLI. See the following example:
|
||||||
|
|
||||||
|
```xml
|
||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<RunSettings>
|
||||||
|
<TestRunParameters>
|
||||||
|
<Parameter name="browser" value="chromium" />
|
||||||
|
<Parameter name="headless" value="false" />
|
||||||
|
<Parameter name="channel" value="msedge" />
|
||||||
|
</TestRunParameters>
|
||||||
|
</RunSettings>
|
||||||
|
```
|
||||||
|
|
||||||
|
```bash tab=bash-bash
|
||||||
|
dotnet test -- TestRunParameters.Parameter\(name=\"browser\", value=\"chromium\"\) TestRunParameters.Parameter\(name=\"headless\", value=\"false\"\) TestRunParameters.Parameter\(name=\"channel\", value=\"msedge\"\)
|
||||||
|
```
|
||||||
|
|
||||||
|
```batch tab=bash-batch
|
||||||
|
dotnet test -- TestRunParameters.Parameter(name=\"browser\", value=\"chromium\") TestRunParameters.Parameter(name=\"headless\", value=\"false\") TestRunParameters.Parameter(name=\"channel\", value=\"msedge\")
|
||||||
|
```
|
||||||
|
|
||||||
|
```powershell tab=bash-powershell
|
||||||
|
dotnet test -- TestRunParameters.Parameter(name=\"browser\", value=\"chromium\") TestRunParameters.Parameter(name=\"headless\", value=\"false\") TestRunParameters.Parameter(name=\"channel\", value=\"msedge\")
|
||||||
|
```
|
||||||
|
|
||||||
### Using Verbose API Logs
|
### Using Verbose API Logs
|
||||||
|
|
||||||
When you have enabled the [verbose API log](./debug.md#verbose-api-logs), via the `DEBUG` environment variable, you will see the messages in the standard error stream. In NUnit, within Visual Studio, that will be the `Tests` pane of the `Output` window. It will also be displayed in the `Test Log` for each test.
|
When you have enabled the [verbose API log](./debug.md#verbose-api-logs), via the `DEBUG` environment variable, you will see the messages in the standard error stream. In NUnit, within Visual Studio, that will be the `Tests` pane of the `Output` window. It will also be displayed in the `Test Log` for each test.
|
||||||
|
|
@ -247,6 +310,73 @@ By default MSTest will run all classes in parallel, while running tests inside e
|
||||||
dotnet test --settings:.runsettings -- MSTest.Parallelize.Workers=4
|
dotnet test --settings:.runsettings -- MSTest.Parallelize.Workers=4
|
||||||
```
|
```
|
||||||
|
|
||||||
|
### Customizing [BrowserContext] options
|
||||||
|
|
||||||
|
To customize context options, you can override the `ContextOptions` method of your test class derived from `Microsoft.Playwright.MSTest.PageTest` or `Microsoft.Playwright.MSTest.ContextTest`. See the following example:
|
||||||
|
|
||||||
|
```csharp
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
using Microsoft.Playwright;
|
||||||
|
using Microsoft.Playwright.MSTest;
|
||||||
|
using Microsoft.VisualStudio.TestTools.UnitTesting;
|
||||||
|
|
||||||
|
namespace PlaywrightTests;
|
||||||
|
|
||||||
|
[TestClass]
|
||||||
|
public class UnitTest1 : PageTest
|
||||||
|
{
|
||||||
|
[TestMethod]
|
||||||
|
public async Task TestWithCustomContextOptions()
|
||||||
|
{
|
||||||
|
// The following Page (and BrowserContext) instance has the custom colorScheme, viewport and baseURL set:
|
||||||
|
await Page.GotoAsync("/login");
|
||||||
|
}
|
||||||
|
|
||||||
|
public override BrowserNewContextOptions ContextOptions()
|
||||||
|
{
|
||||||
|
return new BrowserNewContextOptions()
|
||||||
|
{
|
||||||
|
ColorScheme = ColorScheme.Light,
|
||||||
|
ViewportSize = new()
|
||||||
|
{
|
||||||
|
Width = 1920,
|
||||||
|
Height = 1080
|
||||||
|
},
|
||||||
|
BaseURL = "https://github.com",
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
```
|
||||||
|
|
||||||
|
### Customizing [Browser]/launch options
|
||||||
|
|
||||||
|
[Browser]/launch options can be override either using a run settings file or by setting the run settings options directly via the
|
||||||
|
CLI. See the following example:
|
||||||
|
|
||||||
|
```xml
|
||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<RunSettings>
|
||||||
|
<TestRunParameters>
|
||||||
|
<Parameter name="browser" value="chromium" />
|
||||||
|
<Parameter name="headless" value="false" />
|
||||||
|
<Parameter name="channel" value="msedge" />
|
||||||
|
</TestRunParameters>
|
||||||
|
</RunSettings>
|
||||||
|
```
|
||||||
|
|
||||||
|
```bash tab=bash-bash
|
||||||
|
dotnet test -- TestRunParameters.Parameter\(name=\"browser\", value=\"chromium\"\) TestRunParameters.Parameter\(name=\"headless\", value=\"false\"\) TestRunParameters.Parameter\(name=\"channel\", value=\"msedge\"\)
|
||||||
|
```
|
||||||
|
|
||||||
|
```batch tab=bash-batch
|
||||||
|
dotnet test -- TestRunParameters.Parameter(name=\"browser\", value=\"chromium\") TestRunParameters.Parameter(name=\"headless\", value=\"false\") TestRunParameters.Parameter(name=\"channel\", value=\"msedge\")
|
||||||
|
```
|
||||||
|
|
||||||
|
```powershell tab=bash-powershell
|
||||||
|
dotnet test -- TestRunParameters.Parameter(name=\"browser\", value=\"chromium\") TestRunParameters.Parameter(name=\"headless\", value=\"false\") TestRunParameters.Parameter(name=\"channel\", value=\"msedge\")
|
||||||
|
```
|
||||||
|
|
||||||
### Using Verbose API Logs
|
### Using Verbose API Logs
|
||||||
|
|
||||||
When you have enabled the [verbose API log](./debug.md#verbose-api-logs), via the `DEBUG` environment variable, you will see the messages in the standard error stream. In MSTest, within Visual Studio, that will be the `Tests` pane of the `Output` window. It will also be displayed in the `Test Log` for each test.
|
When you have enabled the [verbose API log](./debug.md#verbose-api-logs), via the `DEBUG` environment variable, you will see the messages in the standard error stream. In MSTest, within Visual Studio, that will be the `Tests` pane of the `Output` window. It will also be displayed in the `Test Log` for each test.
|
||||||
|
|
|
||||||
|
|
@ -1,42 +1,16 @@
|
||||||
---
|
---
|
||||||
id: test-runners
|
id: test-runners
|
||||||
title: "Pytest plugin"
|
title: "Pytest Plugin Reference"
|
||||||
---
|
---
|
||||||
|
|
||||||
Write end-to-end tests for your web apps with [Pytest](https://docs.pytest.org/en/stable/).
|
Playwright provides a [Pytest](https://docs.pytest.org/en/stable/) plugin to write end-to-end tests. To get started with it, refer to the [getting started guide](./intro.md).
|
||||||
|
|
||||||
<!-- TOC -->
|
|
||||||
|
|
||||||
## Usage
|
## Usage
|
||||||
|
|
||||||
```bash
|
To run your tests, use [Pytest](https://docs.pytest.org/en/stable/) CLI.
|
||||||
pip install pytest-playwright
|
|
||||||
```
|
|
||||||
|
|
||||||
Use the `page` fixture to write a basic test. See [more examples](#examples).
|
|
||||||
|
|
||||||
```py
|
|
||||||
# test_my_application.py
|
|
||||||
def test_example_is_working(page):
|
|
||||||
page.goto("https://example.com")
|
|
||||||
assert page.inner_text('h1') == 'Example Domain'
|
|
||||||
page.locator("text=More information").click()
|
|
||||||
```
|
|
||||||
|
|
||||||
To run your tests, use pytest CLI.
|
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
# Run tests (Chromium and headless by default)
|
pytest --browser webkit --headed
|
||||||
pytest
|
|
||||||
|
|
||||||
# Run tests in headed mode
|
|
||||||
pytest --headed
|
|
||||||
|
|
||||||
# Run tests in a different browser (chromium, firefox, webkit)
|
|
||||||
pytest --browser firefox
|
|
||||||
|
|
||||||
# Run tests in multiple browsers
|
|
||||||
pytest --browser chromium --browser webkit
|
|
||||||
```
|
```
|
||||||
|
|
||||||
If you want to add the CLI arguments automatically without specifying them, you can use the [pytest.ini](https://docs.pytest.org/en/stable/reference.html#ini-options-ref) file:
|
If you want to add the CLI arguments automatically without specifying them, you can use the [pytest.ini](https://docs.pytest.org/en/stable/reference.html#ini-options-ref) file:
|
||||||
|
|
|
||||||
|
|
@ -56,7 +56,7 @@ The snapshot name `example-test-1-chromium-darwin.png` consists of a few parts:
|
||||||
If you are not on the same operating system as your CI system, you can use Docker to generate/update the screenshots:
|
If you are not on the same operating system as your CI system, you can use Docker to generate/update the screenshots:
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
docker run --rm --network host -v $(pwd):/work/ -w /work/ -it mcr.microsoft.com/playwright:v1.24.0-focal /bin/bash
|
docker run --rm --network host -v $(pwd):/work/ -w /work/ -it mcr.microsoft.com/playwright:v1.24.2-focal /bin/bash
|
||||||
npm install
|
npm install
|
||||||
npx playwright test --update-snapshots
|
npx playwright test --update-snapshots
|
||||||
```
|
```
|
||||||
|
|
@ -70,7 +70,7 @@ npx playwright test --update-snapshots
|
||||||
> Note that `snapshotName` also accepts an array of path segments to the snapshot file such as `expect().toHaveScreenshot(['relative', 'path', 'to', 'snapshot.png'])`.
|
> Note that `snapshotName` also accepts an array of path segments to the snapshot file such as `expect().toHaveScreenshot(['relative', 'path', 'to', 'snapshot.png'])`.
|
||||||
> However, this path must stay within the snapshots directory for each test file (i.e. `a.spec.js-snapshots`), otherwise it will throw.
|
> However, this path must stay within the snapshots directory for each test file (i.e. `a.spec.js-snapshots`), otherwise it will throw.
|
||||||
|
|
||||||
Playwright Test uses the [pixelmatch](https://github.com/mapbox/pixelmatch) library. You can [pass various options](./test-assertions#expectpageorlocatortomatchsnapshot-options) to modify its behavior:
|
Playwright Test uses the [pixelmatch](https://github.com/mapbox/pixelmatch) library. You can [pass various options](./test-assertions#page-assertions-to-have-screenshot-2) to modify its behavior:
|
||||||
|
|
||||||
```js tab=js-js
|
```js tab=js-js
|
||||||
// example.spec.js
|
// example.spec.js
|
||||||
|
|
|
||||||
|
|
@ -67,6 +67,10 @@ test('example', async ({ page }) => {
|
||||||
});
|
});
|
||||||
```
|
```
|
||||||
|
|
||||||
|
:::note
|
||||||
|
TypeScript with ESM requires Node.js 16 or higher.
|
||||||
|
:::
|
||||||
|
|
||||||
## TypeScript path mapping
|
## TypeScript path mapping
|
||||||
|
|
||||||
If you use [path mapping](https://www.typescriptlang.org/docs/handbook/module-resolution.html#path-mapping) in your `tsconfig.json`, Playwright Test will pick it up. Make sure that `baseUrl` is also set.
|
If you use [path mapping](https://www.typescriptlang.org/docs/handbook/module-resolution.html#path-mapping) in your `tsconfig.json`, Playwright Test will pick it up. Make sure that `baseUrl` is also set.
|
||||||
|
|
|
||||||
|
|
@ -142,7 +142,7 @@ Once you're on Playwright Test, you get a lot!
|
||||||
- Built-in test artifact collection: [video recording](./test-configuration#record-video), [screenshots](./test-configuration#automatic-screenshots) and [playwright traces](./test-configuration#record-test-trace)
|
- Built-in test artifact collection: [video recording](./test-configuration#record-video), [screenshots](./test-configuration#automatic-screenshots) and [playwright traces](./test-configuration#record-test-trace)
|
||||||
|
|
||||||
Also you get all these ✨ awesome tools ✨ that come bundled with Playwright Test:
|
Also you get all these ✨ awesome tools ✨ that come bundled with Playwright Test:
|
||||||
- [Playwright Inspector](./inspector)
|
- [Playwright Inspector](./debug.md)
|
||||||
- [Playwright Test Code generation](./auth#code-generation)
|
- [Playwright Test Code generation](./auth#code-generation)
|
||||||
- [Playwright Tracing](./trace-viewer) for post-mortem debugging
|
- [Playwright Tracing](./trace-viewer) for post-mortem debugging
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -3,8 +3,6 @@ id: troubleshooting
|
||||||
title: "Troubleshooting"
|
title: "Troubleshooting"
|
||||||
---
|
---
|
||||||
|
|
||||||
<!-- TOC -->
|
|
||||||
|
|
||||||
## Browser dependencies
|
## Browser dependencies
|
||||||
|
|
||||||
Playwright does self-inspection every time it runs to make sure the browsers can be launched successfully. If there are missing
|
Playwright does self-inspection every time it runs to make sure the browsers can be launched successfully. If there are missing
|
||||||
|
|
@ -15,6 +13,7 @@ which has a command to install all necessary dependencies automatically for Ubun
|
||||||
LTS releases.
|
LTS releases.
|
||||||
|
|
||||||
## Code transpilation issues
|
## Code transpilation issues
|
||||||
|
* langs: js
|
||||||
|
|
||||||
If you are using a JavaScript transpiler like babel or TypeScript, calling `evaluate()` with an async function might not work. This is because while `playwright` uses `Function.prototype.toString()` to serialize functions while transpilers could be changing the output code in such a way it's incompatible with `playwright`.
|
If you are using a JavaScript transpiler like babel or TypeScript, calling `evaluate()` with an async function might not work. This is because while `playwright` uses `Function.prototype.toString()` to serialize functions while transpilers could be changing the output code in such a way it's incompatible with `playwright`.
|
||||||
|
|
||||||
|
|
@ -27,11 +26,55 @@ await page.evaluate(`(async() => {
|
||||||
```
|
```
|
||||||
|
|
||||||
## Node.js requirements
|
## Node.js requirements
|
||||||
|
* langs: js
|
||||||
|
|
||||||
|
Playwright requires Node.js version 14 or above
|
||||||
|
|
||||||
### ReferenceError: URL is not defined
|
### ReferenceError: URL is not defined
|
||||||
|
|
||||||
Playwright requires Node.js 14 or higher. Node.js 8 is not supported, and will cause you to receive this error.
|
Playwright requires Node.js 14 or higher.
|
||||||
|
|
||||||
# Please file an issue
|
### Unknown file extension ".ts"
|
||||||
|
|
||||||
|
Running TypeScript tests in `"type": "module"` project requires Node.js 16 or higher.
|
||||||
|
|
||||||
|
## .NET requirements
|
||||||
|
* langs: csharp
|
||||||
|
|
||||||
|
Playwright is distributed as a **.NET Standard 2.0** library. We recommend .NET 6 or newer.
|
||||||
|
|
||||||
|
## Python requirements
|
||||||
|
* langs: python
|
||||||
|
|
||||||
|
Playwright requires **Python 3.7** or newer.
|
||||||
|
|
||||||
|
## Java requirements
|
||||||
|
* langs: java
|
||||||
|
|
||||||
|
Playwright requires **Java 8** or newer.
|
||||||
|
|
||||||
|
## System requirements
|
||||||
|
|
||||||
|
The browser binaries for Chromium, Firefox and WebKit work across the 3 platforms (Windows, macOS, Linux):
|
||||||
|
|
||||||
|
### Windows
|
||||||
|
|
||||||
|
Works with Windows and Windows Subsystem for Linux (WSL).
|
||||||
|
|
||||||
|
### macOS
|
||||||
|
|
||||||
|
Requires 11 (Big Sur) or above.
|
||||||
|
|
||||||
|
### Linux
|
||||||
|
|
||||||
|
Depending on your Linux distribution, you might need to install additional
|
||||||
|
dependencies to run the browsers.
|
||||||
|
|
||||||
|
:::note
|
||||||
|
Only Ubuntu 18.04, 20.04, and 22.04 are officially supported.
|
||||||
|
:::
|
||||||
|
|
||||||
|
See also in the [Command line tools](./cli.md#install-system-dependencies)
|
||||||
|
which has a command to install all necessary dependencies automatically for Ubuntu
|
||||||
|
LTS releases.
|
||||||
|
|
||||||
Playwright is a new project, and we are watching the issues very closely. As we solve common issues, this document will grow to include the common answers.
|
|
||||||
240
docs/src/writing-tests-csharp.md
Normal file
240
docs/src/writing-tests-csharp.md
Normal file
|
|
@ -0,0 +1,240 @@
|
||||||
|
---
|
||||||
|
id: writing-tests
|
||||||
|
title: "Writing Tests"
|
||||||
|
---
|
||||||
|
|
||||||
|
Playwright assertions are created specifically for the dynamic web. Checks are automatically retried until the necessary conditions are met. Playwright comes with [auto-wait](./actionability.md) built in meaning it waits for elements to be actionable prior to performing actions. Playwright provides the [Expect](./test-assertions) function to write assertions.
|
||||||
|
|
||||||
|
Take a look at the example test below to see how to write a test using web first assertions, locators and selectors.
|
||||||
|
|
||||||
|
<Tabs
|
||||||
|
groupId="test-runners"
|
||||||
|
defaultValue="nunit"
|
||||||
|
values={[
|
||||||
|
{label: 'NUnit', value: 'nunit'},
|
||||||
|
{label: 'MSTest', value: 'mstest'}
|
||||||
|
]
|
||||||
|
}>
|
||||||
|
<TabItem value="nunit">
|
||||||
|
|
||||||
|
```csharp
|
||||||
|
using System.Text.RegularExpressions;
|
||||||
|
using Microsoft.Playwright.NUnit;
|
||||||
|
|
||||||
|
namespace PlaywrightTests;
|
||||||
|
|
||||||
|
[Parallelizable(ParallelScope.Self)]
|
||||||
|
public class Tests : PageTest
|
||||||
|
{
|
||||||
|
[Test]
|
||||||
|
async public Task HomepageHasPlaywrightInTitleAndGetStartedLinkLinkingtoTheIntroPage()
|
||||||
|
{
|
||||||
|
await Page.GotoAsync("https://playwright.dev");
|
||||||
|
|
||||||
|
// Expect a title "to contain" a substring.
|
||||||
|
await Expect(Page).ToHaveTitleAsync(new Regex("Playwright"));
|
||||||
|
|
||||||
|
// create a locator
|
||||||
|
var getStarted = Page.Locator("text=Get Started");
|
||||||
|
|
||||||
|
// Expect an attribute "to be strictly equal" to the value.
|
||||||
|
await Expect(getStarted).ToHaveAttributeAsync("href", "/docs/intro");
|
||||||
|
|
||||||
|
// Click the get started link.
|
||||||
|
await getStarted.ClickAsync();
|
||||||
|
|
||||||
|
// Expects the URL to contain intro.
|
||||||
|
await Expect(Page).ToHaveURLAsync(new Regex(".*intro"));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
</TabItem>
|
||||||
|
<TabItem value="mstest">
|
||||||
|
|
||||||
|
```csharp
|
||||||
|
using System.Text.RegularExpressions;
|
||||||
|
using Microsoft.Playwright.MSTest;
|
||||||
|
|
||||||
|
namespace PlaywrightTests;
|
||||||
|
|
||||||
|
public class UnitTest1 : PageTest
|
||||||
|
{
|
||||||
|
[TestMethod]
|
||||||
|
async public Task HomepageHasPlaywrightInTitleAndGetStartedLinkLinkingtoTheIntroPage()
|
||||||
|
{
|
||||||
|
await Page.GotoAsync("https://playwright.dev");
|
||||||
|
|
||||||
|
// Expect a title "to contain" a substring.
|
||||||
|
await Expect(Page).ToHaveTitleAsync(new Regex("Playwright"));
|
||||||
|
|
||||||
|
// create a locator
|
||||||
|
var getStarted = Page.Locator("text=Get Started");
|
||||||
|
|
||||||
|
// Expect an attribute "to be strictly equal" to the value.
|
||||||
|
await Expect(getStarted).ToHaveAttributeAsync("href", "/docs/intro");
|
||||||
|
|
||||||
|
// Click the get started link.
|
||||||
|
await getStarted.ClickAsync();
|
||||||
|
|
||||||
|
// Expects the URL to contain intro.
|
||||||
|
await Expect(Page).ToHaveURLAsync(new Regex(".*intro"));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
</TabItem>
|
||||||
|
</Tabs>
|
||||||
|
|
||||||
|
### Assertions
|
||||||
|
|
||||||
|
Playwright provides an async function called [Expect](./test-assertions) to assert and wait until the expected condition is met.
|
||||||
|
|
||||||
|
```csharp
|
||||||
|
await Expect(Page).ToHaveTitleAsync(new Regex("Playwright"));
|
||||||
|
```
|
||||||
|
|
||||||
|
|
||||||
|
### Locators
|
||||||
|
|
||||||
|
[Locators](./locators.md) are the central piece of Playwright's auto-waiting and retry-ability. Locators represent a way to find element(s) on the page at any moment and are used to perform actions on elements such as `.ClickAsync` `.FillAsync` etc. Custom locators can be created with the [`method: Page.locator`] method.
|
||||||
|
|
||||||
|
```csharp
|
||||||
|
var getStarted = Page.Locator("text=Get Started");
|
||||||
|
|
||||||
|
await Expect(getStarted).ToHaveAttributeAsync("href", "/docs/installation");
|
||||||
|
await getStarted.ClickAsync();
|
||||||
|
```
|
||||||
|
|
||||||
|
[Selectors](./selectors.md) are strings that are used to create Locators. Playwright supports many different selectors like [Text](./selectors.md#text-selector), [CSS](./selectors.md#css-selector), [XPath](./selectors.md#xpath-selectors) and many more. Learn more about available selectors and how to pick one in this [in-depth guide](./selectors.md).
|
||||||
|
|
||||||
|
```csharp
|
||||||
|
await Expect(Page.Locator("text=Installation")).ToBeVisibleAsync();
|
||||||
|
```
|
||||||
|
|
||||||
|
|
||||||
|
### Test Isolation
|
||||||
|
|
||||||
|
The Playwright NUnit and MSTest test framework base classes will isolate each test from each other by providing a separate `Page` instance. Pages are isolated between tests due to the Browser Context, which is equivalent to a brand new browser profile, where every test gets a fresh environment, even when multiple tests run in a single Browser.
|
||||||
|
|
||||||
|
<Tabs
|
||||||
|
groupId="test-runners"
|
||||||
|
defaultValue="nunit"
|
||||||
|
values={[
|
||||||
|
{label: 'NUnit', value: 'nunit'},
|
||||||
|
{label: 'MSTest', value: 'mstest'}
|
||||||
|
]
|
||||||
|
}>
|
||||||
|
<TabItem value="nunit">
|
||||||
|
|
||||||
|
```csharp
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
using Microsoft.Playwright.NUnit;
|
||||||
|
using NUnit.Framework;
|
||||||
|
|
||||||
|
namespace PlaywrightTests;
|
||||||
|
|
||||||
|
[Parallelizable(ParallelScope.Self)]
|
||||||
|
public class Tests : PageTest
|
||||||
|
{
|
||||||
|
[Test]
|
||||||
|
public async Task BasicTest()
|
||||||
|
{
|
||||||
|
await Page.GotoAsync("https://playwright.dev");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
</TabItem>
|
||||||
|
<TabItem value="mstest">
|
||||||
|
|
||||||
|
```csharp
|
||||||
|
using Microsoft.Playwright.MSTest;
|
||||||
|
|
||||||
|
namespace PlaywrightTests;
|
||||||
|
|
||||||
|
public class UnitTest1 : PageTest
|
||||||
|
{
|
||||||
|
[TestMethod]
|
||||||
|
public async Task BasicTest()
|
||||||
|
{
|
||||||
|
await Page.GotoAsync("https://playwright.dev");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
</TabItem>
|
||||||
|
</Tabs>
|
||||||
|
|
||||||
|
### Using Test Hooks
|
||||||
|
|
||||||
|
You can use `SetUp`/`TearDown` in NUnit or `TestInitialize`/`TestCleanup` in MSTest to prepare and clean up your test environment:
|
||||||
|
|
||||||
|
<Tabs
|
||||||
|
groupId="test-runners"
|
||||||
|
defaultValue="nunit"
|
||||||
|
values={[
|
||||||
|
{label: 'NUnit', value: 'nunit'},
|
||||||
|
{label: 'MSTest', value: 'mstest'}
|
||||||
|
]
|
||||||
|
}>
|
||||||
|
<TabItem value="nunit">
|
||||||
|
|
||||||
|
```csharp
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
using Microsoft.Playwright.NUnit;
|
||||||
|
using NUnit.Framework;
|
||||||
|
|
||||||
|
namespace PlaywrightTests;
|
||||||
|
|
||||||
|
[Parallelizable(ParallelScope.Self)]
|
||||||
|
public class Tests : PageTest
|
||||||
|
{
|
||||||
|
[Test]
|
||||||
|
public async Task MainNavigation()
|
||||||
|
{
|
||||||
|
// Assertions use the expect API.
|
||||||
|
await Expect(Page).ToHaveURLAsync("https://playwright.dev/");
|
||||||
|
}
|
||||||
|
|
||||||
|
[SetUp]
|
||||||
|
public async Task SetUp()
|
||||||
|
{
|
||||||
|
await Page.GotoAsync("https://playwright.dev");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
</TabItem>
|
||||||
|
<TabItem value="mstest">
|
||||||
|
|
||||||
|
```csharp
|
||||||
|
using Microsoft.Playwright.MSTest;
|
||||||
|
|
||||||
|
namespace PlaywrightTests;
|
||||||
|
|
||||||
|
public class UnitTest1 : PageTest
|
||||||
|
{
|
||||||
|
[TestMethod]
|
||||||
|
public async Task MainNavigation()
|
||||||
|
{
|
||||||
|
// Assertions use the expect API.
|
||||||
|
await Expect(Page).ToHaveURLAsync("https://playwright.dev/");
|
||||||
|
}
|
||||||
|
|
||||||
|
[TestInitialize]
|
||||||
|
public async Task TestInitialize()
|
||||||
|
{
|
||||||
|
await Page.GotoAsync("https://playwright.dev");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
</TabItem>
|
||||||
|
</Tabs>
|
||||||
|
|
||||||
|
## What's Next
|
||||||
|
|
||||||
|
- [Run single tests, multiple tests, headed mode](./running-tests.md)
|
||||||
|
- [Generate tests with Codegen](./codegen.md)
|
||||||
|
- [See a trace of your tests](./trace-viewer.md)
|
||||||
|
|
@ -3,7 +3,7 @@ id: writing-tests
|
||||||
title: "Writing Tests"
|
title: "Writing Tests"
|
||||||
---
|
---
|
||||||
|
|
||||||
Playwright assertions are created specifically for the dynamic web. Checks are automatically retried until the necessary conditions are met. Playwright comes with auto-wait built in meaning it waits for elements to be actionable prior to performing actions. Playwright provides a [test](./api/class-test.md) function to declare tests and the [expect](https://jestjs.io/docs/expect) function to write assertions.
|
Playwright assertions are created specifically for the dynamic web. Checks are automatically retried until the necessary conditions are met. Playwright comes with [auto-wait](./actionability.md) built in meaning it waits for elements to be actionable prior to performing actions. Playwright provides a [test](./api/class-test.md) function to declare tests and the [expect](https://jestjs.io/docs/expect) function to write assertions.
|
||||||
|
|
||||||
Take a look at the example test included when installing Playwright to see how to write a test using web first assertions, locators and selectors.
|
Take a look at the example test included when installing Playwright to see how to write a test using web first assertions, locators and selectors.
|
||||||
|
|
||||||
|
|
@ -69,7 +69,7 @@ await expect(page).toHaveTitle(/Playwright/);
|
||||||
|
|
||||||
### Locators
|
### Locators
|
||||||
|
|
||||||
[Locators](./locators.md) are the central piece of Playwright's auto-waiting and retry-ability. Locators represent a way to find element(s) on the page at any moment and are used to perform actions on elements such as .click. fill etc. Custom locators can be created with the [`method: Page.locator`] method.
|
[Locators](./locators.md) are the central piece of Playwright's auto-waiting and retry-ability. Locators represent a way to find element(s) on the page at any moment and are used to perform actions on elements such as `.click` `.fill` etc. Custom locators can be created with the [`method: Page.locator`] method.
|
||||||
|
|
||||||
```js
|
```js
|
||||||
const getStarted = page.locator('text=Get Started');
|
const getStarted = page.locator('text=Get Started');
|
||||||
|
|
@ -78,7 +78,8 @@ await expect(getStarted).toHaveAttribute('href', '/docs/installation');
|
||||||
await getStarted.click();
|
await getStarted.click();
|
||||||
```
|
```
|
||||||
|
|
||||||
[Selectors](./selectors.md) are strings that are used to create Locators. Playwright supports many different selectors like [Text](./selectors.md#text-selector), [CSS](./selectors.md#css-selector), [XPath](./selectors.md#xpath-selectors) and many more.
|
[Selectors](./selectors.md) are strings that are used to create Locators. Playwright supports many different selectors like [Text](./selectors.md#text-selector), [CSS](./selectors.md#css-selector), [XPath](./selectors.md#xpath-selectors) and many more. Learn more about available selectors and how to pick one in this [in-depth guide](./selectors.md).
|
||||||
|
|
||||||
|
|
||||||
```js
|
```js
|
||||||
await expect(page.locator('text=Installation')).toBeVisible();
|
await expect(page.locator('text=Installation')).toBeVisible();
|
||||||
|
|
@ -134,6 +135,5 @@ test.describe("navigation", () => {
|
||||||
## What's Next
|
## What's Next
|
||||||
|
|
||||||
- [Run single tests, multiple tests, headed mode](./running-tests.md)
|
- [Run single tests, multiple tests, headed mode](./running-tests.md)
|
||||||
- [Debug tests with the Playwright Debugger](./debug.md)
|
|
||||||
- [Generate tests with Codegen](./codegen.md)
|
- [Generate tests with Codegen](./codegen.md)
|
||||||
- [See a trace of your tests](./trace-viewer.md)
|
- [See a trace of your tests](./trace-viewer.md)
|
||||||
107
docs/src/writing-tests-python.md
Normal file
107
docs/src/writing-tests-python.md
Normal file
|
|
@ -0,0 +1,107 @@
|
||||||
|
---
|
||||||
|
id: writing-tests
|
||||||
|
title: "Writing Tests"
|
||||||
|
---
|
||||||
|
|
||||||
|
Playwright assertions are created specifically for the dynamic web. Checks are automatically retried until the necessary conditions are met. Playwright comes with [auto-wait](./actionability.md) built in meaning it waits for elements to be actionable prior to performing actions. Playwright provides an [expect](./test-assertions.md) function to write assertions.
|
||||||
|
|
||||||
|
Take a look at the example test below to see how to write a test using web first assertions, locators and selectors.
|
||||||
|
|
||||||
|
```python
|
||||||
|
import re
|
||||||
|
from playwright.sync_api import Page, expect
|
||||||
|
|
||||||
|
|
||||||
|
def test_homepage_has_Playwright_in_title_and_get_started_link_linking_to_the_intro_page(page: Page):
|
||||||
|
page.goto("https://playwright.dev/")
|
||||||
|
|
||||||
|
# Expect a title "to contain" a substring.
|
||||||
|
expect(page).to_have_title(re.compile("Playwright"))
|
||||||
|
|
||||||
|
# create a locator
|
||||||
|
get_started = page.locator("text=Get Started")
|
||||||
|
|
||||||
|
# Expect an attribute "to be strictly equal" to the value.
|
||||||
|
expect(get_started).to_have_attribute("href", "/docs/intro")
|
||||||
|
|
||||||
|
# Click the get started link.
|
||||||
|
get_started.click()
|
||||||
|
|
||||||
|
# Expects the URL to contain intro.
|
||||||
|
expect(page).to_have_url(re.compile(".*intro"))
|
||||||
|
```
|
||||||
|
|
||||||
|
|
||||||
|
### Assertions
|
||||||
|
|
||||||
|
Playwright provides the [`expect`](./test-assertions.md) function which will wait until the expected condition is met.
|
||||||
|
|
||||||
|
```python
|
||||||
|
import re
|
||||||
|
from playwright.sync_api import expect
|
||||||
|
|
||||||
|
expect(page).to_have_title(re.compile("Playwright"))
|
||||||
|
```
|
||||||
|
|
||||||
|
|
||||||
|
### Locators
|
||||||
|
|
||||||
|
[Locators](./locators.md) are the central piece of Playwright's auto-waiting and retry-ability. Locators represent a way to find element(s) on the page at any moment and are used to perform actions on elements such as `.click` `.fill` etc. Custom locators can be created with the [`method: Page.locator`] method.
|
||||||
|
|
||||||
|
```python
|
||||||
|
from playwright.sync_api import expect
|
||||||
|
|
||||||
|
get_started = page.locator("text=Get Started")
|
||||||
|
|
||||||
|
expect(get_started).to_have_attribute("href", "/docs/installation")
|
||||||
|
get_started.click()
|
||||||
|
```
|
||||||
|
|
||||||
|
[Selectors](./selectors.md) are strings that are used to create Locators. Playwright supports many different selectors like [Text](./selectors.md#text-selector), [CSS](./selectors.md#css-selector), [XPath](./selectors.md#xpath-selectors) and many more. Learn more about available selectors and how to pick one in this [in-depth guide](./selectors.md).
|
||||||
|
|
||||||
|
|
||||||
|
```python
|
||||||
|
from playwright.sync_api import expect
|
||||||
|
|
||||||
|
expect(page.locator("text=Installation")).to_be_visible()
|
||||||
|
```
|
||||||
|
|
||||||
|
|
||||||
|
### Test Isolation
|
||||||
|
|
||||||
|
The Playwright Pytest plugin is based on the concept of test fixtures such as the [built in page fixture](./test-runners.md), which is passed into your test. Pages are isolated between tests due to the Browser Context, which is equivalent to a brand new browser profile, where every test gets a fresh environment, even when multiple tests run in a single Browser.
|
||||||
|
|
||||||
|
```python
|
||||||
|
from playwright.sync_api import Page
|
||||||
|
|
||||||
|
def test_basic_test(page: Page):
|
||||||
|
# ...
|
||||||
|
```
|
||||||
|
|
||||||
|
### Using Test Hooks
|
||||||
|
|
||||||
|
You can use various [fixtures](https://docs.pytest.org/en/6.2.x/fixture.html#autouse-fixtures-fixtures-you-don-t-have-to-request) to execute code before or after your tests and to share objects between them. A `function` scoped fixture e.g. with autouse behaves like a beforeEach/afterEach. And a `module` scoped fixture with autouse behaves like a beforeAll/afterAll which runs before all and after all the tests.
|
||||||
|
|
||||||
|
```python
|
||||||
|
import pytest
|
||||||
|
from playwright.sync_api import Page
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.fixture(scope="function", autouse=True)
|
||||||
|
def before_each_after_each(page: Page):
|
||||||
|
print("beforeEach")
|
||||||
|
# Go to the starting url before each test.
|
||||||
|
page.goto("https://playwright.dev/")
|
||||||
|
yield
|
||||||
|
print("afterEach")
|
||||||
|
|
||||||
|
def test_main_navigation(page: Page):
|
||||||
|
# Assertions use the expect API.
|
||||||
|
expect(page).to_have_url("https://playwright.dev/")
|
||||||
|
```
|
||||||
|
|
||||||
|
## What's Next
|
||||||
|
|
||||||
|
- [Run single tests, multiple tests, headed mode](./running-tests.md)
|
||||||
|
- [Generate tests with Codegen](./codegen.md)
|
||||||
|
- [See a trace of your tests](./trace-viewer.md)
|
||||||
757
package-lock.json
generated
757
package-lock.json
generated
File diff suppressed because it is too large
Load diff
|
|
@ -1,7 +1,7 @@
|
||||||
{
|
{
|
||||||
"name": "playwright-internal",
|
"name": "playwright-internal",
|
||||||
"private": true,
|
"private": true,
|
||||||
"version": "1.24.0-next",
|
"version": "1.24.2",
|
||||||
"description": "A high-level API to automate web browsers",
|
"description": "A high-level API to automate web browsers",
|
||||||
"repository": "github:Microsoft/playwright",
|
"repository": "github:Microsoft/playwright",
|
||||||
"homepage": "https://playwright.dev",
|
"homepage": "https://playwright.dev",
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,6 @@
|
||||||
{
|
{
|
||||||
"name": "playwright-chromium",
|
"name": "playwright-chromium",
|
||||||
"version": "1.24.0-next",
|
"version": "1.24.2",
|
||||||
"description": "A high-level API to automate Chromium",
|
"description": "A high-level API to automate Chromium",
|
||||||
"repository": "github:Microsoft/playwright",
|
"repository": "github:Microsoft/playwright",
|
||||||
"homepage": "https://playwright.dev",
|
"homepage": "https://playwright.dev",
|
||||||
|
|
@ -27,6 +27,6 @@
|
||||||
"install": "node install.js"
|
"install": "node install.js"
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"playwright-core": "1.24.0-next"
|
"playwright-core": "1.24.2"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -33,13 +33,13 @@
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"name": "webkit",
|
"name": "webkit",
|
||||||
"revision": "1681",
|
"revision": "1683",
|
||||||
"installByDefault": true,
|
"installByDefault": true,
|
||||||
"revisionOverrides": {
|
"revisionOverrides": {
|
||||||
"mac10.14": "1446",
|
"mac10.14": "1446",
|
||||||
"mac10.15": "1616"
|
"mac10.15": "1616"
|
||||||
},
|
},
|
||||||
"browserVersion": "15.4"
|
"browserVersion": "16.0"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"name": "ffmpeg",
|
"name": "ffmpeg",
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,6 @@
|
||||||
{
|
{
|
||||||
"name": "playwright-core",
|
"name": "playwright-core",
|
||||||
"version": "1.24.0-next",
|
"version": "1.24.2",
|
||||||
"description": "A high-level API to automate web browsers",
|
"description": "A high-level API to automate web browsers",
|
||||||
"repository": "github:Microsoft/playwright",
|
"repository": "github:Microsoft/playwright",
|
||||||
"homepage": "https://playwright.dev",
|
"homepage": "https://playwright.dev",
|
||||||
|
|
|
||||||
|
|
@ -367,7 +367,10 @@ export class CRNetworkManager {
|
||||||
// For frame-level Requests that are handled by a Service Worker's fetch handler, we'll never get a requestPaused event, so we need to
|
// For frame-level Requests that are handled by a Service Worker's fetch handler, we'll never get a requestPaused event, so we need to
|
||||||
// manually create the request. In an ideal world, crNetworkManager would be able to know this on Network.requestWillBeSent, but there
|
// manually create the request. In an ideal world, crNetworkManager would be able to know this on Network.requestWillBeSent, but there
|
||||||
// is not enough metadata there.
|
// is not enough metadata there.
|
||||||
if (!request && event.response.fromServiceWorker) {
|
//
|
||||||
|
// PW_EXPERIMENTAL_SERVICE_WORKER_NETWORK_EVENTS we guard with, since this would fix an old bug where, when using routing,
|
||||||
|
// request would not be emitted to the user for requests made by a page with a SW (and fetch handler) registered
|
||||||
|
if (!!process.env.PW_EXPERIMENTAL_SERVICE_WORKER_NETWORK_EVENTS && !request && event.response.fromServiceWorker) {
|
||||||
const requestWillBeSentEvent = this._requestIdToRequestWillBeSentEvent.get(event.requestId);
|
const requestWillBeSentEvent = this._requestIdToRequestWillBeSentEvent.get(event.requestId);
|
||||||
const frame = requestWillBeSentEvent?.frameId ? this._page?._frameManager.frame(requestWillBeSentEvent.frameId) : null;
|
const frame = requestWillBeSentEvent?.frameId ? this._page?._frameManager.frame(requestWillBeSentEvent.frameId) : null;
|
||||||
if (requestWillBeSentEvent && frame) {
|
if (requestWillBeSentEvent && frame) {
|
||||||
|
|
|
||||||
|
|
@ -25,7 +25,7 @@ import { headersArrayToObject } from '../../utils';
|
||||||
|
|
||||||
export class CRServiceWorker extends Worker {
|
export class CRServiceWorker extends Worker {
|
||||||
readonly _browserContext: CRBrowserContext;
|
readonly _browserContext: CRBrowserContext;
|
||||||
readonly _networkManager: CRNetworkManager;
|
readonly _networkManager?: CRNetworkManager;
|
||||||
private _session: CRSession;
|
private _session: CRSession;
|
||||||
private _extraHTTPHeaders: types.HeadersArray | null = null;
|
private _extraHTTPHeaders: types.HeadersArray | null = null;
|
||||||
|
|
||||||
|
|
@ -33,12 +33,13 @@ export class CRServiceWorker extends Worker {
|
||||||
super(browserContext, url);
|
super(browserContext, url);
|
||||||
this._session = session;
|
this._session = session;
|
||||||
this._browserContext = browserContext;
|
this._browserContext = browserContext;
|
||||||
|
if (!!process.env.PW_EXPERIMENTAL_SERVICE_WORKER_NETWORK_EVENTS)
|
||||||
this._networkManager = new CRNetworkManager(session, null, this, null);
|
this._networkManager = new CRNetworkManager(session, null, this, null);
|
||||||
session.once('Runtime.executionContextCreated', event => {
|
session.once('Runtime.executionContextCreated', event => {
|
||||||
this._createExecutionContext(new CRExecutionContext(session, event.context));
|
this._createExecutionContext(new CRExecutionContext(session, event.context));
|
||||||
});
|
});
|
||||||
|
|
||||||
if (this._isNetworkInspectionEnabled()) {
|
if (this._networkManager && this._isNetworkInspectionEnabled()) {
|
||||||
this._networkManager.initialize().catch(() => {});
|
this._networkManager.initialize().catch(() => {});
|
||||||
this.updateRequestInterception();
|
this.updateRequestInterception();
|
||||||
this.updateExtraHTTPHeaders(true);
|
this.updateExtraHTTPHeaders(true);
|
||||||
|
|
@ -56,7 +57,7 @@ export class CRServiceWorker extends Worker {
|
||||||
|
|
||||||
const offline = !!this._browserContext._options.offline;
|
const offline = !!this._browserContext._options.offline;
|
||||||
if (!initial || offline)
|
if (!initial || offline)
|
||||||
await this._networkManager.setOffline(offline);
|
await this._networkManager?.setOffline(offline);
|
||||||
}
|
}
|
||||||
|
|
||||||
async updateHttpCredentials(initial: boolean): Promise<void> {
|
async updateHttpCredentials(initial: boolean): Promise<void> {
|
||||||
|
|
@ -65,7 +66,7 @@ export class CRServiceWorker extends Worker {
|
||||||
|
|
||||||
const credentials = this._browserContext._options.httpCredentials || null;
|
const credentials = this._browserContext._options.httpCredentials || null;
|
||||||
if (!initial || credentials)
|
if (!initial || credentials)
|
||||||
await this._networkManager.authenticate(credentials);
|
await this._networkManager?.authenticate(credentials);
|
||||||
}
|
}
|
||||||
|
|
||||||
async updateExtraHTTPHeaders(initial: boolean): Promise<void> {
|
async updateExtraHTTPHeaders(initial: boolean): Promise<void> {
|
||||||
|
|
@ -81,7 +82,7 @@ export class CRServiceWorker extends Worker {
|
||||||
}
|
}
|
||||||
|
|
||||||
updateRequestInterception(): Promise<void> {
|
updateRequestInterception(): Promise<void> {
|
||||||
if (!this._isNetworkInspectionEnabled())
|
if (!this._networkManager || !this._isNetworkInspectionEnabled())
|
||||||
return Promise.resolve();
|
return Promise.resolve();
|
||||||
|
|
||||||
return this._networkManager.setRequestInterception(this.needsRequestInterception()).catch(e => { });
|
return this._networkManager.setRequestInterception(this.needsRequestInterception()).catch(e => { });
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,6 @@
|
||||||
{
|
{
|
||||||
"Blackberry PlayBook": {
|
"Blackberry PlayBook": {
|
||||||
"userAgent": "Mozilla/5.0 (PlayBook; U; RIM Tablet OS 2.1.0; en-US) AppleWebKit/536.2+ (KHTML like Gecko) Version/15.4 Safari/536.2+",
|
"userAgent": "Mozilla/5.0 (PlayBook; U; RIM Tablet OS 2.1.0; en-US) AppleWebKit/536.2+ (KHTML like Gecko) Version/16.0 Safari/536.2+",
|
||||||
"viewport": {
|
"viewport": {
|
||||||
"width": 600,
|
"width": 600,
|
||||||
"height": 1024
|
"height": 1024
|
||||||
|
|
@ -11,7 +11,7 @@
|
||||||
"defaultBrowserType": "webkit"
|
"defaultBrowserType": "webkit"
|
||||||
},
|
},
|
||||||
"Blackberry PlayBook landscape": {
|
"Blackberry PlayBook landscape": {
|
||||||
"userAgent": "Mozilla/5.0 (PlayBook; U; RIM Tablet OS 2.1.0; en-US) AppleWebKit/536.2+ (KHTML like Gecko) Version/15.4 Safari/536.2+",
|
"userAgent": "Mozilla/5.0 (PlayBook; U; RIM Tablet OS 2.1.0; en-US) AppleWebKit/536.2+ (KHTML like Gecko) Version/16.0 Safari/536.2+",
|
||||||
"viewport": {
|
"viewport": {
|
||||||
"width": 1024,
|
"width": 1024,
|
||||||
"height": 600
|
"height": 600
|
||||||
|
|
@ -22,7 +22,7 @@
|
||||||
"defaultBrowserType": "webkit"
|
"defaultBrowserType": "webkit"
|
||||||
},
|
},
|
||||||
"BlackBerry Z30": {
|
"BlackBerry Z30": {
|
||||||
"userAgent": "Mozilla/5.0 (BB10; Touch) AppleWebKit/537.10+ (KHTML, like Gecko) Version/15.4 Mobile Safari/537.10+",
|
"userAgent": "Mozilla/5.0 (BB10; Touch) AppleWebKit/537.10+ (KHTML, like Gecko) Version/16.0 Mobile Safari/537.10+",
|
||||||
"viewport": {
|
"viewport": {
|
||||||
"width": 360,
|
"width": 360,
|
||||||
"height": 640
|
"height": 640
|
||||||
|
|
@ -33,7 +33,7 @@
|
||||||
"defaultBrowserType": "webkit"
|
"defaultBrowserType": "webkit"
|
||||||
},
|
},
|
||||||
"BlackBerry Z30 landscape": {
|
"BlackBerry Z30 landscape": {
|
||||||
"userAgent": "Mozilla/5.0 (BB10; Touch) AppleWebKit/537.10+ (KHTML, like Gecko) Version/15.4 Mobile Safari/537.10+",
|
"userAgent": "Mozilla/5.0 (BB10; Touch) AppleWebKit/537.10+ (KHTML, like Gecko) Version/16.0 Mobile Safari/537.10+",
|
||||||
"viewport": {
|
"viewport": {
|
||||||
"width": 640,
|
"width": 640,
|
||||||
"height": 360
|
"height": 360
|
||||||
|
|
@ -44,7 +44,7 @@
|
||||||
"defaultBrowserType": "webkit"
|
"defaultBrowserType": "webkit"
|
||||||
},
|
},
|
||||||
"Galaxy Note 3": {
|
"Galaxy Note 3": {
|
||||||
"userAgent": "Mozilla/5.0 (Linux; U; Android 4.3; en-us; SM-N900T Build/JSS15J) AppleWebKit/534.30 (KHTML, like Gecko) Version/15.4 Mobile Safari/534.30",
|
"userAgent": "Mozilla/5.0 (Linux; U; Android 4.3; en-us; SM-N900T Build/JSS15J) AppleWebKit/534.30 (KHTML, like Gecko) Version/16.0 Mobile Safari/534.30",
|
||||||
"viewport": {
|
"viewport": {
|
||||||
"width": 360,
|
"width": 360,
|
||||||
"height": 640
|
"height": 640
|
||||||
|
|
@ -55,7 +55,7 @@
|
||||||
"defaultBrowserType": "webkit"
|
"defaultBrowserType": "webkit"
|
||||||
},
|
},
|
||||||
"Galaxy Note 3 landscape": {
|
"Galaxy Note 3 landscape": {
|
||||||
"userAgent": "Mozilla/5.0 (Linux; U; Android 4.3; en-us; SM-N900T Build/JSS15J) AppleWebKit/534.30 (KHTML, like Gecko) Version/15.4 Mobile Safari/534.30",
|
"userAgent": "Mozilla/5.0 (Linux; U; Android 4.3; en-us; SM-N900T Build/JSS15J) AppleWebKit/534.30 (KHTML, like Gecko) Version/16.0 Mobile Safari/534.30",
|
||||||
"viewport": {
|
"viewport": {
|
||||||
"width": 640,
|
"width": 640,
|
||||||
"height": 360
|
"height": 360
|
||||||
|
|
@ -66,7 +66,7 @@
|
||||||
"defaultBrowserType": "webkit"
|
"defaultBrowserType": "webkit"
|
||||||
},
|
},
|
||||||
"Galaxy Note II": {
|
"Galaxy Note II": {
|
||||||
"userAgent": "Mozilla/5.0 (Linux; U; Android 4.1; en-us; GT-N7100 Build/JRO03C) AppleWebKit/534.30 (KHTML, like Gecko) Version/15.4 Mobile Safari/534.30",
|
"userAgent": "Mozilla/5.0 (Linux; U; Android 4.1; en-us; GT-N7100 Build/JRO03C) AppleWebKit/534.30 (KHTML, like Gecko) Version/16.0 Mobile Safari/534.30",
|
||||||
"viewport": {
|
"viewport": {
|
||||||
"width": 360,
|
"width": 360,
|
||||||
"height": 640
|
"height": 640
|
||||||
|
|
@ -77,7 +77,7 @@
|
||||||
"defaultBrowserType": "webkit"
|
"defaultBrowserType": "webkit"
|
||||||
},
|
},
|
||||||
"Galaxy Note II landscape": {
|
"Galaxy Note II landscape": {
|
||||||
"userAgent": "Mozilla/5.0 (Linux; U; Android 4.1; en-us; GT-N7100 Build/JRO03C) AppleWebKit/534.30 (KHTML, like Gecko) Version/15.4 Mobile Safari/534.30",
|
"userAgent": "Mozilla/5.0 (Linux; U; Android 4.1; en-us; GT-N7100 Build/JRO03C) AppleWebKit/534.30 (KHTML, like Gecko) Version/16.0 Mobile Safari/534.30",
|
||||||
"viewport": {
|
"viewport": {
|
||||||
"width": 640,
|
"width": 640,
|
||||||
"height": 360
|
"height": 360
|
||||||
|
|
@ -88,7 +88,7 @@
|
||||||
"defaultBrowserType": "webkit"
|
"defaultBrowserType": "webkit"
|
||||||
},
|
},
|
||||||
"Galaxy S III": {
|
"Galaxy S III": {
|
||||||
"userAgent": "Mozilla/5.0 (Linux; U; Android 4.0; en-us; GT-I9300 Build/IMM76D) AppleWebKit/534.30 (KHTML, like Gecko) Version/15.4 Mobile Safari/534.30",
|
"userAgent": "Mozilla/5.0 (Linux; U; Android 4.0; en-us; GT-I9300 Build/IMM76D) AppleWebKit/534.30 (KHTML, like Gecko) Version/16.0 Mobile Safari/534.30",
|
||||||
"viewport": {
|
"viewport": {
|
||||||
"width": 360,
|
"width": 360,
|
||||||
"height": 640
|
"height": 640
|
||||||
|
|
@ -99,7 +99,7 @@
|
||||||
"defaultBrowserType": "webkit"
|
"defaultBrowserType": "webkit"
|
||||||
},
|
},
|
||||||
"Galaxy S III landscape": {
|
"Galaxy S III landscape": {
|
||||||
"userAgent": "Mozilla/5.0 (Linux; U; Android 4.0; en-us; GT-I9300 Build/IMM76D) AppleWebKit/534.30 (KHTML, like Gecko) Version/15.4 Mobile Safari/534.30",
|
"userAgent": "Mozilla/5.0 (Linux; U; Android 4.0; en-us; GT-I9300 Build/IMM76D) AppleWebKit/534.30 (KHTML, like Gecko) Version/16.0 Mobile Safari/534.30",
|
||||||
"viewport": {
|
"viewport": {
|
||||||
"width": 640,
|
"width": 640,
|
||||||
"height": 360
|
"height": 360
|
||||||
|
|
@ -198,7 +198,7 @@
|
||||||
"defaultBrowserType": "chromium"
|
"defaultBrowserType": "chromium"
|
||||||
},
|
},
|
||||||
"iPad (gen 6)": {
|
"iPad (gen 6)": {
|
||||||
"userAgent": "Mozilla/5.0 (iPad; CPU OS 12_2 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/15.4 Mobile/15E148 Safari/604.1",
|
"userAgent": "Mozilla/5.0 (iPad; CPU OS 12_2 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/16.0 Mobile/15E148 Safari/604.1",
|
||||||
"viewport": {
|
"viewport": {
|
||||||
"width": 768,
|
"width": 768,
|
||||||
"height": 1024
|
"height": 1024
|
||||||
|
|
@ -209,7 +209,7 @@
|
||||||
"defaultBrowserType": "webkit"
|
"defaultBrowserType": "webkit"
|
||||||
},
|
},
|
||||||
"iPad (gen 6) landscape": {
|
"iPad (gen 6) landscape": {
|
||||||
"userAgent": "Mozilla/5.0 (iPad; CPU OS 12_2 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/15.4 Mobile/15E148 Safari/604.1",
|
"userAgent": "Mozilla/5.0 (iPad; CPU OS 12_2 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/16.0 Mobile/15E148 Safari/604.1",
|
||||||
"viewport": {
|
"viewport": {
|
||||||
"width": 1024,
|
"width": 1024,
|
||||||
"height": 768
|
"height": 768
|
||||||
|
|
@ -220,7 +220,7 @@
|
||||||
"defaultBrowserType": "webkit"
|
"defaultBrowserType": "webkit"
|
||||||
},
|
},
|
||||||
"iPad (gen 7)": {
|
"iPad (gen 7)": {
|
||||||
"userAgent": "Mozilla/5.0 (iPad; CPU OS 12_2 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/15.4 Mobile/15E148 Safari/604.1",
|
"userAgent": "Mozilla/5.0 (iPad; CPU OS 12_2 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/16.0 Mobile/15E148 Safari/604.1",
|
||||||
"viewport": {
|
"viewport": {
|
||||||
"width": 810,
|
"width": 810,
|
||||||
"height": 1080
|
"height": 1080
|
||||||
|
|
@ -231,7 +231,7 @@
|
||||||
"defaultBrowserType": "webkit"
|
"defaultBrowserType": "webkit"
|
||||||
},
|
},
|
||||||
"iPad (gen 7) landscape": {
|
"iPad (gen 7) landscape": {
|
||||||
"userAgent": "Mozilla/5.0 (iPad; CPU OS 12_2 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/15.4 Mobile/15E148 Safari/604.1",
|
"userAgent": "Mozilla/5.0 (iPad; CPU OS 12_2 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/16.0 Mobile/15E148 Safari/604.1",
|
||||||
"viewport": {
|
"viewport": {
|
||||||
"width": 1080,
|
"width": 1080,
|
||||||
"height": 810
|
"height": 810
|
||||||
|
|
@ -242,7 +242,7 @@
|
||||||
"defaultBrowserType": "webkit"
|
"defaultBrowserType": "webkit"
|
||||||
},
|
},
|
||||||
"iPad Mini": {
|
"iPad Mini": {
|
||||||
"userAgent": "Mozilla/5.0 (iPad; CPU OS 12_2 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/15.4 Mobile/15E148 Safari/604.1",
|
"userAgent": "Mozilla/5.0 (iPad; CPU OS 12_2 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/16.0 Mobile/15E148 Safari/604.1",
|
||||||
"viewport": {
|
"viewport": {
|
||||||
"width": 768,
|
"width": 768,
|
||||||
"height": 1024
|
"height": 1024
|
||||||
|
|
@ -253,7 +253,7 @@
|
||||||
"defaultBrowserType": "webkit"
|
"defaultBrowserType": "webkit"
|
||||||
},
|
},
|
||||||
"iPad Mini landscape": {
|
"iPad Mini landscape": {
|
||||||
"userAgent": "Mozilla/5.0 (iPad; CPU OS 12_2 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/15.4 Mobile/15E148 Safari/604.1",
|
"userAgent": "Mozilla/5.0 (iPad; CPU OS 12_2 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/16.0 Mobile/15E148 Safari/604.1",
|
||||||
"viewport": {
|
"viewport": {
|
||||||
"width": 1024,
|
"width": 1024,
|
||||||
"height": 768
|
"height": 768
|
||||||
|
|
@ -264,7 +264,7 @@
|
||||||
"defaultBrowserType": "webkit"
|
"defaultBrowserType": "webkit"
|
||||||
},
|
},
|
||||||
"iPad Pro 11": {
|
"iPad Pro 11": {
|
||||||
"userAgent": "Mozilla/5.0 (iPad; CPU OS 12_2 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/15.4 Mobile/15E148 Safari/604.1",
|
"userAgent": "Mozilla/5.0 (iPad; CPU OS 12_2 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/16.0 Mobile/15E148 Safari/604.1",
|
||||||
"viewport": {
|
"viewport": {
|
||||||
"width": 834,
|
"width": 834,
|
||||||
"height": 1194
|
"height": 1194
|
||||||
|
|
@ -275,7 +275,7 @@
|
||||||
"defaultBrowserType": "webkit"
|
"defaultBrowserType": "webkit"
|
||||||
},
|
},
|
||||||
"iPad Pro 11 landscape": {
|
"iPad Pro 11 landscape": {
|
||||||
"userAgent": "Mozilla/5.0 (iPad; CPU OS 12_2 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/15.4 Mobile/15E148 Safari/604.1",
|
"userAgent": "Mozilla/5.0 (iPad; CPU OS 12_2 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/16.0 Mobile/15E148 Safari/604.1",
|
||||||
"viewport": {
|
"viewport": {
|
||||||
"width": 1194,
|
"width": 1194,
|
||||||
"height": 834
|
"height": 834
|
||||||
|
|
@ -286,7 +286,7 @@
|
||||||
"defaultBrowserType": "webkit"
|
"defaultBrowserType": "webkit"
|
||||||
},
|
},
|
||||||
"iPhone 6": {
|
"iPhone 6": {
|
||||||
"userAgent": "Mozilla/5.0 (iPhone; CPU iPhone OS 11_0 like Mac OS X) AppleWebKit/604.1.38 (KHTML, like Gecko) Version/15.4 Mobile/15A372 Safari/604.1",
|
"userAgent": "Mozilla/5.0 (iPhone; CPU iPhone OS 11_0 like Mac OS X) AppleWebKit/604.1.38 (KHTML, like Gecko) Version/16.0 Mobile/15A372 Safari/604.1",
|
||||||
"viewport": {
|
"viewport": {
|
||||||
"width": 375,
|
"width": 375,
|
||||||
"height": 667
|
"height": 667
|
||||||
|
|
@ -297,7 +297,7 @@
|
||||||
"defaultBrowserType": "webkit"
|
"defaultBrowserType": "webkit"
|
||||||
},
|
},
|
||||||
"iPhone 6 landscape": {
|
"iPhone 6 landscape": {
|
||||||
"userAgent": "Mozilla/5.0 (iPhone; CPU iPhone OS 11_0 like Mac OS X) AppleWebKit/604.1.38 (KHTML, like Gecko) Version/15.4 Mobile/15A372 Safari/604.1",
|
"userAgent": "Mozilla/5.0 (iPhone; CPU iPhone OS 11_0 like Mac OS X) AppleWebKit/604.1.38 (KHTML, like Gecko) Version/16.0 Mobile/15A372 Safari/604.1",
|
||||||
"viewport": {
|
"viewport": {
|
||||||
"width": 667,
|
"width": 667,
|
||||||
"height": 375
|
"height": 375
|
||||||
|
|
@ -308,7 +308,7 @@
|
||||||
"defaultBrowserType": "webkit"
|
"defaultBrowserType": "webkit"
|
||||||
},
|
},
|
||||||
"iPhone 6 Plus": {
|
"iPhone 6 Plus": {
|
||||||
"userAgent": "Mozilla/5.0 (iPhone; CPU iPhone OS 11_0 like Mac OS X) AppleWebKit/604.1.38 (KHTML, like Gecko) Version/15.4 Mobile/15A372 Safari/604.1",
|
"userAgent": "Mozilla/5.0 (iPhone; CPU iPhone OS 11_0 like Mac OS X) AppleWebKit/604.1.38 (KHTML, like Gecko) Version/16.0 Mobile/15A372 Safari/604.1",
|
||||||
"viewport": {
|
"viewport": {
|
||||||
"width": 414,
|
"width": 414,
|
||||||
"height": 736
|
"height": 736
|
||||||
|
|
@ -319,7 +319,7 @@
|
||||||
"defaultBrowserType": "webkit"
|
"defaultBrowserType": "webkit"
|
||||||
},
|
},
|
||||||
"iPhone 6 Plus landscape": {
|
"iPhone 6 Plus landscape": {
|
||||||
"userAgent": "Mozilla/5.0 (iPhone; CPU iPhone OS 11_0 like Mac OS X) AppleWebKit/604.1.38 (KHTML, like Gecko) Version/15.4 Mobile/15A372 Safari/604.1",
|
"userAgent": "Mozilla/5.0 (iPhone; CPU iPhone OS 11_0 like Mac OS X) AppleWebKit/604.1.38 (KHTML, like Gecko) Version/16.0 Mobile/15A372 Safari/604.1",
|
||||||
"viewport": {
|
"viewport": {
|
||||||
"width": 736,
|
"width": 736,
|
||||||
"height": 414
|
"height": 414
|
||||||
|
|
@ -330,7 +330,7 @@
|
||||||
"defaultBrowserType": "webkit"
|
"defaultBrowserType": "webkit"
|
||||||
},
|
},
|
||||||
"iPhone 7": {
|
"iPhone 7": {
|
||||||
"userAgent": "Mozilla/5.0 (iPhone; CPU iPhone OS 11_0 like Mac OS X) AppleWebKit/604.1.38 (KHTML, like Gecko) Version/15.4 Mobile/15A372 Safari/604.1",
|
"userAgent": "Mozilla/5.0 (iPhone; CPU iPhone OS 11_0 like Mac OS X) AppleWebKit/604.1.38 (KHTML, like Gecko) Version/16.0 Mobile/15A372 Safari/604.1",
|
||||||
"viewport": {
|
"viewport": {
|
||||||
"width": 375,
|
"width": 375,
|
||||||
"height": 667
|
"height": 667
|
||||||
|
|
@ -341,7 +341,7 @@
|
||||||
"defaultBrowserType": "webkit"
|
"defaultBrowserType": "webkit"
|
||||||
},
|
},
|
||||||
"iPhone 7 landscape": {
|
"iPhone 7 landscape": {
|
||||||
"userAgent": "Mozilla/5.0 (iPhone; CPU iPhone OS 11_0 like Mac OS X) AppleWebKit/604.1.38 (KHTML, like Gecko) Version/15.4 Mobile/15A372 Safari/604.1",
|
"userAgent": "Mozilla/5.0 (iPhone; CPU iPhone OS 11_0 like Mac OS X) AppleWebKit/604.1.38 (KHTML, like Gecko) Version/16.0 Mobile/15A372 Safari/604.1",
|
||||||
"viewport": {
|
"viewport": {
|
||||||
"width": 667,
|
"width": 667,
|
||||||
"height": 375
|
"height": 375
|
||||||
|
|
@ -352,7 +352,7 @@
|
||||||
"defaultBrowserType": "webkit"
|
"defaultBrowserType": "webkit"
|
||||||
},
|
},
|
||||||
"iPhone 7 Plus": {
|
"iPhone 7 Plus": {
|
||||||
"userAgent": "Mozilla/5.0 (iPhone; CPU iPhone OS 11_0 like Mac OS X) AppleWebKit/604.1.38 (KHTML, like Gecko) Version/15.4 Mobile/15A372 Safari/604.1",
|
"userAgent": "Mozilla/5.0 (iPhone; CPU iPhone OS 11_0 like Mac OS X) AppleWebKit/604.1.38 (KHTML, like Gecko) Version/16.0 Mobile/15A372 Safari/604.1",
|
||||||
"viewport": {
|
"viewport": {
|
||||||
"width": 414,
|
"width": 414,
|
||||||
"height": 736
|
"height": 736
|
||||||
|
|
@ -363,7 +363,7 @@
|
||||||
"defaultBrowserType": "webkit"
|
"defaultBrowserType": "webkit"
|
||||||
},
|
},
|
||||||
"iPhone 7 Plus landscape": {
|
"iPhone 7 Plus landscape": {
|
||||||
"userAgent": "Mozilla/5.0 (iPhone; CPU iPhone OS 11_0 like Mac OS X) AppleWebKit/604.1.38 (KHTML, like Gecko) Version/15.4 Mobile/15A372 Safari/604.1",
|
"userAgent": "Mozilla/5.0 (iPhone; CPU iPhone OS 11_0 like Mac OS X) AppleWebKit/604.1.38 (KHTML, like Gecko) Version/16.0 Mobile/15A372 Safari/604.1",
|
||||||
"viewport": {
|
"viewport": {
|
||||||
"width": 736,
|
"width": 736,
|
||||||
"height": 414
|
"height": 414
|
||||||
|
|
@ -374,7 +374,7 @@
|
||||||
"defaultBrowserType": "webkit"
|
"defaultBrowserType": "webkit"
|
||||||
},
|
},
|
||||||
"iPhone 8": {
|
"iPhone 8": {
|
||||||
"userAgent": "Mozilla/5.0 (iPhone; CPU iPhone OS 11_0 like Mac OS X) AppleWebKit/604.1.38 (KHTML, like Gecko) Version/15.4 Mobile/15A372 Safari/604.1",
|
"userAgent": "Mozilla/5.0 (iPhone; CPU iPhone OS 11_0 like Mac OS X) AppleWebKit/604.1.38 (KHTML, like Gecko) Version/16.0 Mobile/15A372 Safari/604.1",
|
||||||
"viewport": {
|
"viewport": {
|
||||||
"width": 375,
|
"width": 375,
|
||||||
"height": 667
|
"height": 667
|
||||||
|
|
@ -385,7 +385,7 @@
|
||||||
"defaultBrowserType": "webkit"
|
"defaultBrowserType": "webkit"
|
||||||
},
|
},
|
||||||
"iPhone 8 landscape": {
|
"iPhone 8 landscape": {
|
||||||
"userAgent": "Mozilla/5.0 (iPhone; CPU iPhone OS 11_0 like Mac OS X) AppleWebKit/604.1.38 (KHTML, like Gecko) Version/15.4 Mobile/15A372 Safari/604.1",
|
"userAgent": "Mozilla/5.0 (iPhone; CPU iPhone OS 11_0 like Mac OS X) AppleWebKit/604.1.38 (KHTML, like Gecko) Version/16.0 Mobile/15A372 Safari/604.1",
|
||||||
"viewport": {
|
"viewport": {
|
||||||
"width": 667,
|
"width": 667,
|
||||||
"height": 375
|
"height": 375
|
||||||
|
|
@ -396,7 +396,7 @@
|
||||||
"defaultBrowserType": "webkit"
|
"defaultBrowserType": "webkit"
|
||||||
},
|
},
|
||||||
"iPhone 8 Plus": {
|
"iPhone 8 Plus": {
|
||||||
"userAgent": "Mozilla/5.0 (iPhone; CPU iPhone OS 11_0 like Mac OS X) AppleWebKit/604.1.38 (KHTML, like Gecko) Version/15.4 Mobile/15A372 Safari/604.1",
|
"userAgent": "Mozilla/5.0 (iPhone; CPU iPhone OS 11_0 like Mac OS X) AppleWebKit/604.1.38 (KHTML, like Gecko) Version/16.0 Mobile/15A372 Safari/604.1",
|
||||||
"viewport": {
|
"viewport": {
|
||||||
"width": 414,
|
"width": 414,
|
||||||
"height": 736
|
"height": 736
|
||||||
|
|
@ -407,7 +407,7 @@
|
||||||
"defaultBrowserType": "webkit"
|
"defaultBrowserType": "webkit"
|
||||||
},
|
},
|
||||||
"iPhone 8 Plus landscape": {
|
"iPhone 8 Plus landscape": {
|
||||||
"userAgent": "Mozilla/5.0 (iPhone; CPU iPhone OS 11_0 like Mac OS X) AppleWebKit/604.1.38 (KHTML, like Gecko) Version/15.4 Mobile/15A372 Safari/604.1",
|
"userAgent": "Mozilla/5.0 (iPhone; CPU iPhone OS 11_0 like Mac OS X) AppleWebKit/604.1.38 (KHTML, like Gecko) Version/16.0 Mobile/15A372 Safari/604.1",
|
||||||
"viewport": {
|
"viewport": {
|
||||||
"width": 736,
|
"width": 736,
|
||||||
"height": 414
|
"height": 414
|
||||||
|
|
@ -418,7 +418,7 @@
|
||||||
"defaultBrowserType": "webkit"
|
"defaultBrowserType": "webkit"
|
||||||
},
|
},
|
||||||
"iPhone SE": {
|
"iPhone SE": {
|
||||||
"userAgent": "Mozilla/5.0 (iPhone; CPU iPhone OS 10_3_1 like Mac OS X) AppleWebKit/603.1.30 (KHTML, like Gecko) Version/15.4 Mobile/14E304 Safari/602.1",
|
"userAgent": "Mozilla/5.0 (iPhone; CPU iPhone OS 10_3_1 like Mac OS X) AppleWebKit/603.1.30 (KHTML, like Gecko) Version/16.0 Mobile/14E304 Safari/602.1",
|
||||||
"viewport": {
|
"viewport": {
|
||||||
"width": 320,
|
"width": 320,
|
||||||
"height": 568
|
"height": 568
|
||||||
|
|
@ -429,7 +429,7 @@
|
||||||
"defaultBrowserType": "webkit"
|
"defaultBrowserType": "webkit"
|
||||||
},
|
},
|
||||||
"iPhone SE landscape": {
|
"iPhone SE landscape": {
|
||||||
"userAgent": "Mozilla/5.0 (iPhone; CPU iPhone OS 10_3_1 like Mac OS X) AppleWebKit/603.1.30 (KHTML, like Gecko) Version/15.4 Mobile/14E304 Safari/602.1",
|
"userAgent": "Mozilla/5.0 (iPhone; CPU iPhone OS 10_3_1 like Mac OS X) AppleWebKit/603.1.30 (KHTML, like Gecko) Version/16.0 Mobile/14E304 Safari/602.1",
|
||||||
"viewport": {
|
"viewport": {
|
||||||
"width": 568,
|
"width": 568,
|
||||||
"height": 320
|
"height": 320
|
||||||
|
|
@ -440,7 +440,7 @@
|
||||||
"defaultBrowserType": "webkit"
|
"defaultBrowserType": "webkit"
|
||||||
},
|
},
|
||||||
"iPhone X": {
|
"iPhone X": {
|
||||||
"userAgent": "Mozilla/5.0 (iPhone; CPU iPhone OS 11_0 like Mac OS X) AppleWebKit/604.1.38 (KHTML, like Gecko) Version/15.4 Mobile/15A372 Safari/604.1",
|
"userAgent": "Mozilla/5.0 (iPhone; CPU iPhone OS 11_0 like Mac OS X) AppleWebKit/604.1.38 (KHTML, like Gecko) Version/16.0 Mobile/15A372 Safari/604.1",
|
||||||
"viewport": {
|
"viewport": {
|
||||||
"width": 375,
|
"width": 375,
|
||||||
"height": 812
|
"height": 812
|
||||||
|
|
@ -451,7 +451,7 @@
|
||||||
"defaultBrowserType": "webkit"
|
"defaultBrowserType": "webkit"
|
||||||
},
|
},
|
||||||
"iPhone X landscape": {
|
"iPhone X landscape": {
|
||||||
"userAgent": "Mozilla/5.0 (iPhone; CPU iPhone OS 11_0 like Mac OS X) AppleWebKit/604.1.38 (KHTML, like Gecko) Version/15.4 Mobile/15A372 Safari/604.1",
|
"userAgent": "Mozilla/5.0 (iPhone; CPU iPhone OS 11_0 like Mac OS X) AppleWebKit/604.1.38 (KHTML, like Gecko) Version/16.0 Mobile/15A372 Safari/604.1",
|
||||||
"viewport": {
|
"viewport": {
|
||||||
"width": 812,
|
"width": 812,
|
||||||
"height": 375
|
"height": 375
|
||||||
|
|
@ -462,7 +462,7 @@
|
||||||
"defaultBrowserType": "webkit"
|
"defaultBrowserType": "webkit"
|
||||||
},
|
},
|
||||||
"iPhone XR": {
|
"iPhone XR": {
|
||||||
"userAgent": "Mozilla/5.0 (iPhone; CPU iPhone OS 12_0 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/15.4 Mobile/15E148 Safari/604.1",
|
"userAgent": "Mozilla/5.0 (iPhone; CPU iPhone OS 12_0 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/16.0 Mobile/15E148 Safari/604.1",
|
||||||
"viewport": {
|
"viewport": {
|
||||||
"width": 414,
|
"width": 414,
|
||||||
"height": 896
|
"height": 896
|
||||||
|
|
@ -473,7 +473,7 @@
|
||||||
"defaultBrowserType": "webkit"
|
"defaultBrowserType": "webkit"
|
||||||
},
|
},
|
||||||
"iPhone XR landscape": {
|
"iPhone XR landscape": {
|
||||||
"userAgent": "Mozilla/5.0 (iPhone; CPU iPhone OS 12_0 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/15.4 Mobile/15E148 Safari/604.1",
|
"userAgent": "Mozilla/5.0 (iPhone; CPU iPhone OS 12_0 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/16.0 Mobile/15E148 Safari/604.1",
|
||||||
"viewport": {
|
"viewport": {
|
||||||
"width": 896,
|
"width": 896,
|
||||||
"height": 414
|
"height": 414
|
||||||
|
|
@ -484,7 +484,7 @@
|
||||||
"defaultBrowserType": "webkit"
|
"defaultBrowserType": "webkit"
|
||||||
},
|
},
|
||||||
"iPhone 11": {
|
"iPhone 11": {
|
||||||
"userAgent": "Mozilla/5.0 (iPhone; CPU iPhone OS 12_2 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/15.4 Mobile/15E148 Safari/604.1",
|
"userAgent": "Mozilla/5.0 (iPhone; CPU iPhone OS 12_2 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/16.0 Mobile/15E148 Safari/604.1",
|
||||||
"screen": {
|
"screen": {
|
||||||
"width": 414,
|
"width": 414,
|
||||||
"height": 896
|
"height": 896
|
||||||
|
|
@ -499,7 +499,7 @@
|
||||||
"defaultBrowserType": "webkit"
|
"defaultBrowserType": "webkit"
|
||||||
},
|
},
|
||||||
"iPhone 11 landscape": {
|
"iPhone 11 landscape": {
|
||||||
"userAgent": "Mozilla/5.0 (iPhone; CPU iPhone OS 12_2 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/15.4 Mobile/15E148 Safari/604.1",
|
"userAgent": "Mozilla/5.0 (iPhone; CPU iPhone OS 12_2 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/16.0 Mobile/15E148 Safari/604.1",
|
||||||
"screen": {
|
"screen": {
|
||||||
"width": 414,
|
"width": 414,
|
||||||
"height": 896
|
"height": 896
|
||||||
|
|
@ -514,7 +514,7 @@
|
||||||
"defaultBrowserType": "webkit"
|
"defaultBrowserType": "webkit"
|
||||||
},
|
},
|
||||||
"iPhone 11 Pro": {
|
"iPhone 11 Pro": {
|
||||||
"userAgent": "Mozilla/5.0 (iPhone; CPU iPhone OS 12_2 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/15.4 Mobile/15E148 Safari/604.1",
|
"userAgent": "Mozilla/5.0 (iPhone; CPU iPhone OS 12_2 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/16.0 Mobile/15E148 Safari/604.1",
|
||||||
"screen": {
|
"screen": {
|
||||||
"width": 375,
|
"width": 375,
|
||||||
"height": 812
|
"height": 812
|
||||||
|
|
@ -529,7 +529,7 @@
|
||||||
"defaultBrowserType": "webkit"
|
"defaultBrowserType": "webkit"
|
||||||
},
|
},
|
||||||
"iPhone 11 Pro landscape": {
|
"iPhone 11 Pro landscape": {
|
||||||
"userAgent": "Mozilla/5.0 (iPhone; CPU iPhone OS 12_2 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/15.4 Mobile/15E148 Safari/604.1",
|
"userAgent": "Mozilla/5.0 (iPhone; CPU iPhone OS 12_2 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/16.0 Mobile/15E148 Safari/604.1",
|
||||||
"screen": {
|
"screen": {
|
||||||
"width": 375,
|
"width": 375,
|
||||||
"height": 812
|
"height": 812
|
||||||
|
|
@ -544,7 +544,7 @@
|
||||||
"defaultBrowserType": "webkit"
|
"defaultBrowserType": "webkit"
|
||||||
},
|
},
|
||||||
"iPhone 11 Pro Max": {
|
"iPhone 11 Pro Max": {
|
||||||
"userAgent": "Mozilla/5.0 (iPhone; CPU iPhone OS 12_2 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/15.4 Mobile/15E148 Safari/604.1",
|
"userAgent": "Mozilla/5.0 (iPhone; CPU iPhone OS 12_2 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/16.0 Mobile/15E148 Safari/604.1",
|
||||||
"screen": {
|
"screen": {
|
||||||
"width": 414,
|
"width": 414,
|
||||||
"height": 896
|
"height": 896
|
||||||
|
|
@ -559,7 +559,7 @@
|
||||||
"defaultBrowserType": "webkit"
|
"defaultBrowserType": "webkit"
|
||||||
},
|
},
|
||||||
"iPhone 11 Pro Max landscape": {
|
"iPhone 11 Pro Max landscape": {
|
||||||
"userAgent": "Mozilla/5.0 (iPhone; CPU iPhone OS 12_2 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/15.4 Mobile/15E148 Safari/604.1",
|
"userAgent": "Mozilla/5.0 (iPhone; CPU iPhone OS 12_2 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/16.0 Mobile/15E148 Safari/604.1",
|
||||||
"screen": {
|
"screen": {
|
||||||
"width": 414,
|
"width": 414,
|
||||||
"height": 896
|
"height": 896
|
||||||
|
|
@ -574,7 +574,7 @@
|
||||||
"defaultBrowserType": "webkit"
|
"defaultBrowserType": "webkit"
|
||||||
},
|
},
|
||||||
"iPhone 12": {
|
"iPhone 12": {
|
||||||
"userAgent": "Mozilla/5.0 (iPhone; CPU iPhone OS 14_4 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/15.4 Mobile/15E148 Safari/604.1",
|
"userAgent": "Mozilla/5.0 (iPhone; CPU iPhone OS 14_4 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/16.0 Mobile/15E148 Safari/604.1",
|
||||||
"screen": {
|
"screen": {
|
||||||
"width": 390,
|
"width": 390,
|
||||||
"height": 844
|
"height": 844
|
||||||
|
|
@ -589,7 +589,7 @@
|
||||||
"defaultBrowserType": "webkit"
|
"defaultBrowserType": "webkit"
|
||||||
},
|
},
|
||||||
"iPhone 12 landscape": {
|
"iPhone 12 landscape": {
|
||||||
"userAgent": "Mozilla/5.0 (iPhone; CPU iPhone OS 14_4 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/15.4 Mobile/15E148 Safari/604.1",
|
"userAgent": "Mozilla/5.0 (iPhone; CPU iPhone OS 14_4 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/16.0 Mobile/15E148 Safari/604.1",
|
||||||
"screen": {
|
"screen": {
|
||||||
"width": 390,
|
"width": 390,
|
||||||
"height": 844
|
"height": 844
|
||||||
|
|
@ -604,7 +604,7 @@
|
||||||
"defaultBrowserType": "webkit"
|
"defaultBrowserType": "webkit"
|
||||||
},
|
},
|
||||||
"iPhone 12 Pro": {
|
"iPhone 12 Pro": {
|
||||||
"userAgent": "Mozilla/5.0 (iPhone; CPU iPhone OS 14_4 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/15.4 Mobile/15E148 Safari/604.1",
|
"userAgent": "Mozilla/5.0 (iPhone; CPU iPhone OS 14_4 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/16.0 Mobile/15E148 Safari/604.1",
|
||||||
"screen": {
|
"screen": {
|
||||||
"width": 390,
|
"width": 390,
|
||||||
"height": 844
|
"height": 844
|
||||||
|
|
@ -619,7 +619,7 @@
|
||||||
"defaultBrowserType": "webkit"
|
"defaultBrowserType": "webkit"
|
||||||
},
|
},
|
||||||
"iPhone 12 Pro landscape": {
|
"iPhone 12 Pro landscape": {
|
||||||
"userAgent": "Mozilla/5.0 (iPhone; CPU iPhone OS 14_4 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/15.4 Mobile/15E148 Safari/604.1",
|
"userAgent": "Mozilla/5.0 (iPhone; CPU iPhone OS 14_4 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/16.0 Mobile/15E148 Safari/604.1",
|
||||||
"screen": {
|
"screen": {
|
||||||
"width": 390,
|
"width": 390,
|
||||||
"height": 844
|
"height": 844
|
||||||
|
|
@ -634,7 +634,7 @@
|
||||||
"defaultBrowserType": "webkit"
|
"defaultBrowserType": "webkit"
|
||||||
},
|
},
|
||||||
"iPhone 12 Pro Max": {
|
"iPhone 12 Pro Max": {
|
||||||
"userAgent": "Mozilla/5.0 (iPhone; CPU iPhone OS 14_4 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/15.4 Mobile/15E148 Safari/604.1",
|
"userAgent": "Mozilla/5.0 (iPhone; CPU iPhone OS 14_4 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/16.0 Mobile/15E148 Safari/604.1",
|
||||||
"screen": {
|
"screen": {
|
||||||
"width": 428,
|
"width": 428,
|
||||||
"height": 926
|
"height": 926
|
||||||
|
|
@ -649,7 +649,7 @@
|
||||||
"defaultBrowserType": "webkit"
|
"defaultBrowserType": "webkit"
|
||||||
},
|
},
|
||||||
"iPhone 12 Pro Max landscape": {
|
"iPhone 12 Pro Max landscape": {
|
||||||
"userAgent": "Mozilla/5.0 (iPhone; CPU iPhone OS 14_4 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/15.4 Mobile/15E148 Safari/604.1",
|
"userAgent": "Mozilla/5.0 (iPhone; CPU iPhone OS 14_4 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/16.0 Mobile/15E148 Safari/604.1",
|
||||||
"screen": {
|
"screen": {
|
||||||
"width": 428,
|
"width": 428,
|
||||||
"height": 926
|
"height": 926
|
||||||
|
|
@ -664,7 +664,7 @@
|
||||||
"defaultBrowserType": "webkit"
|
"defaultBrowserType": "webkit"
|
||||||
},
|
},
|
||||||
"iPhone 12 Mini": {
|
"iPhone 12 Mini": {
|
||||||
"userAgent": "Mozilla/5.0 (iPhone; CPU iPhone OS 14_4 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/15.4 Mobile/15E148 Safari/604.1",
|
"userAgent": "Mozilla/5.0 (iPhone; CPU iPhone OS 14_4 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/16.0 Mobile/15E148 Safari/604.1",
|
||||||
"screen": {
|
"screen": {
|
||||||
"width": 375,
|
"width": 375,
|
||||||
"height": 812
|
"height": 812
|
||||||
|
|
@ -679,7 +679,7 @@
|
||||||
"defaultBrowserType": "webkit"
|
"defaultBrowserType": "webkit"
|
||||||
},
|
},
|
||||||
"iPhone 12 Mini landscape": {
|
"iPhone 12 Mini landscape": {
|
||||||
"userAgent": "Mozilla/5.0 (iPhone; CPU iPhone OS 14_4 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/15.4 Mobile/15E148 Safari/604.1",
|
"userAgent": "Mozilla/5.0 (iPhone; CPU iPhone OS 14_4 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/16.0 Mobile/15E148 Safari/604.1",
|
||||||
"screen": {
|
"screen": {
|
||||||
"width": 375,
|
"width": 375,
|
||||||
"height": 812
|
"height": 812
|
||||||
|
|
@ -694,7 +694,7 @@
|
||||||
"defaultBrowserType": "webkit"
|
"defaultBrowserType": "webkit"
|
||||||
},
|
},
|
||||||
"iPhone 13": {
|
"iPhone 13": {
|
||||||
"userAgent": "Mozilla/5.0 (iPhone; CPU iPhone OS 15_0 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/15.4 Mobile/15E148 Safari/604.1",
|
"userAgent": "Mozilla/5.0 (iPhone; CPU iPhone OS 15_0 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/16.0 Mobile/15E148 Safari/604.1",
|
||||||
"screen": {
|
"screen": {
|
||||||
"width": 390,
|
"width": 390,
|
||||||
"height": 844
|
"height": 844
|
||||||
|
|
@ -709,7 +709,7 @@
|
||||||
"defaultBrowserType": "webkit"
|
"defaultBrowserType": "webkit"
|
||||||
},
|
},
|
||||||
"iPhone 13 landscape": {
|
"iPhone 13 landscape": {
|
||||||
"userAgent": "Mozilla/5.0 (iPhone; CPU iPhone OS 15_0 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/15.4 Mobile/15E148 Safari/604.1",
|
"userAgent": "Mozilla/5.0 (iPhone; CPU iPhone OS 15_0 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/16.0 Mobile/15E148 Safari/604.1",
|
||||||
"screen": {
|
"screen": {
|
||||||
"width": 390,
|
"width": 390,
|
||||||
"height": 844
|
"height": 844
|
||||||
|
|
@ -724,7 +724,7 @@
|
||||||
"defaultBrowserType": "webkit"
|
"defaultBrowserType": "webkit"
|
||||||
},
|
},
|
||||||
"iPhone 13 Pro": {
|
"iPhone 13 Pro": {
|
||||||
"userAgent": "Mozilla/5.0 (iPhone; CPU iPhone OS 15_0 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/15.4 Mobile/15E148 Safari/604.1",
|
"userAgent": "Mozilla/5.0 (iPhone; CPU iPhone OS 15_0 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/16.0 Mobile/15E148 Safari/604.1",
|
||||||
"screen": {
|
"screen": {
|
||||||
"width": 390,
|
"width": 390,
|
||||||
"height": 844
|
"height": 844
|
||||||
|
|
@ -739,7 +739,7 @@
|
||||||
"defaultBrowserType": "webkit"
|
"defaultBrowserType": "webkit"
|
||||||
},
|
},
|
||||||
"iPhone 13 Pro landscape": {
|
"iPhone 13 Pro landscape": {
|
||||||
"userAgent": "Mozilla/5.0 (iPhone; CPU iPhone OS 15_0 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/15.4 Mobile/15E148 Safari/604.1",
|
"userAgent": "Mozilla/5.0 (iPhone; CPU iPhone OS 15_0 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/16.0 Mobile/15E148 Safari/604.1",
|
||||||
"screen": {
|
"screen": {
|
||||||
"width": 390,
|
"width": 390,
|
||||||
"height": 844
|
"height": 844
|
||||||
|
|
@ -754,7 +754,7 @@
|
||||||
"defaultBrowserType": "webkit"
|
"defaultBrowserType": "webkit"
|
||||||
},
|
},
|
||||||
"iPhone 13 Pro Max": {
|
"iPhone 13 Pro Max": {
|
||||||
"userAgent": "Mozilla/5.0 (iPhone; CPU iPhone OS 15_0 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/15.4 Mobile/15E148 Safari/604.1",
|
"userAgent": "Mozilla/5.0 (iPhone; CPU iPhone OS 15_0 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/16.0 Mobile/15E148 Safari/604.1",
|
||||||
"screen": {
|
"screen": {
|
||||||
"width": 428,
|
"width": 428,
|
||||||
"height": 926
|
"height": 926
|
||||||
|
|
@ -769,7 +769,7 @@
|
||||||
"defaultBrowserType": "webkit"
|
"defaultBrowserType": "webkit"
|
||||||
},
|
},
|
||||||
"iPhone 13 Pro Max landscape": {
|
"iPhone 13 Pro Max landscape": {
|
||||||
"userAgent": "Mozilla/5.0 (iPhone; CPU iPhone OS 15_0 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/15.4 Mobile/15E148 Safari/604.1",
|
"userAgent": "Mozilla/5.0 (iPhone; CPU iPhone OS 15_0 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/16.0 Mobile/15E148 Safari/604.1",
|
||||||
"screen": {
|
"screen": {
|
||||||
"width": 428,
|
"width": 428,
|
||||||
"height": 926
|
"height": 926
|
||||||
|
|
@ -784,7 +784,7 @@
|
||||||
"defaultBrowserType": "webkit"
|
"defaultBrowserType": "webkit"
|
||||||
},
|
},
|
||||||
"iPhone 13 Mini": {
|
"iPhone 13 Mini": {
|
||||||
"userAgent": "Mozilla/5.0 (iPhone; CPU iPhone OS 15_0 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/15.4 Mobile/15E148 Safari/604.1",
|
"userAgent": "Mozilla/5.0 (iPhone; CPU iPhone OS 15_0 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/16.0 Mobile/15E148 Safari/604.1",
|
||||||
"screen": {
|
"screen": {
|
||||||
"width": 375,
|
"width": 375,
|
||||||
"height": 812
|
"height": 812
|
||||||
|
|
@ -799,7 +799,7 @@
|
||||||
"defaultBrowserType": "webkit"
|
"defaultBrowserType": "webkit"
|
||||||
},
|
},
|
||||||
"iPhone 13 Mini landscape": {
|
"iPhone 13 Mini landscape": {
|
||||||
"userAgent": "Mozilla/5.0 (iPhone; CPU iPhone OS 15_0 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/15.4 Mobile/15E148 Safari/604.1",
|
"userAgent": "Mozilla/5.0 (iPhone; CPU iPhone OS 15_0 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/16.0 Mobile/15E148 Safari/604.1",
|
||||||
"screen": {
|
"screen": {
|
||||||
"width": 375,
|
"width": 375,
|
||||||
"height": 812
|
"height": 812
|
||||||
|
|
@ -1337,7 +1337,7 @@
|
||||||
"defaultBrowserType": "firefox"
|
"defaultBrowserType": "firefox"
|
||||||
},
|
},
|
||||||
"Desktop Safari": {
|
"Desktop Safari": {
|
||||||
"userAgent": "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/15.4 Safari/605.1.15",
|
"userAgent": "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/16.0 Safari/605.1.15",
|
||||||
"screen": {
|
"screen": {
|
||||||
"width": 1792,
|
"width": 1792,
|
||||||
"height": 1120
|
"height": 1120
|
||||||
|
|
|
||||||
|
|
@ -1071,7 +1071,7 @@ export class InjectedScript {
|
||||||
let received: string | undefined;
|
let received: string | undefined;
|
||||||
if (expression === 'to.have.attribute') {
|
if (expression === 'to.have.attribute') {
|
||||||
received = element.getAttribute(options.expressionArg) || '';
|
received = element.getAttribute(options.expressionArg) || '';
|
||||||
} else if (expression === 'to.have.class' || expression === 'to.contain.class') {
|
} else if (expression === 'to.have.class') {
|
||||||
received = element.classList.toString();
|
received = element.classList.toString();
|
||||||
} else if (expression === 'to.have.css') {
|
} else if (expression === 'to.have.css') {
|
||||||
received = window.getComputedStyle(element).getPropertyValue(options.expressionArg);
|
received = window.getComputedStyle(element).getPropertyValue(options.expressionArg);
|
||||||
|
|
@ -1092,9 +1092,7 @@ export class InjectedScript {
|
||||||
|
|
||||||
if (received !== undefined && options.expectedText) {
|
if (received !== undefined && options.expectedText) {
|
||||||
const matcher = new ExpectedTextMatcher(options.expectedText[0]);
|
const matcher = new ExpectedTextMatcher(options.expectedText[0]);
|
||||||
return { received, matches: matcher.matches(received, {
|
return { received, matches: matcher.matches(received) };
|
||||||
toContainClass: expression === 'to.contain.class',
|
|
||||||
}) };
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -1114,7 +1112,7 @@ export class InjectedScript {
|
||||||
let received: string[] | undefined;
|
let received: string[] | undefined;
|
||||||
if (expression === 'to.have.text.array' || expression === 'to.contain.text.array')
|
if (expression === 'to.have.text.array' || expression === 'to.contain.text.array')
|
||||||
received = elements.map(e => options.useInnerText ? (e as HTMLElement).innerText : e.textContent || '');
|
received = elements.map(e => options.useInnerText ? (e as HTMLElement).innerText : e.textContent || '');
|
||||||
else if (expression === 'to.have.class.array' || expression === 'to.contain.class.array')
|
else if (expression === 'to.have.class.array')
|
||||||
received = elements.map(e => e.classList.toString());
|
received = elements.map(e => e.classList.toString());
|
||||||
|
|
||||||
if (received && options.expectedText) {
|
if (received && options.expectedText) {
|
||||||
|
|
@ -1128,9 +1126,7 @@ export class InjectedScript {
|
||||||
const matchers = options.expectedText.map(e => new ExpectedTextMatcher(e));
|
const matchers = options.expectedText.map(e => new ExpectedTextMatcher(e));
|
||||||
let mIndex = 0, rIndex = 0;
|
let mIndex = 0, rIndex = 0;
|
||||||
while (mIndex < matchers.length && rIndex < received.length) {
|
while (mIndex < matchers.length && rIndex < received.length) {
|
||||||
if (matchers[mIndex].matches(received[rIndex], {
|
if (matchers[mIndex].matches(received[rIndex]))
|
||||||
toContainClass: expression === 'to.contain.class.array',
|
|
||||||
}))
|
|
||||||
++mIndex;
|
++mIndex;
|
||||||
++rIndex;
|
++rIndex;
|
||||||
}
|
}
|
||||||
|
|
@ -1265,9 +1261,7 @@ class ExpectedTextMatcher {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
matches(text: string, { toContainClass }: { toContainClass?: boolean } = {}): boolean {
|
matches(text: string): boolean {
|
||||||
if (toContainClass)
|
|
||||||
return this.matchesClassList(text);
|
|
||||||
if (!this._regex)
|
if (!this._regex)
|
||||||
text = this.normalize(text)!;
|
text = this.normalize(text)!;
|
||||||
if (this._string !== undefined)
|
if (this._string !== undefined)
|
||||||
|
|
@ -1279,18 +1273,6 @@ class ExpectedTextMatcher {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
private matchesClassList(received: string): boolean {
|
|
||||||
const expected = this.normalizeClassList(this._string || '');
|
|
||||||
if (expected.length === 0)
|
|
||||||
return false;
|
|
||||||
const normalizedReceived = this.normalizeClassList(received);
|
|
||||||
return expected.every(classListEntry => normalizedReceived.includes(classListEntry));
|
|
||||||
}
|
|
||||||
|
|
||||||
private normalizeClassList(classList: string): string[] {
|
|
||||||
return classList.trim().split(/\s+/g).map(c => this.normalize(c)).filter(c => c) as string[];
|
|
||||||
}
|
|
||||||
|
|
||||||
private normalize(s: string | undefined): string | undefined {
|
private normalize(s: string | undefined): string | undefined {
|
||||||
if (!s)
|
if (!s)
|
||||||
return s;
|
return s;
|
||||||
|
|
|
||||||
|
|
@ -184,7 +184,7 @@ const DOWNLOAD_PATHS = {
|
||||||
'ubuntu18.04-arm64': undefined,
|
'ubuntu18.04-arm64': undefined,
|
||||||
'ubuntu20.04-arm64': 'builds/webkit/%s/webkit-ubuntu-20.04-arm64.zip',
|
'ubuntu20.04-arm64': 'builds/webkit/%s/webkit-ubuntu-20.04-arm64.zip',
|
||||||
'ubuntu22.04-arm64': 'builds/webkit/%s/webkit-ubuntu-22.04-arm64.zip',
|
'ubuntu22.04-arm64': 'builds/webkit/%s/webkit-ubuntu-22.04-arm64.zip',
|
||||||
'debian11': 'builds/webkit/%s/webkit-linux-universal.zip',
|
'debian11': 'builds/webkit/%s/webkit-debian-11.zip',
|
||||||
'mac10.13': undefined,
|
'mac10.13': undefined,
|
||||||
'mac10.14': 'builds/deprecated-webkit-mac-10.14/%s/deprecated-webkit-mac-10.14.zip',
|
'mac10.14': 'builds/deprecated-webkit-mac-10.14/%s/deprecated-webkit-mac-10.14.zip',
|
||||||
'mac10.15': 'builds/webkit/%s/webkit-mac-10.15.zip',
|
'mac10.15': 'builds/webkit/%s/webkit-mac-10.15.zip',
|
||||||
|
|
@ -618,11 +618,6 @@ export class Registry {
|
||||||
if (browserName === 'firefox' && distributionInfo?.id === 'ubuntu' && distributionInfo?.version === '16.04')
|
if (browserName === 'firefox' && distributionInfo?.id === 'ubuntu' && distributionInfo?.version === '16.04')
|
||||||
throw new Error(`Cannot launch Firefox on Ubuntu 16.04! Minimum required Ubuntu version for Firefox browser is 18.04`);
|
throw new Error(`Cannot launch Firefox on Ubuntu 16.04! Minimum required Ubuntu version for Firefox browser is 18.04`);
|
||||||
|
|
||||||
// Skip dependency validation for WebKit on non-ubuntu distributions since it takes
|
|
||||||
// forever and is not needed due to universal build.
|
|
||||||
if (os.platform() === 'linux' && browserName === 'webkit' && distributionInfo?.id !== 'ubuntu')
|
|
||||||
return;
|
|
||||||
|
|
||||||
if (os.platform() === 'linux')
|
if (os.platform() === 'linux')
|
||||||
return await validateDependenciesLinux(sdkLanguage, linuxLddDirectories.map(d => path.join(browserDirectory, d)), dlOpenLibraries);
|
return await validateDependenciesLinux(sdkLanguage, linuxLddDirectories.map(d => path.join(browserDirectory, d)), dlOpenLibraries);
|
||||||
if (os.platform() === 'win32' && os.arch() === 'x64')
|
if (os.platform() === 'win32' && os.arch() === 'x64')
|
||||||
|
|
@ -751,21 +746,22 @@ export class Registry {
|
||||||
private async _installMSEdgeChannel(channel: 'msedge'|'msedge-beta'|'msedge-dev', scripts: Record<'linux' | 'darwin' | 'win32', string>) {
|
private async _installMSEdgeChannel(channel: 'msedge'|'msedge-beta'|'msedge-dev', scripts: Record<'linux' | 'darwin' | 'win32', string>) {
|
||||||
const scriptArgs: string[] = [];
|
const scriptArgs: string[] = [];
|
||||||
if (process.platform !== 'linux') {
|
if (process.platform !== 'linux') {
|
||||||
const products = JSON.parse(await fetchData({ url: 'https://edgeupdates.microsoft.com/api/products' }));
|
const products = lowercaseAllKeys(JSON.parse(await fetchData({ url: 'https://edgeupdates.microsoft.com/api/products' })));
|
||||||
|
|
||||||
const productName = {
|
const productName = {
|
||||||
'msedge': 'Stable',
|
'msedge': 'Stable',
|
||||||
'msedge-beta': 'Beta',
|
'msedge-beta': 'Beta',
|
||||||
'msedge-dev': 'Dev',
|
'msedge-dev': 'Dev',
|
||||||
}[channel];
|
}[channel];
|
||||||
const product = products.find((product: any) => product.Product === productName);
|
const product = products.find((product: any) => product.product === productName);
|
||||||
const searchConfig = ({
|
const searchConfig = ({
|
||||||
darwin: { platform: 'MacOS', arch: 'universal', artifact: 'pkg' },
|
darwin: { platform: 'MacOS', arch: 'universal', artifact: 'pkg' },
|
||||||
win32: { platform: 'Windows', arch: 'x64', artifact: 'msi' },
|
win32: { platform: 'Windows', arch: 'x64', artifact: 'msi' },
|
||||||
} as any)[process.platform];
|
} as any)[process.platform];
|
||||||
const release = searchConfig ? product.Releases.find((release: any) => release.Platform === searchConfig.platform && release.Architecture === searchConfig.arch) : null;
|
const release = searchConfig ? product.releases.find((release: any) => release.platform === searchConfig.platform && release.architecture === searchConfig.arch) : null;
|
||||||
const artifact = release ? release.Artifacts.find((artifact: any) => artifact.ArtifactName === searchConfig.artifact) : null;
|
const artifact = release ? release.artifacts.find((artifact: any) => artifact.artifactname === searchConfig.artifact) : null;
|
||||||
if (artifact)
|
if (artifact)
|
||||||
scriptArgs.push(artifact.Location /* url */);
|
scriptArgs.push(artifact.location /* url */);
|
||||||
else
|
else
|
||||||
throw new Error(`Cannot install ${channel} on ${process.platform}`);
|
throw new Error(`Cannot install ${channel} on ${process.platform}`);
|
||||||
}
|
}
|
||||||
|
|
@ -913,4 +909,17 @@ export function findChromiumChannel(sdkLanguage: string): string | undefined {
|
||||||
return channel;
|
return channel;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function lowercaseAllKeys(json: any): any {
|
||||||
|
if (typeof json !== 'object' || !json)
|
||||||
|
return json;
|
||||||
|
|
||||||
|
if (Array.isArray(json))
|
||||||
|
return json.map(lowercaseAllKeys);
|
||||||
|
|
||||||
|
const result: any = {};
|
||||||
|
for (const [key, value] of Object.entries(json))
|
||||||
|
result[key.toLowerCase()] = lowercaseAllKeys(value);
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
export const registry = new Registry(require('../../../browsers.json'));
|
export const registry = new Registry(require('../../../browsers.json'));
|
||||||
|
|
|
||||||
|
|
@ -717,30 +717,83 @@ export const deps: any = {
|
||||||
'libxtst6'
|
'libxtst6'
|
||||||
],
|
],
|
||||||
webkit: [
|
webkit: [
|
||||||
// We use universal build on debian so webkit does not require any dependencies.
|
'gstreamer1.0-libav',
|
||||||
|
'gstreamer1.0-plugins-bad',
|
||||||
|
'gstreamer1.0-plugins-base',
|
||||||
|
'gstreamer1.0-plugins-good',
|
||||||
|
'libatk-bridge2.0-0',
|
||||||
|
'libatk1.0-0',
|
||||||
|
'libcairo2',
|
||||||
|
'libdbus-1-3',
|
||||||
|
'libdrm2',
|
||||||
|
'libegl1',
|
||||||
|
'libenchant-2-2',
|
||||||
|
'libepoxy0',
|
||||||
|
'libevdev2',
|
||||||
|
'libfontconfig1',
|
||||||
|
'libfreetype6',
|
||||||
|
'libgbm1',
|
||||||
|
'libgdk-pixbuf-2.0-0',
|
||||||
|
'libgles2',
|
||||||
|
'libglib2.0-0',
|
||||||
|
'libglx0',
|
||||||
|
'libgstreamer-gl1.0-0',
|
||||||
|
'libgstreamer-plugins-base1.0-0',
|
||||||
|
'libgstreamer1.0-0',
|
||||||
|
'libgtk-3-0',
|
||||||
|
'libgudev-1.0-0',
|
||||||
|
'libharfbuzz-icu0',
|
||||||
|
'libharfbuzz0b',
|
||||||
|
'libhyphen0',
|
||||||
|
'libicu67',
|
||||||
|
'libjavascriptcoregtk-4.0-18',
|
||||||
|
'libjpeg62-turbo',
|
||||||
|
'liblcms2-2',
|
||||||
|
'libmanette-0.2-0',
|
||||||
|
'libnotify4',
|
||||||
|
'libopengl0',
|
||||||
|
'libopenjp2-7',
|
||||||
|
'libopus0',
|
||||||
|
'libpango-1.0-0',
|
||||||
|
'libpng16-16',
|
||||||
|
'libproxy1v5',
|
||||||
|
'libsecret-1-0',
|
||||||
|
'libsoup2.4-1',
|
||||||
|
'libwayland-client0',
|
||||||
|
'libwayland-egl1',
|
||||||
|
'libwayland-server0',
|
||||||
|
'libwebkit2gtk-4.0-37',
|
||||||
|
'libwebp6',
|
||||||
|
'libwebpdemux2',
|
||||||
|
'libwoff1',
|
||||||
|
'libwpe-1.0-1',
|
||||||
|
'libwpebackend-fdo-1.0-1',
|
||||||
|
'libwpewebkit-1.0-3',
|
||||||
|
'libx11-6',
|
||||||
|
'libxcomposite1',
|
||||||
|
'libxdamage1',
|
||||||
|
'libxkbcommon0',
|
||||||
|
'libxml2',
|
||||||
|
'libxslt1.1'
|
||||||
],
|
],
|
||||||
lib2package: {
|
lib2package: {
|
||||||
'libasound.so.2': 'libasound2',
|
'libasound.so.2': 'libasound2',
|
||||||
'libatk-1.0.so.0': 'libatk1.0-0',
|
'libatk-1.0.so.0': 'libatk1.0-0',
|
||||||
'libatk-bridge-2.0.so.0': 'libatk-bridge2.0-0',
|
'libatk-bridge-2.0.so.0': 'libatk-bridge2.0-0',
|
||||||
'libatomic.so.1': 'libatomic1',
|
|
||||||
'libatspi.so.0': 'libatspi2.0-0',
|
'libatspi.so.0': 'libatspi2.0-0',
|
||||||
'libc.so.6': 'libc6',
|
|
||||||
'libcairo-gobject.so.2': 'libcairo-gobject2',
|
'libcairo-gobject.so.2': 'libcairo-gobject2',
|
||||||
'libcairo.so.2': 'libcairo2',
|
'libcairo.so.2': 'libcairo2',
|
||||||
'libcups.so.2': 'libcups2',
|
'libcups.so.2': 'libcups2',
|
||||||
'libdbus-1.so.3': 'libdbus-1-3',
|
'libdbus-1.so.3': 'libdbus-1-3',
|
||||||
'libdbus-glib-1.so.2': 'libdbus-glib-1-2',
|
'libdbus-glib-1.so.2': 'libdbus-glib-1-2',
|
||||||
'libdl.so.2': 'libc6',
|
|
||||||
'libdrm.so.2': 'libdrm2',
|
'libdrm.so.2': 'libdrm2',
|
||||||
'libEGL.so.1': 'libegl1',
|
'libEGL.so.1': 'libegl1',
|
||||||
'libenchant-2.so.2': 'libenchant-2-2',
|
'libenchant-2.so.2': 'libenchant-2-2',
|
||||||
'libepoxy.so.0': 'libepoxy0',
|
'libepoxy.so.0': 'libepoxy0',
|
||||||
|
'libevdev.so.2': 'libevdev2',
|
||||||
'libfontconfig.so.1': 'libfontconfig1',
|
'libfontconfig.so.1': 'libfontconfig1',
|
||||||
'libfreetype.so.6': 'libfreetype6',
|
'libfreetype.so.6': 'libfreetype6',
|
||||||
'libgbm.so.1': 'libgbm1',
|
'libgbm.so.1': 'libgbm1',
|
||||||
'libgcc_s.so.1': 'libgcc-s1',
|
|
||||||
'libgcrypt.so.20': 'libgcrypt20',
|
|
||||||
'libgdk_pixbuf-2.0.so.0': 'libgdk-pixbuf-2.0-0',
|
'libgdk_pixbuf-2.0.so.0': 'libgdk-pixbuf-2.0-0',
|
||||||
'libgdk-3.so.0': 'libgtk-3-0',
|
'libgdk-3.so.0': 'libgtk-3-0',
|
||||||
'libgio-2.0.so.0': 'libglib2.0-0',
|
'libgio-2.0.so.0': 'libglib2.0-0',
|
||||||
|
|
@ -749,12 +802,10 @@ export const deps: any = {
|
||||||
'libGLX.so.0': 'libglx0',
|
'libGLX.so.0': 'libglx0',
|
||||||
'libgmodule-2.0.so.0': 'libglib2.0-0',
|
'libgmodule-2.0.so.0': 'libglib2.0-0',
|
||||||
'libgobject-2.0.so.0': 'libglib2.0-0',
|
'libgobject-2.0.so.0': 'libglib2.0-0',
|
||||||
'libgpg-error.so.0': 'libgpg-error0',
|
|
||||||
'libgstallocators-1.0.so.0': 'libgstreamer-plugins-base1.0-0',
|
'libgstallocators-1.0.so.0': 'libgstreamer-plugins-base1.0-0',
|
||||||
'libgstapp-1.0.so.0': 'libgstreamer-plugins-base1.0-0',
|
'libgstapp-1.0.so.0': 'libgstreamer-plugins-base1.0-0',
|
||||||
'libgstaudio-1.0.so.0': 'libgstreamer-plugins-base1.0-0',
|
'libgstaudio-1.0.so.0': 'libgstreamer-plugins-base1.0-0',
|
||||||
'libgstbase-1.0.so.0': 'libgstreamer1.0-0',
|
'libgstbase-1.0.so.0': 'libgstreamer1.0-0',
|
||||||
'libgstcodecparsers-1.0.so.0': 'libgstreamer-plugins-bad1.0-0',
|
|
||||||
'libgstfft-1.0.so.0': 'libgstreamer-plugins-base1.0-0',
|
'libgstfft-1.0.so.0': 'libgstreamer-plugins-base1.0-0',
|
||||||
'libgstgl-1.0.so.0': 'libgstreamer-gl1.0-0',
|
'libgstgl-1.0.so.0': 'libgstreamer-gl1.0-0',
|
||||||
'libgstpbutils-1.0.so.0': 'libgstreamer-plugins-base1.0-0',
|
'libgstpbutils-1.0.so.0': 'libgstreamer-plugins-base1.0-0',
|
||||||
|
|
@ -762,13 +813,15 @@ export const deps: any = {
|
||||||
'libgsttag-1.0.so.0': 'libgstreamer-plugins-base1.0-0',
|
'libgsttag-1.0.so.0': 'libgstreamer-plugins-base1.0-0',
|
||||||
'libgstvideo-1.0.so.0': 'libgstreamer-plugins-base1.0-0',
|
'libgstvideo-1.0.so.0': 'libgstreamer-plugins-base1.0-0',
|
||||||
'libgtk-3.so.0': 'libgtk-3-0',
|
'libgtk-3.so.0': 'libgtk-3-0',
|
||||||
|
'libgudev-1.0.so.0': 'libgudev-1.0-0',
|
||||||
'libharfbuzz-icu.so.0': 'libharfbuzz-icu0',
|
'libharfbuzz-icu.so.0': 'libharfbuzz-icu0',
|
||||||
'libharfbuzz.so.0': 'libharfbuzz0b',
|
'libharfbuzz.so.0': 'libharfbuzz0b',
|
||||||
'libhyphen.so.0': 'libhyphen0',
|
'libhyphen.so.0': 'libhyphen0',
|
||||||
|
'libicui18n.so.67': 'libicu67',
|
||||||
|
'libicuuc.so.67': 'libicu67',
|
||||||
'libjavascriptcoregtk-4.0.so.18': 'libjavascriptcoregtk-4.0-18',
|
'libjavascriptcoregtk-4.0.so.18': 'libjavascriptcoregtk-4.0-18',
|
||||||
'libjpeg.so.62': 'libjpeg62-turbo',
|
'libjpeg.so.62': 'libjpeg62-turbo',
|
||||||
'liblcms2.so.2': 'liblcms2-2',
|
'liblcms2.so.2': 'liblcms2-2',
|
||||||
'libm.so.6': 'libc6',
|
|
||||||
'libmanette-0.2.so.0': 'libmanette-0.2-0',
|
'libmanette-0.2.so.0': 'libmanette-0.2-0',
|
||||||
'libnotify.so.4': 'libnotify4',
|
'libnotify.so.4': 'libnotify4',
|
||||||
'libnspr4.so': 'libnspr4',
|
'libnspr4.so': 'libnspr4',
|
||||||
|
|
@ -780,19 +833,15 @@ export const deps: any = {
|
||||||
'libpango-1.0.so.0': 'libpango-1.0-0',
|
'libpango-1.0.so.0': 'libpango-1.0-0',
|
||||||
'libpangocairo-1.0.so.0': 'libpangocairo-1.0-0',
|
'libpangocairo-1.0.so.0': 'libpangocairo-1.0-0',
|
||||||
'libpng16.so.16': 'libpng16-16',
|
'libpng16.so.16': 'libpng16-16',
|
||||||
'libpthread.so.0': 'libc6',
|
'libproxy.so.1': 'libproxy1v5',
|
||||||
'libsecret-1.so.0': 'libsecret-1-0',
|
'libsecret-1.so.0': 'libsecret-1-0',
|
||||||
'libsmime3.so': 'libnss3',
|
'libsmime3.so': 'libnss3',
|
||||||
'libsoup-2.4.so.1': 'libsoup2.4-1',
|
'libsoup-2.4.so.1': 'libsoup2.4-1',
|
||||||
'libsqlite3.so.0': 'libsqlite3-0',
|
|
||||||
'libstdc++.so.6': 'libstdc++6',
|
|
||||||
'libsystemd.so.0': 'libsystemd0',
|
|
||||||
'libtasn1.so.6': 'libtasn1-6',
|
|
||||||
'libvpx.so.6': 'libvpx6',
|
|
||||||
'libwayland-client.so.0': 'libwayland-client0',
|
'libwayland-client.so.0': 'libwayland-client0',
|
||||||
'libwayland-egl.so.1': 'libwayland-egl1',
|
'libwayland-egl.so.1': 'libwayland-egl1',
|
||||||
'libwayland-server.so.0': 'libwayland-server0',
|
'libwayland-server.so.0': 'libwayland-server0',
|
||||||
'libwebkit2gtk-4.0.so.37': 'libwebkit2gtk-4.0-37',
|
'libwebkit2gtk-4.0.so.37': 'libwebkit2gtk-4.0-37',
|
||||||
|
'libwebp.so.6': 'libwebp6',
|
||||||
'libwebpdemux.so.2': 'libwebpdemux2',
|
'libwebpdemux.so.2': 'libwebpdemux2',
|
||||||
'libwoff2dec.so.1.0.2': 'libwoff1',
|
'libwoff2dec.so.1.0.2': 'libwoff1',
|
||||||
'libwpe-1.0.so.1': 'libwpe-1.0-1',
|
'libwpe-1.0.so.1': 'libwpe-1.0-1',
|
||||||
|
|
@ -813,10 +862,8 @@ export const deps: any = {
|
||||||
'libXrandr.so.2': 'libxrandr2',
|
'libXrandr.so.2': 'libxrandr2',
|
||||||
'libXrender.so.1': 'libxrender1',
|
'libXrender.so.1': 'libxrender1',
|
||||||
'libxslt.so.1': 'libxslt1.1',
|
'libxslt.so.1': 'libxslt1.1',
|
||||||
'libXt.so.6': 'libxt6',
|
|
||||||
'libXtst.so.6': 'libxtst6',
|
'libXtst.so.6': 'libxtst6',
|
||||||
'libz.so.1': 'libzadc4',
|
}
|
||||||
},
|
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -32,8 +32,8 @@ import { kPageProxyMessageReceived, WKConnection, WKSession } from './wkConnecti
|
||||||
import { WKPage } from './wkPage';
|
import { WKPage } from './wkPage';
|
||||||
import { kBrowserClosedError } from '../../common/errors';
|
import { kBrowserClosedError } from '../../common/errors';
|
||||||
|
|
||||||
const DEFAULT_USER_AGENT = 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/15.4 Safari/605.1.15';
|
const DEFAULT_USER_AGENT = 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/16.0 Safari/605.1.15';
|
||||||
const BROWSER_VERSION = '15.4';
|
const BROWSER_VERSION = '16.0';
|
||||||
|
|
||||||
export class WKBrowser extends Browser {
|
export class WKBrowser extends Browser {
|
||||||
private readonly _connection: WKConnection;
|
private readonly _connection: WKConnection;
|
||||||
|
|
|
||||||
12
packages/playwright-core/types/types.d.ts
vendored
12
packages/playwright-core/types/types.d.ts
vendored
|
|
@ -10379,7 +10379,9 @@ export interface BrowserType<Unused = {}> {
|
||||||
*/
|
*/
|
||||||
connectOverCDP(options: ConnectOverCDPOptions & { wsEndpoint?: string }): Promise<Browser>;
|
connectOverCDP(options: ConnectOverCDPOptions & { wsEndpoint?: string }): Promise<Browser>;
|
||||||
/**
|
/**
|
||||||
* This method attaches Playwright to an existing browser instance.
|
* This method attaches Playwright to an existing browser instance. When connecting to another browser launched via
|
||||||
|
* `BrowserType.launchServer` in Node.js, the major and minor version needs to match the client version (1.2.3 → is
|
||||||
|
* compatible with 1.2.x).
|
||||||
* @param wsEndpoint A browser websocket endpoint to connect to.
|
* @param wsEndpoint A browser websocket endpoint to connect to.
|
||||||
* @param options
|
* @param options
|
||||||
*/
|
*/
|
||||||
|
|
@ -10391,7 +10393,9 @@ export interface BrowserType<Unused = {}> {
|
||||||
* @deprecated
|
* @deprecated
|
||||||
*/
|
*/
|
||||||
/**
|
/**
|
||||||
* This method attaches Playwright to an existing browser instance.
|
* This method attaches Playwright to an existing browser instance. When connecting to another browser launched via
|
||||||
|
* `BrowserType.launchServer` in Node.js, the major and minor version needs to match the client version (1.2.3 → is
|
||||||
|
* compatible with 1.2.x).
|
||||||
* @param wsEndpoint A browser websocket endpoint to connect to.
|
* @param wsEndpoint A browser websocket endpoint to connect to.
|
||||||
* @param options
|
* @param options
|
||||||
*/
|
*/
|
||||||
|
|
@ -10835,7 +10839,9 @@ export interface BrowserType<Unused = {}> {
|
||||||
}): Promise<BrowserContext>;
|
}): Promise<BrowserContext>;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns the browser app instance.
|
* Returns the browser app instance. You can connect to it via
|
||||||
|
* [browserType.connect(wsEndpoint[, options])](https://playwright.dev/docs/api/class-browsertype#browser-type-connect),
|
||||||
|
* which requires the major/minor client/server version to match (1.2.3 → is compatible with 1.2.x).
|
||||||
*
|
*
|
||||||
* Launches browser server that client can connect to. An example of launching a browser executable and connecting to it
|
* Launches browser server that client can connect to. An example of launching a browser executable and connecting to it
|
||||||
* later:
|
* later:
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,6 @@
|
||||||
{
|
{
|
||||||
"name": "@playwright/experimental-ct-react",
|
"name": "@playwright/experimental-ct-react",
|
||||||
"version": "1.24.0-next",
|
"version": "1.24.2",
|
||||||
"description": "Playwright Component Testing for React",
|
"description": "Playwright Component Testing for React",
|
||||||
"repository": "github:Microsoft/playwright",
|
"repository": "github:Microsoft/playwright",
|
||||||
"homepage": "https://playwright.dev",
|
"homepage": "https://playwright.dev",
|
||||||
|
|
@ -27,7 +27,7 @@
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@vitejs/plugin-react": "^1.0.7",
|
"@vitejs/plugin-react": "^1.0.7",
|
||||||
"@playwright/test": "1.24.0-next",
|
"@playwright/test": "1.24.2",
|
||||||
"vite": "^2.9.5"
|
"vite": "^2.9.5"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
4
packages/playwright-ct-svelte/index.d.ts
vendored
4
packages/playwright-ct-svelte/index.d.ts
vendored
|
|
@ -39,13 +39,13 @@ interface ComponentFixtures {
|
||||||
props?: { [key: string]: any },
|
props?: { [key: string]: any },
|
||||||
slots?: { [key: string]: any },
|
slots?: { [key: string]: any },
|
||||||
on?: { [key: string]: Function },
|
on?: { [key: string]: Function },
|
||||||
hooksConfig: any,
|
hooksConfig?: any,
|
||||||
}): Promise<Locator>;
|
}): Promise<Locator>;
|
||||||
mount<Props>(component: any, options: {
|
mount<Props>(component: any, options: {
|
||||||
props: Props,
|
props: Props,
|
||||||
slots?: { [key: string]: any },
|
slots?: { [key: string]: any },
|
||||||
on?: { [key: string]: Function },
|
on?: { [key: string]: Function },
|
||||||
hooksConfig: any,
|
hooksConfig?: any,
|
||||||
}): Promise<Locator>;
|
}): Promise<Locator>;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,6 @@
|
||||||
{
|
{
|
||||||
"name": "@playwright/experimental-ct-svelte",
|
"name": "@playwright/experimental-ct-svelte",
|
||||||
"version": "1.24.0-next",
|
"version": "1.24.2",
|
||||||
"description": "Playwright Component Testing for Svelte",
|
"description": "Playwright Component Testing for Svelte",
|
||||||
"repository": "github:Microsoft/playwright",
|
"repository": "github:Microsoft/playwright",
|
||||||
"homepage": "https://playwright.dev",
|
"homepage": "https://playwright.dev",
|
||||||
|
|
@ -26,8 +26,8 @@
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@sveltejs/vite-plugin-svelte": "^1.0.0-next.30",
|
"@sveltejs/vite-plugin-svelte": "^1.0.1",
|
||||||
"@playwright/test": "1.24.0-next",
|
"@playwright/test": "1.24.2",
|
||||||
"vite": "^2.9.5"
|
"vite": "^3.0.0"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,6 @@
|
||||||
{
|
{
|
||||||
"name": "@playwright/experimental-ct-vue",
|
"name": "@playwright/experimental-ct-vue",
|
||||||
"version": "1.24.0-next",
|
"version": "1.24.2",
|
||||||
"description": "Playwright Component Testing for Vue",
|
"description": "Playwright Component Testing for Vue",
|
||||||
"repository": "github:Microsoft/playwright",
|
"repository": "github:Microsoft/playwright",
|
||||||
"homepage": "https://playwright.dev",
|
"homepage": "https://playwright.dev",
|
||||||
|
|
@ -27,7 +27,7 @@
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@vitejs/plugin-vue": "^2.3.1",
|
"@vitejs/plugin-vue": "^2.3.1",
|
||||||
"@playwright/test": "1.24.0-next",
|
"@playwright/test": "1.24.2",
|
||||||
"vite": "^2.9.5"
|
"vite": "^2.9.5"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
4
packages/playwright-ct-vue2/index.d.ts
vendored
4
packages/playwright-ct-vue2/index.d.ts
vendored
|
|
@ -40,13 +40,13 @@ export interface ComponentFixtures {
|
||||||
props?: { [key: string]: any },
|
props?: { [key: string]: any },
|
||||||
slots?: { [key: string]: any },
|
slots?: { [key: string]: any },
|
||||||
on?: { [key: string]: Function },
|
on?: { [key: string]: Function },
|
||||||
hooksConfig: any,
|
hooksConfig?: any,
|
||||||
}): Promise<Locator>;
|
}): Promise<Locator>;
|
||||||
mount<Props>(component: any, options: {
|
mount<Props>(component: any, options: {
|
||||||
props: Props,
|
props: Props,
|
||||||
slots?: { [key: string]: any },
|
slots?: { [key: string]: any },
|
||||||
on?: { [key: string]: Function },
|
on?: { [key: string]: Function },
|
||||||
hooksConfig: any,
|
hooksConfig?: any,
|
||||||
}): Promise<Locator>;
|
}): Promise<Locator>;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,6 @@
|
||||||
{
|
{
|
||||||
"name": "@playwright/experimental-ct-vue2",
|
"name": "@playwright/experimental-ct-vue2",
|
||||||
"version": "1.24.0-next",
|
"version": "1.24.2",
|
||||||
"description": "Playwright Component Testing for Vue2",
|
"description": "Playwright Component Testing for Vue2",
|
||||||
"repository": "github:Microsoft/playwright",
|
"repository": "github:Microsoft/playwright",
|
||||||
"homepage": "https://playwright.dev",
|
"homepage": "https://playwright.dev",
|
||||||
|
|
@ -26,7 +26,7 @@
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@playwright/test": "1.24.0-next",
|
"@playwright/test": "1.24.2",
|
||||||
"vite": "^2.9.5",
|
"vite": "^2.9.5",
|
||||||
"vite-plugin-vue2": "^2.0.1"
|
"vite-plugin-vue2": "^2.0.1"
|
||||||
},
|
},
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,6 @@
|
||||||
{
|
{
|
||||||
"name": "playwright-firefox",
|
"name": "playwright-firefox",
|
||||||
"version": "1.24.0-next",
|
"version": "1.24.2",
|
||||||
"description": "A high-level API to automate Firefox",
|
"description": "A high-level API to automate Firefox",
|
||||||
"repository": "github:Microsoft/playwright",
|
"repository": "github:Microsoft/playwright",
|
||||||
"homepage": "https://playwright.dev",
|
"homepage": "https://playwright.dev",
|
||||||
|
|
@ -27,6 +27,6 @@
|
||||||
"install": "node install.js"
|
"install": "node install.js"
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"playwright-core": "1.24.0-next"
|
"playwright-core": "1.24.2"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,6 @@
|
||||||
{
|
{
|
||||||
"name": "@playwright/test",
|
"name": "@playwright/test",
|
||||||
"version": "1.24.0-next",
|
"version": "1.24.2",
|
||||||
"description": "A high-level API to automate web browsers",
|
"description": "A high-level API to automate web browsers",
|
||||||
"repository": "github:Microsoft/playwright",
|
"repository": "github:Microsoft/playwright",
|
||||||
"homepage": "https://playwright.dev",
|
"homepage": "https://playwright.dev",
|
||||||
|
|
@ -33,6 +33,6 @@
|
||||||
"license": "Apache-2.0",
|
"license": "Apache-2.0",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@types/node": "*",
|
"@types/node": "*",
|
||||||
"playwright-core": "1.24.0-next"
|
"playwright-core": "1.24.2"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -29,7 +29,6 @@ import {
|
||||||
toContainText,
|
toContainText,
|
||||||
toHaveAttribute,
|
toHaveAttribute,
|
||||||
toHaveClass,
|
toHaveClass,
|
||||||
toContainClass,
|
|
||||||
toHaveCount,
|
toHaveCount,
|
||||||
toHaveCSS,
|
toHaveCSS,
|
||||||
toHaveId,
|
toHaveId,
|
||||||
|
|
@ -135,7 +134,6 @@ const customMatchers = {
|
||||||
toContainText,
|
toContainText,
|
||||||
toHaveAttribute,
|
toHaveAttribute,
|
||||||
toHaveClass,
|
toHaveClass,
|
||||||
toContainClass,
|
|
||||||
toHaveCount,
|
toHaveCount,
|
||||||
toHaveCSS,
|
toHaveCSS,
|
||||||
toHaveId,
|
toHaveId,
|
||||||
|
|
|
||||||
|
|
@ -18,7 +18,9 @@ import fs from 'fs';
|
||||||
import url from 'url';
|
import url from 'url';
|
||||||
import { transformHook, resolveHook, belongsToNodeModules } from './transform';
|
import { transformHook, resolveHook, belongsToNodeModules } from './transform';
|
||||||
|
|
||||||
async function resolve(specifier: string, context: { parentURL: string }, defaultResolve: any) {
|
// Node < 18.6: defaultResolve takes 3 arguments.
|
||||||
|
// Node >= 18.6: nextResolve from the chain takes 2 arguments.
|
||||||
|
async function resolve(specifier: string, context: { parentURL?: string }, defaultResolve: Function) {
|
||||||
if (context.parentURL && context.parentURL.startsWith('file://')) {
|
if (context.parentURL && context.parentURL.startsWith('file://')) {
|
||||||
const filename = url.fileURLToPath(context.parentURL);
|
const filename = url.fileURLToPath(context.parentURL);
|
||||||
const resolved = resolveHook(filename, specifier);
|
const resolved = resolveHook(filename, specifier);
|
||||||
|
|
@ -28,7 +30,9 @@ async function resolve(specifier: string, context: { parentURL: string }, defaul
|
||||||
return defaultResolve(specifier, context, defaultResolve);
|
return defaultResolve(specifier, context, defaultResolve);
|
||||||
}
|
}
|
||||||
|
|
||||||
async function load(moduleUrl: string, context: any, defaultLoad: any) {
|
// Node < 18.6: defaultLoad takes 3 arguments.
|
||||||
|
// Node >= 18.6: nextLoad from the chain takes 2 arguments.
|
||||||
|
async function load(moduleUrl: string, context: { format?: string }, defaultLoad: Function) {
|
||||||
// Bail out for wasm, json, etc.
|
// Bail out for wasm, json, etc.
|
||||||
// non-js files have context.format === undefined
|
// non-js files have context.format === undefined
|
||||||
if (context.format !== 'commonjs' && context.format !== 'module' && context.format !== undefined)
|
if (context.format !== 'commonjs' && context.format !== 'module' && context.format !== undefined)
|
||||||
|
|
@ -49,7 +53,8 @@ async function load(moduleUrl: string, context: any, defaultLoad: any) {
|
||||||
const code = fs.readFileSync(filename, 'utf-8');
|
const code = fs.readFileSync(filename, 'utf-8');
|
||||||
const source = transformHook(code, filename, moduleUrl);
|
const source = transformHook(code, filename, moduleUrl);
|
||||||
// Output format is always the same as input format, if it was unknown, we always report modules.
|
// Output format is always the same as input format, if it was unknown, we always report modules.
|
||||||
return { format: context.format || 'module', source };
|
// shortCurcuit is required by Node >= 18.6 to designate no more loaders should be called.
|
||||||
|
return { format: context.format || 'module', source, shortCircuit: true };
|
||||||
}
|
}
|
||||||
|
|
||||||
module.exports = { resolve, load };
|
module.exports = { resolve, load };
|
||||||
|
|
|
||||||
|
|
@ -16,7 +16,7 @@
|
||||||
|
|
||||||
import { installTransform } from './transform';
|
import { installTransform } from './transform';
|
||||||
import type { Config, Project, ReporterDescription, FullProjectInternal, FullConfigInternal, Fixtures, FixturesWithLocation } from './types';
|
import type { Config, Project, ReporterDescription, FullProjectInternal, FullConfigInternal, Fixtures, FixturesWithLocation } from './types';
|
||||||
import { getPackageJsonPath, filterUndefinedFixtures, errorWithFile } from './util';
|
import { getPackageJsonPath, mergeObjects, errorWithFile } from './util';
|
||||||
import { setCurrentlyLoadingFileSuite } from './globals';
|
import { setCurrentlyLoadingFileSuite } from './globals';
|
||||||
import { Suite, type TestCase } from './test';
|
import { Suite, type TestCase } from './test';
|
||||||
import type { SerializedLoaderData } from './ipc';
|
import type { SerializedLoaderData } from './ipc';
|
||||||
|
|
@ -98,7 +98,7 @@ export class Loader {
|
||||||
throw new Error(`Cannot use --browser option when configuration file defines projects. Specify browserName in the projects instead.`);
|
throw new Error(`Cannot use --browser option when configuration file defines projects. Specify browserName in the projects instead.`);
|
||||||
config.projects = takeFirst(this._configCLIOverrides.projects, config.projects as any);
|
config.projects = takeFirst(this._configCLIOverrides.projects, config.projects as any);
|
||||||
config.workers = takeFirst(this._configCLIOverrides.workers, config.workers);
|
config.workers = takeFirst(this._configCLIOverrides.workers, config.workers);
|
||||||
config.use = { ...filterUndefinedFixtures(config.use), ...filterUndefinedFixtures(this._configCLIOverrides.use) };
|
config.use = mergeObjects(config.use, this._configCLIOverrides.use);
|
||||||
for (const project of config.projects || [])
|
for (const project of config.projects || [])
|
||||||
this._applyCLIOverridesToProject(project);
|
this._applyCLIOverridesToProject(project);
|
||||||
|
|
||||||
|
|
@ -232,7 +232,7 @@ export class Loader {
|
||||||
projectConfig.repeatEach = takeFirst(this._configCLIOverrides.repeatEach, projectConfig.repeatEach);
|
projectConfig.repeatEach = takeFirst(this._configCLIOverrides.repeatEach, projectConfig.repeatEach);
|
||||||
projectConfig.retries = takeFirst(this._configCLIOverrides.retries, projectConfig.retries);
|
projectConfig.retries = takeFirst(this._configCLIOverrides.retries, projectConfig.retries);
|
||||||
projectConfig.timeout = takeFirst(this._configCLIOverrides.timeout, projectConfig.timeout);
|
projectConfig.timeout = takeFirst(this._configCLIOverrides.timeout, projectConfig.timeout);
|
||||||
projectConfig.use = { ...filterUndefinedFixtures(projectConfig.use), ...filterUndefinedFixtures(this._configCLIOverrides.use) };
|
projectConfig.use = mergeObjects(projectConfig.use, this._configCLIOverrides.use);
|
||||||
}
|
}
|
||||||
|
|
||||||
private _resolveProject(config: Config, fullConfig: FullConfigInternal, projectConfig: Project, throwawayArtifactsPath: string): FullProjectInternal {
|
private _resolveProject(config: Config, fullConfig: FullConfigInternal, projectConfig: Project, throwawayArtifactsPath: string): FullProjectInternal {
|
||||||
|
|
@ -271,7 +271,7 @@ export class Loader {
|
||||||
testIgnore: takeFirst(projectConfig.testIgnore, config.testIgnore, []),
|
testIgnore: takeFirst(projectConfig.testIgnore, config.testIgnore, []),
|
||||||
testMatch: takeFirst(projectConfig.testMatch, config.testMatch, '**/?(*.)@(spec|test).*'),
|
testMatch: takeFirst(projectConfig.testMatch, config.testMatch, '**/?(*.)@(spec|test).*'),
|
||||||
timeout: takeFirst(projectConfig.timeout, config.timeout, defaultTimeout),
|
timeout: takeFirst(projectConfig.timeout, config.timeout, defaultTimeout),
|
||||||
use: { ...filterUndefinedFixtures(config.use), ...filterUndefinedFixtures(projectConfig.use) },
|
use: mergeObjects(config.use, projectConfig.use),
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -164,25 +164,6 @@ export function toHaveClass(
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export function toContainClass(
|
|
||||||
this: ReturnType<Expect['getState']>,
|
|
||||||
locator: LocatorEx,
|
|
||||||
expected: string | string[],
|
|
||||||
options?: { timeout?: number, ignoreCase?: boolean },
|
|
||||||
) {
|
|
||||||
if (Array.isArray(expected)) {
|
|
||||||
return toEqual.call(this, 'toContainClass', locator, 'Locator', async (isNot, timeout, customStackTrace) => {
|
|
||||||
const expectedText = toExpectedTextValues(expected, { ignoreCase: options?.ignoreCase });
|
|
||||||
return await locator._expect(customStackTrace, 'to.contain.class.array', { expectedText, isNot, timeout });
|
|
||||||
}, expected, options);
|
|
||||||
} else {
|
|
||||||
return toMatchText.call(this, 'toContainClass', locator, 'Locator', async (isNot, timeout, customStackTrace) => {
|
|
||||||
const expectedText = toExpectedTextValues([expected], { ignoreCase: options?.ignoreCase });
|
|
||||||
return await locator._expect(customStackTrace, 'to.contain.class', { expectedText, isNot, timeout });
|
|
||||||
}, expected, options);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
export function toHaveCount(
|
export function toHaveCount(
|
||||||
this: ReturnType<Expect['getState']>,
|
this: ReturnType<Expect['getState']>,
|
||||||
locator: LocatorEx,
|
locator: LocatorEx,
|
||||||
|
|
|
||||||
|
|
@ -473,12 +473,17 @@ export class Runner {
|
||||||
}
|
}
|
||||||
|
|
||||||
// The do global setup.
|
// The do global setup.
|
||||||
if (!sigintWatcher.hadSignal() && config.globalSetup) {
|
if (!sigintWatcher.hadSignal()) {
|
||||||
|
if (config.globalSetup) {
|
||||||
const hook = await this._loader.loadGlobalHook(config.globalSetup, 'globalSetup');
|
const hook = await this._loader.loadGlobalHook(config.globalSetup, 'globalSetup');
|
||||||
await Promise.race([
|
await Promise.race([
|
||||||
Promise.resolve().then(() => hook(this._loader.fullConfig())).then((r: any) => globalSetupResult = r || '<noop>'),
|
Promise.resolve().then(() => hook(this._loader.fullConfig())).then((r: any) => globalSetupResult = r || '<noop>'),
|
||||||
sigintWatcher.promise(),
|
sigintWatcher.promise(),
|
||||||
]);
|
]);
|
||||||
|
} else {
|
||||||
|
// Make sure we run globalTeardown.
|
||||||
|
globalSetupResult = '<noop>';
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}, result);
|
}, result);
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -19,7 +19,7 @@ import { currentlyLoadingFileSuite, currentTestInfo, setCurrentlyLoadingFileSuit
|
||||||
import { TestCase, Suite } from './test';
|
import { TestCase, Suite } from './test';
|
||||||
import { wrapFunctionWithLocation } from './transform';
|
import { wrapFunctionWithLocation } from './transform';
|
||||||
import type { Fixtures, FixturesWithLocation, Location, TestType } from './types';
|
import type { Fixtures, FixturesWithLocation, Location, TestType } from './types';
|
||||||
import { errorWithLocation, filterUndefinedFixtures, serializeError } from './util';
|
import { errorWithLocation, serializeError } from './util';
|
||||||
|
|
||||||
const testTypeSymbol = Symbol('testType');
|
const testTypeSymbol = Symbol('testType');
|
||||||
|
|
||||||
|
|
@ -197,7 +197,7 @@ export class TestTypeImpl {
|
||||||
|
|
||||||
private _use(location: Location, fixtures: Fixtures) {
|
private _use(location: Location, fixtures: Fixtures) {
|
||||||
const suite = this._ensureCurrentSuite(location, `test.use()`);
|
const suite = this._ensureCurrentSuite(location, `test.use()`);
|
||||||
suite._use.push({ fixtures: filterUndefinedFixtures(fixtures) , location });
|
suite._use.push({ fixtures, location });
|
||||||
}
|
}
|
||||||
|
|
||||||
private async _step(location: Location, title: string, body: () => Promise<void>): Promise<void> {
|
private async _step(location: Location, title: string, body: () => Promise<void>): Promise<void> {
|
||||||
|
|
@ -223,7 +223,7 @@ export class TestTypeImpl {
|
||||||
private _extend(location: Location, fixtures: Fixtures) {
|
private _extend(location: Location, fixtures: Fixtures) {
|
||||||
if ((fixtures as any)[testTypeSymbol])
|
if ((fixtures as any)[testTypeSymbol])
|
||||||
throw new Error(`test.extend() accepts fixtures object, not a test object.\nDid you mean to call test._extendTest()?`);
|
throw new Error(`test.extend() accepts fixtures object, not a test object.\nDid you mean to call test._extendTest()?`);
|
||||||
const fixturesWithLocation: FixturesWithLocation = { fixtures: filterUndefinedFixtures(fixtures), location };
|
const fixturesWithLocation: FixturesWithLocation = { fixtures, location };
|
||||||
return new TestTypeImpl([...this.fixtures, fixturesWithLocation]).test;
|
return new TestTypeImpl([...this.fixtures, fixturesWithLocation]).test;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -153,11 +153,15 @@ export function createTitleMatcher(patterns: RegExp | RegExp[]): Matcher {
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
export function filterUndefinedFixtures<T extends object>(o: T | undefined): T {
|
export function mergeObjects<A extends object, B extends object>(a: A | undefined | void, b: B | undefined | void): A & B {
|
||||||
// We don't want "undefined" values to actually mean "undefined",
|
const result = { ...a } as any;
|
||||||
// but rather "no opinion about this option", like in all other
|
if (!Object.is(b, undefined)) {
|
||||||
// places in the config.
|
for (const [name, value] of Object.entries(b as B)) {
|
||||||
return Object.fromEntries(Object.entries(o || {}).filter(entry => !Object.is(entry[1], undefined))) as any as T;
|
if (!Object.is(value, undefined))
|
||||||
|
result[name] = value;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return result as any;
|
||||||
}
|
}
|
||||||
|
|
||||||
export function forceRegExp(pattern: string): RegExp {
|
export function forceRegExp(pattern: string): RegExp {
|
||||||
|
|
|
||||||
47
packages/playwright-test/types/test.d.ts
vendored
47
packages/playwright-test/types/test.d.ts
vendored
|
|
@ -487,7 +487,7 @@ interface TestConfig {
|
||||||
* ```
|
* ```
|
||||||
*
|
*
|
||||||
*/
|
*/
|
||||||
webServer?: TestConfigWebServer;
|
webServer?: TestConfigWebServer | TestConfigWebServer[];
|
||||||
/**
|
/**
|
||||||
* Configuration for the `expect` assertion library. Learn more about [various timeouts](https://playwright.dev/docs/test-timeouts).
|
* Configuration for the `expect` assertion library. Learn more about [various timeouts](https://playwright.dev/docs/test-timeouts).
|
||||||
*
|
*
|
||||||
|
|
@ -3327,46 +3327,6 @@ interface LocatorAssertions {
|
||||||
timeout?: number;
|
timeout?: number;
|
||||||
}): Promise<void>;
|
}): Promise<void>;
|
||||||
|
|
||||||
/**
|
|
||||||
* Ensures the [Locator] points to an element that contains the given CSS class (or multiple). In contrast to
|
|
||||||
* [locatorAssertions.toHaveClass(expected[, options])](https://playwright.dev/docs/api/class-locatorassertions#locator-assertions-to-have-class)
|
|
||||||
* which requires that the [Locator] has exactly the provided classes, `toContainClass` verifies that the [Locator] has a
|
|
||||||
* subset (or all) of the given CSS classes.
|
|
||||||
*
|
|
||||||
* ```html
|
|
||||||
* <div class='foo bar baz' id='component'>
|
|
||||||
* <div class='item alice'></div>
|
|
||||||
* <div class='item bob'></div>
|
|
||||||
* </div>
|
|
||||||
* ```
|
|
||||||
*
|
|
||||||
* ```js
|
|
||||||
* const locator = page.locator('#component');
|
|
||||||
* await expect(locator).toContainClass('bar baz'); // pass, both classes are on element
|
|
||||||
* await expect(locator).toContainClass('ba'); // fail, element has no 'ba' class
|
|
||||||
*
|
|
||||||
* const itemLocator = page.locator('#component .item');
|
|
||||||
* await expect(itemLocator).toContainClass(['alice', 'bob']); // pass, first element has alice, second bob
|
|
||||||
* await expect(itemLocator).toContainClass(['alice', 'bob carl']); // no carl class found on second item element
|
|
||||||
* await expect(itemLocator).toContainClass(['alice', 'bob', 'foobar']); // we expect 3 elements with the item class, but there are only 2
|
|
||||||
* ```
|
|
||||||
*
|
|
||||||
* Note that locator must point to a single element when passing a string or to multiple elements when passing an array.
|
|
||||||
* @param expected Expected classnames, whitespace separated. When passing an array, the given classes must be present on the locator elements.
|
|
||||||
* @param options
|
|
||||||
*/
|
|
||||||
toContainClass(expected: string|Array<string>, options?: {
|
|
||||||
/**
|
|
||||||
* Whether to perform case-insensitive match.
|
|
||||||
*/
|
|
||||||
ignoreCase?: boolean;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Time to retry the assertion for. Defaults to `timeout` in `TestConfig.expect`.
|
|
||||||
*/
|
|
||||||
timeout?: number;
|
|
||||||
}): Promise<void>;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Ensures the [Locator] points to an element that contains the given text. You can use regular expressions for the value
|
* Ensures the [Locator] points to an element that contains the given text. You can use regular expressions for the value
|
||||||
* as well.
|
* as well.
|
||||||
|
|
@ -3426,8 +3386,7 @@ interface LocatorAssertions {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Ensures the [Locator] points to an element with given CSS classes. This needs to be a full match or using a relaxed
|
* Ensures the [Locator] points to an element with given CSS classes. This needs to be a full match or using a relaxed
|
||||||
* regular expression. For matching partial class names, use
|
* regular expression.
|
||||||
* [locatorAssertions.toContainClass(expected[, options])](https://playwright.dev/docs/api/class-locatorassertions#locator-assertions-to-contain-class).
|
|
||||||
*
|
*
|
||||||
* ```html
|
* ```html
|
||||||
* <div class='selected row' id='component'></div>
|
* <div class='selected row' id='component'></div>
|
||||||
|
|
@ -4028,7 +3987,7 @@ interface PageAssertions {
|
||||||
* await expect(page).toHaveURL(/.*checkout/);
|
* await expect(page).toHaveURL(/.*checkout/);
|
||||||
* ```
|
* ```
|
||||||
*
|
*
|
||||||
* @param urlOrRegExp Expected substring or RegExp.
|
* @param urlOrRegExp Expected URL string or RegExp.
|
||||||
* @param options
|
* @param options
|
||||||
*/
|
*/
|
||||||
toHaveURL(urlOrRegExp: string|RegExp, options?: {
|
toHaveURL(urlOrRegExp: string|RegExp, options?: {
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,6 @@
|
||||||
{
|
{
|
||||||
"name": "playwright-webkit",
|
"name": "playwright-webkit",
|
||||||
"version": "1.24.0-next",
|
"version": "1.24.2",
|
||||||
"description": "A high-level API to automate WebKit",
|
"description": "A high-level API to automate WebKit",
|
||||||
"repository": "github:Microsoft/playwright",
|
"repository": "github:Microsoft/playwright",
|
||||||
"homepage": "https://playwright.dev",
|
"homepage": "https://playwright.dev",
|
||||||
|
|
@ -27,6 +27,6 @@
|
||||||
"install": "node install.js"
|
"install": "node install.js"
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"playwright-core": "1.24.0-next"
|
"playwright-core": "1.24.2"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,6 @@
|
||||||
{
|
{
|
||||||
"name": "playwright",
|
"name": "playwright",
|
||||||
"version": "1.24.0-next",
|
"version": "1.24.2",
|
||||||
"description": "A high-level API to automate web browsers",
|
"description": "A high-level API to automate web browsers",
|
||||||
"repository": "github:Microsoft/playwright",
|
"repository": "github:Microsoft/playwright",
|
||||||
"homepage": "https://playwright.dev",
|
"homepage": "https://playwright.dev",
|
||||||
|
|
@ -27,6 +27,6 @@
|
||||||
"install": "node install.js"
|
"install": "node install.js"
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"playwright-core": "1.24.0-next"
|
"playwright-core": "1.24.2"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
File diff suppressed because it is too large
Load diff
|
|
@ -391,3 +391,29 @@ test('should work with undefined values and base', async ({ runInlineTest }) =>
|
||||||
expect(result.exitCode).toBe(0);
|
expect(result.exitCode).toBe(0);
|
||||||
expect(result.passed).toBe(1);
|
expect(result.passed).toBe(1);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
test('should have correct types for the config', async ({ runTSC }) => {
|
||||||
|
const result = await runTSC({
|
||||||
|
'playwright.config.ts': `
|
||||||
|
import type { PlaywrightTestConfig } from '@playwright/test';
|
||||||
|
|
||||||
|
const config: PlaywrightTestConfig = {
|
||||||
|
webServer: [
|
||||||
|
{
|
||||||
|
command: 'echo 123',
|
||||||
|
env: { PORT: '123' },
|
||||||
|
port: 123,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
command: 'echo 123',
|
||||||
|
env: { NODE_ENV: 'test' },
|
||||||
|
port: 8082,
|
||||||
|
},
|
||||||
|
],
|
||||||
|
};
|
||||||
|
|
||||||
|
export default config;
|
||||||
|
`
|
||||||
|
});
|
||||||
|
expect(result.exitCode).toBe(0);
|
||||||
|
});
|
||||||
|
|
|
||||||
|
|
@ -48,6 +48,29 @@ test('globalSetup and globalTeardown should work', async ({ runInlineTest }) =>
|
||||||
expect(output).toContain('teardown=42');
|
expect(output).toContain('teardown=42');
|
||||||
});
|
});
|
||||||
|
|
||||||
|
test('standalone globalTeardown should work', async ({ runInlineTest }) => {
|
||||||
|
const { results, output } = await runInlineTest({
|
||||||
|
'playwright.config.ts': `
|
||||||
|
import * as path from 'path';
|
||||||
|
module.exports = {
|
||||||
|
globalTeardown: './globalTeardown.ts',
|
||||||
|
};
|
||||||
|
`,
|
||||||
|
'globalTeardown.ts': `
|
||||||
|
module.exports = async () => {
|
||||||
|
console.log('got my teardown');
|
||||||
|
};
|
||||||
|
`,
|
||||||
|
'a.test.js': `
|
||||||
|
const { test } = pwt;
|
||||||
|
test('should work', async ({}, testInfo) => {
|
||||||
|
});
|
||||||
|
`,
|
||||||
|
});
|
||||||
|
expect(results[0].status).toBe('passed');
|
||||||
|
expect(output).toContain('got my teardown');
|
||||||
|
});
|
||||||
|
|
||||||
test('globalTeardown runs after failures', async ({ runInlineTest }) => {
|
test('globalTeardown runs after failures', async ({ runInlineTest }) => {
|
||||||
const { results, output } = await runInlineTest({
|
const { results, output } = await runInlineTest({
|
||||||
'playwright.config.ts': `
|
'playwright.config.ts': `
|
||||||
|
|
|
||||||
|
|
@ -262,60 +262,6 @@ test('should support toHaveClass w/ array', async ({ runInlineTest }) => {
|
||||||
expect(result.exitCode).toBe(1);
|
expect(result.exitCode).toBe(1);
|
||||||
});
|
});
|
||||||
|
|
||||||
test('should support toContainClass', async ({ runInlineTest }) => {
|
|
||||||
const result = await runInlineTest({
|
|
||||||
'a.test.ts': `
|
|
||||||
const { test } = pwt;
|
|
||||||
|
|
||||||
test('pass', async ({ page }) => {
|
|
||||||
await page.setContent(\`
|
|
||||||
<div class="foo bar baz">
|
|
||||||
<span class="alice item baz"></span>
|
|
||||||
<span class="bob item baz"></span>
|
|
||||||
</div>
|
|
||||||
\`);
|
|
||||||
const locator = page.locator('div');
|
|
||||||
await expect(locator).toContainClass('foo');
|
|
||||||
// Leading/trailing whitespace
|
|
||||||
await expect(locator).toContainClass(' foo ');
|
|
||||||
// empty should not pass
|
|
||||||
await expect(locator).not.toContainClass('');
|
|
||||||
await expect(locator).toContainClass('bar');
|
|
||||||
await expect(locator).toContainClass('baz');
|
|
||||||
await expect(locator).toContainClass('foo baz');
|
|
||||||
await expect(locator).toContainClass('baz foo');
|
|
||||||
await expect(locator).not.toContainClass('ba');
|
|
||||||
|
|
||||||
await expect(locator).toContainClass('BAZ FoO', { ignoreCase: true });
|
|
||||||
await expect(locator).not.toContainClass('BAZ');
|
|
||||||
|
|
||||||
const locatorSpan = page.locator('div span');
|
|
||||||
await expect(locatorSpan).toContainClass(['alice baz', 'bob']);
|
|
||||||
await expect(locatorSpan).not.toContainClass(['alice', 'alice']);
|
|
||||||
});
|
|
||||||
|
|
||||||
test('fail', async ({ page }) => {
|
|
||||||
await page.setContent('<div class="bar baz"></div>');
|
|
||||||
const locator = page.locator('div');
|
|
||||||
await expect(locator).toContainClass('foo', { timeout: 1000 });
|
|
||||||
});
|
|
||||||
|
|
||||||
test('fail length mismatch', async ({ page }) => {
|
|
||||||
await page.setContent('<div><span class="alice"></span><span class="bob"></span></div>');
|
|
||||||
const locator = page.locator('div span');
|
|
||||||
await expect(locator).toContainClass('alice', { timeout: 1000 });
|
|
||||||
});
|
|
||||||
`,
|
|
||||||
}, { workers: 1 });
|
|
||||||
const output = stripAnsi(result.output);
|
|
||||||
expect(output).toContain('expect(locator).toContainClass');
|
|
||||||
expect(output).toContain('Expected string: \"foo\"');
|
|
||||||
expect(output).toContain('Received string: \"bar baz\"');
|
|
||||||
expect(result.passed).toBe(1);
|
|
||||||
expect(result.failed).toBe(2);
|
|
||||||
expect(result.exitCode).toBe(1);
|
|
||||||
});
|
|
||||||
|
|
||||||
test('should support toHaveTitle', async ({ runInlineTest }) => {
|
test('should support toHaveTitle', async ({ runInlineTest }) => {
|
||||||
const result = await runInlineTest({
|
const result = await runInlineTest({
|
||||||
'a.test.ts': `
|
'a.test.ts': `
|
||||||
|
|
|
||||||
|
|
@ -226,11 +226,11 @@ test('test._extendTest should print nice message when used as extend', async ({
|
||||||
expect(result.output).toContain('Did you mean to call test.extend() with fixtures instead?');
|
expect(result.output).toContain('Did you mean to call test.extend() with fixtures instead?');
|
||||||
});
|
});
|
||||||
|
|
||||||
test('fixture options should ignore undefined value', async ({ runInlineTest }) => {
|
test('test.use() with undefined should not be ignored', async ({ runInlineTest }) => {
|
||||||
const result = await runInlineTest({
|
const result = await runInlineTest({
|
||||||
'playwright.config.ts': `
|
'playwright.config.ts': `
|
||||||
module.exports = {
|
module.exports = {
|
||||||
use: { option: undefined },
|
use: { option: 'config' },
|
||||||
};
|
};
|
||||||
`,
|
`,
|
||||||
'a.test.js': `
|
'a.test.js': `
|
||||||
|
|
@ -238,13 +238,11 @@ test('fixture options should ignore undefined value', async ({ runInlineTest })
|
||||||
test('test1', async ({ option }) => {
|
test('test1', async ({ option }) => {
|
||||||
console.log('test1-' + option);
|
console.log('test1-' + option);
|
||||||
});
|
});
|
||||||
|
|
||||||
test.describe('', () => {
|
test.describe('', () => {
|
||||||
test.use({ option: 'foo' });
|
test.use({ option: 'foo' });
|
||||||
test('test2', async ({ option }) => {
|
test('test2', async ({ option }) => {
|
||||||
console.log('test2-' + option);
|
console.log('test2-' + option);
|
||||||
});
|
});
|
||||||
|
|
||||||
test.describe('', () => {
|
test.describe('', () => {
|
||||||
test.use({ option: undefined });
|
test.use({ option: undefined });
|
||||||
test('test3', async ({ option }) => {
|
test('test3', async ({ option }) => {
|
||||||
|
|
@ -252,7 +250,6 @@ test('fixture options should ignore undefined value', async ({ runInlineTest })
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
test.extend({ option: undefined })('test4', async ({ option }) => {
|
test.extend({ option: undefined })('test4', async ({ option }) => {
|
||||||
console.log('test4-' + option);
|
console.log('test4-' + option);
|
||||||
});
|
});
|
||||||
|
|
@ -260,8 +257,8 @@ test('fixture options should ignore undefined value', async ({ runInlineTest })
|
||||||
});
|
});
|
||||||
expect(result.exitCode).toBe(0);
|
expect(result.exitCode).toBe(0);
|
||||||
expect(result.passed).toBe(4);
|
expect(result.passed).toBe(4);
|
||||||
expect(result.output).toContain('test1-default');
|
expect(result.output).toContain('test1-config');
|
||||||
expect(result.output).toContain('test2-foo');
|
expect(result.output).toContain('test2-foo');
|
||||||
expect(result.output).toContain('test3-foo');
|
expect(result.output).toContain('test3-undefined');
|
||||||
expect(result.output).toContain('test4-default');
|
expect(result.output).toContain('test4-undefined');
|
||||||
});
|
});
|
||||||
|
|
|
||||||
2
utils/generate_types/overrides-test.d.ts
vendored
2
utils/generate_types/overrides-test.d.ts
vendored
|
|
@ -58,7 +58,7 @@ type LiteralUnion<T extends U, U = string> = T | (U & { zz_IGNORE_ME?: never });
|
||||||
|
|
||||||
interface TestConfig {
|
interface TestConfig {
|
||||||
reporter?: LiteralUnion<'list'|'dot'|'line'|'github'|'json'|'junit'|'null'|'html', string> | ReporterDescription[];
|
reporter?: LiteralUnion<'list'|'dot'|'line'|'github'|'json'|'junit'|'null'|'html', string> | ReporterDescription[];
|
||||||
webServer?: TestConfigWebServer;
|
webServer?: TestConfigWebServer | TestConfigWebServer[];
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface Config<TestArgs = {}, WorkerArgs = {}> extends TestConfig {
|
export interface Config<TestArgs = {}, WorkerArgs = {}> extends TestConfig {
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue