From 717b687d7cd91df895569e7ab7f471bf6a2cd403 Mon Sep 17 00:00:00 2001 From: github-actions <41898282+github-actions[bot]@users.noreply.github.com> Date: Sat, 9 Sep 2023 10:06:03 +0000 Subject: [PATCH] feat(roll): roll to ToT Playwright (09-09-23) --- nodejs/docs/release-notes.mdx | 4 +- nodejs/docs/writing-tests.mdx | 18 ++++- python/docs/intro.mdx | 26 ++++--- python/docs/writing-tests.mdx | 134 ++++++++++++++++++++++++++-------- 4 files changed, 133 insertions(+), 49 deletions(-) diff --git a/nodejs/docs/release-notes.mdx b/nodejs/docs/release-notes.mdx index 6eb9e20a08e..5354f7ef8ff 100644 --- a/nodejs/docs/release-notes.mdx +++ b/nodejs/docs/release-notes.mdx @@ -12,7 +12,7 @@ import LiteYouTube from '@site/src/components/LiteYouTube'; ### New `npx playwright merge-reports` tool -If you run tests on multiple shards, you can now merge all reports in a single HTML report (or any other report) using the new `merge-reports` CLI tool. +If you run tests on multiple shards, you can now merge all reports in a single HTML report (or any other report) using the new `merge-reports` CLI tool. Using `merge-reports` tool requires the following steps: 1. Adding a new "blob" reporter to the config when running on CI: @@ -41,7 +41,7 @@ Playwright now supports Debian 12 Bookworm on both x86_64 and arm64 for Chromium Linux support looks like this: | | Ubuntu 20.04 | Ubuntu 22.04 | Debian 11 | Debian 12 | -| :--- | :---: | :---: | :---: | :---: | +| :--- | :---: | :---: | :---: | :---: | | Chromium | ✅ | ✅ | ✅ | ✅ | | WebKit | ✅ | ✅ | ✅ | ✅ | | Firefox | ✅ | ✅ | ✅ | ✅ | diff --git a/nodejs/docs/writing-tests.mdx b/nodejs/docs/writing-tests.mdx index 446ce99da05..ad53cbff698 100644 --- a/nodejs/docs/writing-tests.mdx +++ b/nodejs/docs/writing-tests.mdx @@ -6,6 +6,8 @@ import Tabs from '@theme/Tabs'; import TabItem from '@theme/TabItem'; import HTMLCard from '@site/src/components/HTMLCard'; +## Introduction + Playwright tests are simple, they - **perform actions**, and - **assert the state** against expectations. @@ -71,6 +73,7 @@ Performing actions starts with locating the elements. Playwright uses [Locators ```js // Create a locator. const getStarted = page.getByRole('link', { name: 'Get started' }); + // Click it. await getStarted.click(); ``` @@ -127,15 +130,20 @@ Here is the list of the most popular async assertions. Note that there are [many | [expect(locator).toHaveValue()](/api/class-locatorassertions.mdx#locator-assertions-to-have-value) | Input element has value | | [expect(page).toHaveTitle()](/api/class-pageassertions.mdx#page-assertions-to-have-title) | Page has title | | [expect(page).toHaveURL()](/api/class-pageassertions.mdx#page-assertions-to-have-url) | Page has URL | -| [expect(page).toHaveScreenshot()](/api/class-pageassertions.mdx#page-assertions-to-have-screenshot-1) | Page has screenshot | ### Test Isolation -Playwright Test is based on the concept of [test fixtures](./test-fixtures.mdx) such as the [built in page fixture](./test-fixtures#built-in-fixtures), 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. +Playwright Test is based on the concept of [test fixtures](./test-fixtures.mdx) such as the [built in page fixture](./test-fixtures#built-in-fixtures), which is passed into your test. Pages are [isolated between tests due to the Browser Context](./browser-contexts), 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. ```js title="tests/example.spec.ts" -test('basic test', async ({ page }) => { - // ... +import { test } from '@playwright/test'; + +test('example test', async ({ page }) => { + // "page" belongs to an isolated BrowserContext, created for this specific test. +}); + +test('another test', async ({ page }) => { + // "page" in this second test is completely isolated from the first test. }); ``` @@ -163,6 +171,8 @@ test.describe('navigation', () => { - [Run single test, multiple tests, headed mode](./running-tests.mdx) - [Generate tests with Codegen](./codegen-intro.mdx) - [See a trace of your tests](./trace-viewer-intro.mdx) +- [Explore UI Mode](./test-ui-mode.mdx) +- [Run tests on CI with GitHub Actions](./ci-intro.mdx) [Accessibility]: /api/class-accessibility.mdx "Accessibility" diff --git a/python/docs/intro.mdx b/python/docs/intro.mdx index 68b18f0aa5f..e2e670adf40 100644 --- a/python/docs/intro.mdx +++ b/python/docs/intro.mdx @@ -6,8 +6,16 @@ import Tabs from '@theme/Tabs'; import TabItem from '@theme/TabItem'; import HTMLCard from '@site/src/components/HTMLCard'; +## Introduction + Playwright was created specifically to accommodate the needs of end-to-end testing. Playwright supports all modern rendering engines including Chromium, WebKit, and Firefox. Test on Windows, Linux, and macOS, locally or on CI, headless or headed with native mobile emulation. +**You will learn** +- [How to install Playwright](/intro.mdx#installing-playwright) +- [How to run the example test](/intro.mdx#running-the-example-test) + +## Installing Playwright + Playwright recommends using the official [Playwright Pytest plugin](./test-runners.mdx) 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.mdx) 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. Get started by installing Playwright and running the example test to see it in action. @@ -46,27 +54,23 @@ playwright install ## Add Example Test -Create a file that follows the `test_` prefix convention, such as `test_my_application.py`, inside the current working directory or in a sub-directory with the code below: +Create a file that follows the `test_` prefix convention, such as `test_example.py`, inside the current working directory or in a sub-directory with the code below. Make sure your test name also follows the `test_` prefix convention. -```py title="test_my_application.py" +```py title="test_example.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): +def test_has_title(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.get_by_role("link", name="Get started") - - # Expect an attribute "to be strictly equal" to the value. - expect(get_started).to_have_attribute("href", "/docs/intro") +def test_get_started_link(page: Page): + page.goto("https://playwright.dev/") # Click the get started link. - get_started.click() + page.get_by_role("link", name="Get started").click() # Expects page to have a heading with the name of Installation. expect(page.get_by_role("heading", name="Installation")).to_be_visible() @@ -74,7 +78,7 @@ def test_homepage_has_Playwright_in_title_and_get_started_link_linking_to_the_in ## 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. +By default tests will be run on chromium. This can be configured via the [CLI options](./running-tests.mdx). 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 pytest diff --git a/python/docs/writing-tests.mdx b/python/docs/writing-tests.mdx index 2e295a95603..55093f57d2d 100644 --- a/python/docs/writing-tests.mdx +++ b/python/docs/writing-tests.mdx @@ -6,86 +6,155 @@ import Tabs from '@theme/Tabs'; import TabItem from '@theme/TabItem'; import HTMLCard from '@site/src/components/HTMLCard'; -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.mdx) built in meaning it waits for elements to be actionable prior to performing actions. Playwright provides an [expect](./test-assertions.mdx) function to write assertions. +## Introduction -Take a look at the example test below to see how to write a test using [locators](/locators.mdx) and web first assertions. +Playwright tests are simple, they +- **perform actions**, and +- **assert the state** against expectations. -```python +There is no need to wait for anything prior to performing an action: Playwright automatically waits for the wide range of [actionability](./actionability.mdx) checks to pass prior to performing each action. + +There is also no need to deal with the race conditions when performing the checks - Playwright assertions are designed in a way that they describe the expectations that need to be eventually met. + +That's it! These design choices allow Playwright users to forget about flaky timeouts and racy checks in their tests altogether. + +**You will learn** +- [How to write the first test](/writing-tests.mdx#first-test) +- [How to perform actions](/writing-tests.mdx#actions) +- [How to use assertions](/writing-tests.mdx#assertions) +- [How tests run in isolation](/writing-tests.mdx#test-isolation) +- [How to use test hooks](/writing-tests.mdx#using-test-hooks) + +## First test + +Take a look at the following example to see how to write a test. Note how the file name follows the `test_` prefix convention as well as each test name. + +```python title="test_example.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): +def test_has_title(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.get_by_role("link", name="Get started") - - # Expect an attribute "to be strictly equal" to the value. - expect(get_started).to_have_attribute("href", "/docs/intro") +def test_get_started_link(page: Page): + page.goto("https://playwright.dev/") # Click the get started link. - get_started.click() + page.get_by_role("link", name="Get started").click() # Expects page to have a heading with the name of Installation. expect(page.get_by_role("heading", name="Installation")).to_be_visible() ``` -### Assertions +## Actions -Playwright provides the [`expect`](./test-assertions.mdx) function which will wait until the expected condition is met. +### Navigation -```python -import re -from playwright.sync_api import expect +Most of the tests will start with navigating page to the URL. After that, test will be able to interact with the page elements. -expect(page).to_have_title(re.compile("Playwright")) +```python +page.goto("https://playwright.dev/") ``` -### Locators +Playwright will wait for page to reach the load state prior to moving forward. Learn more about the [page.goto()](/api/class-page.mdx#page-goto) options. -[Locators](./locators.mdx) 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. +### Interactions -```python -from playwright.sync_api import expect +Performing actions starts with locating the elements. Playwright uses [Locators API](./locators.mdx) for that. Locators represent a way to find element(s) on the page at any moment, learn more about the [different types](./locators.mdx) of locators available. Playwright will wait for the element to be [actionable](./actionability.mdx) prior to performing the action, so there is no need to wait for it to become available. +```python +# Create a locator. get_started = page.get_by_role("link", name="Get started") -expect(get_started).to_have_attribute("href", "/docs/installation") +# Click it. get_started.click() ``` -### Test Isolation +In most cases, it'll be written in one line: + +```python +page.get_by_role("link", name="Get started").click() +``` -The Playwright Pytest plugin is based on the concept of test fixtures such as the [built in page fixture](./test-runners.mdx), 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. +### Basic actions + +This is the list of the most popular Playwright actions. Note that there are many more, so make sure to check the [Locator API](./api/class-locator.mdx) section to learn more about them. + +| Action | Description | +| :- | :- | +| [locator.check()](/api/class-locator.mdx#locator-check) | Check the input checkbox | +| [locator.click()](/api/class-locator.mdx#locator-click) | Click the element | +| [locator.uncheck()](/api/class-locator.mdx#locator-uncheck) | Uncheck the input checkbox | +| [locator.hover()](/api/class-locator.mdx#locator-hover) | Hover mouse over the element | +| [locator.fill()](/api/class-locator.mdx#locator-fill) | Fill the form field, input text | +| [locator.focus()](/api/class-locator.mdx#locator-focus) | Focus the element | +| [locator.press()](/api/class-locator.mdx#locator-press) | Press single key | +| [locator.set_input_files()](/api/class-locator.mdx#locator-set-input-files) | Pick files to upload | +| [locator.select_option()](/api/class-locator.mdx#locator-select-option) | Select option in the drop down | + +## Assertions + +Playwright includes [assertions](./test-assertions.mdx) that will wait until the expected condition is met. Using these assertions allows making the tests non-flaky and resilient. For example, this code will wait until the page gets the title containing "Playwright": ```python +import re +from playwright.sync_api import expect + +expect(page).to_have_title(re.compile("Playwright")) +``` + +Here is the list of the most popular async assertions. Note that there are [many more](./test-assertions.mdx) to get familiar with: + +| Assertion | Description | +| :- | :- | +| [expect(locator).to_be_checked()](/api/class-locatorassertions.mdx#locator-assertions-to-be-checked) | Checkbox is checked | +| [expect(locator).to_be_enabled()](/api/class-locatorassertions.mdx#locator-assertions-to-be-enabled) | Control is enabled | +| [expect(locator).to_be_visible()](/api/class-locatorassertions.mdx#locator-assertions-to-be-visible) | Element is visible | +| [expect(locator).to_contain_text()](/api/class-locatorassertions.mdx#locator-assertions-to-contain-text) | Element contains text | +| [expect(locator).to_have_attribute()](/api/class-locatorassertions.mdx#locator-assertions-to-have-attribute) | Element has attribute | +| [expect(locator).to_have_count()](/api/class-locatorassertions.mdx#locator-assertions-to-have-count) | List of elements has given length | +| [expect(locator).to_have_text()](/api/class-locatorassertions.mdx#locator-assertions-to-have-text) | Element matches text | +| [expect(locator).to_have_value()](/api/class-locatorassertions.mdx#locator-assertions-to-have-value) | Input element has value | +| [expect(page).to_have_title()](/api/class-pageassertions.mdx#page-assertions-to-have-title) | Page has title | +| [expect(page).to_have_url()](/api/class-pageassertions.mdx#page-assertions-to-have-url) | Page has URL | + +### Test Isolation + +The Playwright Pytest plugin is based on the concept of test fixtures such as the [built in page fixture](./test-runners.mdx), which is passed into your test. Pages are [isolated between tests due to the Browser Context](./browser-contexts), 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 title="test_example.py" from playwright.sync_api import Page -def test_basic_test(page: Page): +def test_example_test(page: Page): + pass + # "page" belongs to an isolated BrowserContext, created for this specific test. + +def test_another_test(page: Page): pass - # ... + # "page" in this second test is completely isolated from the first test. ``` ### 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 +```python title="test_example.py" import pytest -from playwright.sync_api import Page - +from playwright.sync_api import Page, expect @pytest.fixture(scope="function", autouse=True) def before_each_after_each(page: Page): - print("beforeEach") + + print("before the test runs") + # Go to the starting url before each test. page.goto("https://playwright.dev/") yield - print("afterEach") + + print("after the test runs") def test_main_navigation(page: Page): # Assertions use the expect API. @@ -94,8 +163,9 @@ def test_main_navigation(page: Page): ## What's Next - [Run single test, multiple tests, headed mode](./running-tests.mdx) -- [Generate tests with Codegen](./codegen.mdx) +- [Generate tests with Codegen](./codegen-intro.mdx) - [See a trace of your tests](./trace-viewer-intro.mdx) +- [Run tests on CI with GitHub Actions](./ci-intro.mdx) [Accessibility]: /api/class-accessibility.mdx "Accessibility"