Playwright

How to write E2E tests with Python 

Technical Team Lead at HedgeServ Bulgaria

Rositsa Kotseva

What is Playwright?

A library that is used as a general purpose browser automation tool. It provides a powerful set of APIs to automate the web applications testing.

The functionalities that provides

  • Cross-browser
  • Cross-platform - Windows, Linux, MacOS.
  • API supported in different languages - Python, Java, .NET, JS and TS
  • Desktop and Mobile Emulation

The functionalities that provides

  • OO Architecture(one browser, multiple browser contexts)
  • Support of sync and async API 
  • Auto-wait
  • Tracing
  • Assertions and automatic retries
  • Reuse of state - authentication

The functionalities that provides

  • CodeGen
  • Playwright inspector
  • Trace Viewer
  • Videos and screenshots of actions/full tests.
  • API testing

The architecture

 Install and run

─$ pip install pytest-playwright
─$ playwright install
─$ touch test_playwright.py
─$ pytest

еxample

from playwright.sync_api import Page, expect


def test_playwright_python_page_title(page: Page) -> None:
    page.goto("https://playwright.dev/python/")
    assert page.title() == "Fast and reliable end-to-end testing for modern web apps | Playwright Python"


def test_get_started_link(page: Page) -> None:
    page.goto("https://playwright.dev/python/")

    # Click the get started link.
    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()


def test_playwright_library_link(page: Page) -> None:
    page.goto("https://playwright.dev/python/docs/intro")

    # Click the Playwright library link
    page.get_by_role("link", name="Playwright library").click()

    assert page.url == 'https://playwright.dev/python/docs/library'

еxample

╰─$ pytest test_playwright.py                                                                                                                                                                                  1 ↵
================================================================ test session starts ===============================================================================================
platform linux -- Python 3.10.7, pytest-8.2.1, pluggy-1.5.0
rootdir: /home/kamen/workshop/code
plugins: playwright-0.5.0, base-url-2.1.0
collected 3 items                                                                                                                                                                                                 

test_playwright.py ...                                                                                                                                                                                      [100%]

================================================================= 3 passed in 1.26s ================================================================================================

Browser instance and context

def test_with_screenshot(playwright: Playwright):
    chromium_engine = playwright.chromium
    browser = chromium_engine.launch()
    context = browser.new_context()
    page = context.new_page()
    page.goto("https://hackbulgaria.com")
    page.screenshot(path="screenshot_hb.png")
    locator = page.get_by_role("link", name="Запиши се")
    expect(locator).to_be_visible()
    browser.close()

Locators

locator = page.get_by_role("button", name="Log in")
page.get_by_text("Welcome!")
page.get_by_label("Password")
page.get_by_placeholder("name@example.com")
page.get_by_alt_text("playwright logo")
page.get_by_title("Elements count")
page.get_by_test_id("some data test id")
page.locator("css=button")
page.locator("xpath=//button")

Auto-waiting

Basic Actions

locator = page.get_by_role("button", name="Log in")
locator.click()
locator.check()
locator.hover()
locator.fill()
locator.focus()
...select_option()
...uncheck()
...etc

Assertions

locator = page.get_by_role("button", name="Log in")
expect(locator).to_be_enabled()
expect(locator).to_be_visible()
expect(locator).to_be_checked()
expect(locator).to_be_contain_text()
expect(locator).to_have_value()
expect(locator).to_have_text()
....etc

Fixtures

Function scope: created when requested in a test function and destroyed when the test ends.

 

Session scope: created when requested in a test function and destroyed when all tests end.

  • playwright: Playwright instance.
  • browser_type: BrowserType instance of the current browser.
  • browser: Browser instance launched by Playwright.
  • browser_name: Browser name as string.
  • browser_channel: Browser channel as string.
  • is_chromium, is_webkit, is_firefox: Booleans for the browser types.

API testing examples

@pytest.fixture(scope="session")
def api_request_context(
    playwright: Playwright,
) -> Generator[APIRequestContext, None, None]:
    headers = {
        "Accept": "XXXXXX",
        "Authorization": f"token {SOME_API_TOKEN}",
    }
    request_context = playwright.request.new_context(
        base_url="https://api.some_webpage.com", extra_http_headers=headers
    )
    yield request_context
    request_context.dispose()

API testing examples

def test_create_new_report(api_request_context: APIRequestContext) -> None:
    data = {
        "report_name": "Report 1"
    }
    new_report = api_request_context.post(f"/reports/create, data=data)
    assert new_report.ok

    all_reports = api_request_context.get(f"/reports/all")
    assert reports.ok

CodeGen

╰─$ playwright codegen  hackbulgaria.com

CodeGen

CodeGen

import re
from playwright.sync_api import Playwright, sync_playwright, expect


def run(playwright: Playwright) -> None:
    browser = playwright.chromium.launch(headless=False)
    context = browser.new_context()
    page = context.new_page()
    page.goto("https://www.hackbulgaria.com/")
    page.get_by_role("link", name="Запиши се").get_by_role("button").click()
    page.get_by_role("link", name="Кариеростарт").nth(1).click()
    page.get_by_role("button", name="Разгледай всички възможности (40) arrow").click()
    page.goto("https://www.hackbulgaria.com/career-start/opportunities?activeLocationIds=%5B%22D20lRYGtS6q9Xvu9ewi6hg%22%5D")
    page.goto("https://www.hackbulgaria.com/career-start/opportunities?activeLocationIds=%5B%22D20lRYGtS6q9Xvu9ewi6hg%22%2C%22aBa1wbURTZWUHz8BXKn4Og%22%5D")
    page.get_by_role("link", name="Junior-friendly позиции Junior Full-Stack Developer with Laravel във").click()
    with page.expect_popup() as page1_info:
        page.get_by_role("button", name="Научи повече").click()
    page1 = page1_info.value
    page.get_by_role("link", name="Workshops").nth(1).click()
    page.get_by_role("link", name="Събития").nth(1).click()
    with page.expect_popup() as page2_info:
        page.get_by_role("link", name="Facebook страницата на").click()
    page2 = page2_info.value

    # ---------------------
    context.close()
    browser.close()


with sync_playwright() as playwright:
    run(playwright)

Inspector

Trace Viewer

pytest --tracing on
playwright show-trace test-results/test-playwright-py-test-playwright-library-link-chromium/trace.zip 

Useful links

10.9k stars, 825 forks, around 1.2k closed issues

Q & A

Thank you!

Playwright

By Rositsa Zlateva

Playwright

  • 32