ruk·si

🎭️ Playwright

Updated at 2024-06-09 03:15

Playwright is an end-to-end testing library. It allows you to write tests that interact with your web application in a browser to ensure that it works as expected.

You can use JavaScript, Python, Java or C#. I've used it with Python so the examples are in Python.

Navigation

# go to a page and wait for everything to load
page.goto("https://example.com")

# do something and assume a certain URL change
page.get_by_text("Let me in!").click()
page.wait_for_url("**/login")

# but, in general, Playwright is smart enough to wait for the element to load
page.get_by_text("Let me in!").click()
expect(page.get_by_role("heading", name="Welcome!")).to_be_visible()
page.get_by_label("Username").fill("bob")

Do not use with page.expect_navigation() as it's not reliable. Use page.wait_for_url.

page.wait_for_url("**/my-account")

Finding Elements

You use get_by_* and locate functions to find elements on the page.

  1. For form fields, use label > placeholder > testid
  2. For images, use alt_text > testid
  3. For everything else, use role+name > text > testid.
page.get_by_label("Birthday")
page.get_by_placeholder("Birthday")
page.get_by_testid("birthday")
# etc.

Avoid locating elements with CSS or XPath. They are the least resilient and can break on unrelated DOM changes.

You use filter to narrow down the elements you're looking for.

page.get_by_role("listitem").filter(
    has_text="Product 2"
).get_by_role(
    "button", name="Add to cart"
).click()

You can also filter with "one that has a child like this".

page.get_by_role("listitem").filter(
    has=page.get_by_role("heading", name="Product 2")
).get_by_role(
    "button",
    name="Add to cart"
).click()

Performing Actions

page.get_by_label("Birthday").fill("2020-02-02")

page.get_by_label('I agree').check()
expect(page.get_by_label('Subscribe to newsletter')).to_be_checked()

page.get_by_label('Choose a color').select_option('blue')

# the name here is "the accessible name" of the element
# https://w3c.github.io/accname/#dfn-accessible-name
page.get_by_role("button", name="Submit", exact=True).click()
page.get_by_role("button", name=re.compile("submit", re.IGNORECASE)).click()

Checking Assumptions

expect(page.get_by_role("heading", name="Welcome!")).to_be_visible()

expect(page.get_by_role("button", name="Submit")).not_to_be_disabled()

expect(page.locator("list > .component")).to_have_count(3)

expect(page.locator("ul > li")).to_have_text(["Text 1", "Text 2", "Text 3"])

Browser Context

Each test runs in a separate browser context. A kin to an incognito tab in a browser.

You can emulate a lot with the context like setting viewport, timezone, geolocation, permissions, etc.

When you notice that registration and login per test becomes slow, use context.storage_state. It snapshots cookies and local storage so you can restore them later.

# Save storage state into the file.
storage = context.storage_state(path="playwright/.auth/state.json")

...

# Create a new context with the saved storage state.
context = browser.new_context(storage_state="playwright/.auth/state.json")

You'd normally use playwright/.auth directory to save this.