Playwright Python
Complete Beginner's Notes
-created by
Raniya Sherin
21 Sections • All Topics Covered • With Examples
Python 3 Pytest Async/Sync POM CI/CD BDD
Playwright Python — Complete Beginner's Notes Page 2
Table of Contents
Section Title Page
1 Getting Started with Playwright 3
2 Locators — Finding Elements 5
3 Actions — Interacting with Pages 7
4 Events, Waiting & Dialogs 9
5 Authentication & Sessions 11
6 Automated Mail Checker Project 12
7 Pytest — Writing & Running Tests 13
8 pytest-playwright Plugin 15
9 Playwright Tools 16
10 Web-First Assertions 17
11 UI Testing Playground 18
12 Playwright Fixtures 19
13 Page Object Model (POM) 20
14 Network Events 21
15 API Testing 22
16 Optimisation & Speed Tips 23
17 Tips, Tricks & Debugging 24
Microsoft Playwright • Python Automation • Beginner Friendly
Playwright Python — Complete Beginner's Notes Page 3
18 GitHub API Testing 25
19 Continuous Integration (CI/CD) 26
20 Data-Driven Testing 27
21 Behaviour-Driven Development (BDD) 28
— Quick Reference Cheat Sheet 29
Microsoft Playwright • Python Automation • Beginner Friendly
Playwright Python — Complete Beginner's Notes Page 4
Section 1
Getting Started with Playwright
This section teaches you everything you need to start using Playwright from scratch.
You will learn what Playwright is, why it is useful, how to install it, and how to write
your very first automation script step by step.
What is Playwright?
Playwright is a free, open-source library created by Microsoft. It allows you to write Python code
that controls a real web browser — just like a human would — clicking buttons, filling forms,
reading text, and navigating pages. It supports three major browsers: Chromium (the engine
behind Chrome), Firefox, and WebKit (the engine behind Safari).
Why use Playwright?
• Web Testing: Automatically test your website to make sure everything works correctly.
• Web Scraping: Collect data from websites automatically without clicking manually.
• Task Automation: Automate boring, repetitive browser tasks such as filling the same form
every day.
• Cross-Browser Support: Run the same script on Chrome, Firefox, and Safari with minimal
changes.
• Fast and Reliable: Playwright waits for pages to load automatically, so your scripts rarely
break.
Installation — Step by Step
Before writing any code you must install Playwright. Open your terminal (Command Prompt on
Windows, Terminal on Mac/Linux) and run the following commands one at a time:
# Step 1: Install the Playwright Python library using pip
pip install playwright
# Step 2: Download the actual browser files (Chromium, Firefox, WebKit)
playwright install
Microsoft Playwright • Python Automation • Beginner Friendly
Playwright Python — Complete Beginner's Notes Page 5
■ Tip: Always use a virtual environment to keep your project tidy. Run: python -m venv venv then
source venv/bin/activate (Mac/Linux) or venv\Scripts\activate (Windows).
Launching Your First Browser
The basic pattern in Playwright is always the same: launch a browser → open a new page → go
to a URL → do something → close the browser. Here is the simplest possible script:
from playwright.sync_api import sync_playwright
with sync_playwright() as p:
# Launch Chromium browser. headless=False means you can SEE the window.
browser = [Link](headless=False)
# Open a blank new browser tab
page = browser.new_page()
# Navigate to a website
[Link]("[Link]
# Print the title of the page to the terminal
print([Link]())
# Close the browser when done
[Link]()
Understanding headless Mode
• headless=False: The browser window opens visibly on your screen. Great for learning and
debugging.
• headless=True (default): The browser runs in the background without any visible window.
Much faster — use this for automated tests in production.
Microsoft Playwright • Python Automation • Beginner Friendly
Playwright Python — Complete Beginner's Notes Page 6
Section 2
Locators — Finding Elements on a Page
Before you can click a button or fill in a form, Playwright needs to know WHERE that
element is on the page. A Locator is like a precise address or GPS coordinate for any
element in the HTML. This section covers all the different ways to find elements.
What is a Locator?
Every webpage is made of HTML elements — buttons, links, text boxes, headings, images, etc. A
Locator is a way of telling Playwright "find this specific element for me." Once Playwright finds
the element, you can interact with it (click, type, read, etc.).
■ Note: Always prefer Role, Label, or Text locators. They are more stable and readable than raw CSS
or XPath.
Locator by Role (Most Recommended)
Every HTML element has a "role" — a semantic meaning. A <button> has the role "button", an
<a> tag has the role "link", an <h1> has the role "heading". This is the most reliable way to find
elements because it matches how real users experience the page:
# Click a button whose visible text is "Submit"
page.get_by_role("button", name="Submit").click()
# Click a link whose visible text is "Home"
page.get_by_role("link", name="Home").click()
# Check if a heading is visible
page.get_by_role("heading", name="Welcome").is_visible()
# Find a text input box
page.get_by_role("textbox", name="Search").fill("Python")
Locator by Placeholder and Label
Input fields often have a placeholder (grey hint text) or a label above them. You can use either to
find the field:
# Find by the placeholder text inside the input box
page.get_by_placeholder("Enter your
email").fill("test@[Link]")
# Find by the label text that sits above/beside the input
page.get_by_label("Password").fill("secret123")
Microsoft Playwright • Python Automation • Beginner Friendly
Playwright Python — Complete Beginner's Notes Page 7
Locator by Visible Text
If you can see text on the page, you can find the element containing that text:
# Find any element that contains this text
page.get_by_text("Welcome back!").is_visible()
# exact=True means it must match EXACTLY, not just contain the text
page.get_by_text("Sign In", exact=True).click()
CSS Selectors
CSS selectors are borrowed from web design. They are powerful but slightly more fragile than role-
based locators. Use them when the above options do not work:
# By CSS class name (elements with class="submit-btn")
[Link]("[Link]-btn").click()
# By ID (elements with id="login-form")
[Link]("#login-form").is_visible()
# By HTML attribute
[Link]("input[type=email]").fill("a@[Link]")
# Parent > child hierarchy
[Link]("div > [Link]").inner_text()
# Pseudo-class: the 2nd list item
[Link]("li:nth-child(2)").click()
XPath Locators
XPath is a full path language for navigating HTML trees. It is the most powerful but also the most
verbose. Use as a last resort:
# Find a button whose text is exactly "Login"
[Link]('//button[text()="Login"]').click()
# Find an input with id="username"
[Link]('//input[@id="username"]').fill("admin")
# Find a div that contains the class "error"
[Link]('//div[contains(@class, "error")]').inner_text()
# Find the very last list item on the page
[Link]("//li[last()]").click()
Microsoft Playwright • Python Automation • Beginner Friendly
Playwright Python — Complete Beginner's Notes Page 8
Section 3
Actions — Interacting with Web Pages
Once you have found an element using a Locator, you need to DO something with it.
Actions are all the things you can do to a web element — clicking, typing, hovering,
selecting from dropdowns, uploading files, and pressing keyboard keys.
Mouse Actions
• click(): A standard left mouse button click.
• dblclick(): A double-click — opens files, selects words in text editors.
• click(button="right"): Right-click — opens context menus.
• hover(): Moves the mouse over an element without clicking — triggers dropdown menus.
• drag_and_drop(): Grabs an element and drops it somewhere else.
[Link]("#btn").click() # left click
[Link]("#btn").dblclick() # double click
[Link]("#btn").click(button="right")# right click
[Link]("#item").hover() # hover over element
# Drag element from #source and drop it onto #target
page.drag_and_drop("#source", "#target")
Text Input
• fill(): Clears the field first, then types the whole text at once. Fast and recommended.
• type(): Types character by character, just like a real keyboard. Useful for testing
autocomplete.
• clear(): Empties the field without typing anything.
# fill is fastest — clears existing text then inserts new text
[Link]("#search").fill("Playwright")
# type simulates real key presses one character at a time
[Link]("#search").type("Playwright")
# clear removes everything from the field
[Link]("#search").clear()
Microsoft Playwright • Python Automation • Beginner Friendly
Playwright Python — Complete Beginner's Notes Page 9
Checkboxes, Radio Buttons, and Switches
# Tick a checkbox
page.get_by_label("I agree to terms").check()
# Untick a checkbox
page.get_by_label("I agree to terms").uncheck()
# Select a radio button option
page.get_by_label("Male").check()
# Read whether a checkbox is currently ticked (returns True or False)
is_checked = page.get_by_label("I agree to
terms").is_checked()
print(is_checked) # True or False
Dropdown (Select) Menus
# Select by the visible label text
[Link]("#country").select_option("India")
# Select by the hidden value attribute in the HTML
[Link]("#country").select_option(value="IN")
# Select by position (0 = first item, 1 = second item, etc.)
[Link]("#country").select_option(index=2)
Uploading Files
# Upload a single file
[Link]("input[type=file]").set_input_files("[Link]")
# Upload multiple files at once
[Link]("input[type=file]").set_input_files(["[Link]"
, "[Link]"])
Keyboard Shortcuts
[Link]("Enter") # press the Enter key
[Link]("Control+A") # Ctrl+A = select all text
[Link]("Control+C") # Ctrl+C = copy
[Link]("Tab") # move focus to next element
[Link]("Hello World") # type a full string via keyboard
Microsoft Playwright • Python Automation • Beginner Friendly
Playwright Python — Complete Beginner's Notes Page 10
Section 4
Events, Waiting & Dialogs
Web pages do not always respond instantly. Elements may take time to appear, pop-up
dialogs may interrupt your script, and files may need to be downloaded. This section
explains how Playwright handles all of these situations automatically and how you can
take extra control when needed.
Auto-Waiting — Playwright's Superpower
Most browser automation tools fail because they try to click an element before it finishes loading.
Playwright solves this by automatically waiting for an element to be visible, enabled, and stable
(not moving) before it interacts with it. You almost never need to add a manual sleep() call.
# Playwright waits automatically — no [Link]() needed!
page.get_by_role("button", name="Load Data").click()
# If you want to explicitly wait for some text to appear:
page.get_by_text("Data Loaded").wait_for()
Navigation Waiting
[Link]("[Link]
# Wait until there is no network traffic for 500ms (page is fully loaded)
page.wait_for_load_state("networkidle")
# Wait until the HTML structure (DOM) has finished building
page.wait_for_load_state("domcontentloaded")
Custom Waiting
# Wait for a specific element to appear on the page
page.wait_for_selector("#result")
# Wait a fixed number of milliseconds (2000ms = 2 seconds)
# Use sparingly — better to wait for elements, not time
page.wait_for_timeout(2000)
# Wait until a JavaScript expression becomes true
page.wait_for_function("[Link] === true")
Microsoft Playwright • Python Automation • Beginner Friendly
Playwright Python — Complete Beginner's Notes Page 11
Listening to Page Events
# Print every [Link]() message from the browser
[Link]("console", lambda msg: print("Console:", [Link]))
# Print any JavaScript errors that occur on the page
[Link]("pageerror", lambda err: print("JS Error:", err))
Handling Pop-up Dialogs (Alert, Confirm, Prompt)
Websites sometimes show browser pop-ups. Playwright lets you handle them automatically by
setting up a listener BEFORE the action that triggers the dialog:
# Accept the dialog (click OK)
[Link]("dialog", lambda dialog: [Link]())
# Dismiss the dialog (click Cancel)
[Link]("dialog", lambda dialog: [Link]())
# For a prompt dialog, type a custom answer and accept
[Link]("dialog", lambda d: [Link]("My Answer"))
Downloading Files
# Use expect_download() to capture the file that gets downloaded
with page.expect_download() as dl_info:
page.get_by_role("button", name="Download Report").click()
download = dl_info.value
download.save_as("my_report.pdf") # save it to your computer
Sync vs Async — Which Should I Use?
• Sync (sync_api): Code runs line by line, one step at a time. Easiest for beginners. Use this
when you are just starting out.
• Async (async_api): Code can handle multiple tasks at the same time. Faster for large
projects that need to run many tests in parallel.
# Async example — note the "async" and "await" keywords
import asyncio
from playwright.async_api import async_playwright
async def main():
async with async_playwright() as p:
browser = await [Link]()
Microsoft Playwright • Python Automation • Beginner Friendly
Playwright Python — Complete Beginner's Notes Page 12
page = await browser.new_page()
await [Link]("[Link]
print(await [Link]())
await [Link]()
[Link](main())
Microsoft Playwright • Python Automation • Beginner Friendly
Playwright Python — Complete Beginner's Notes Page 13
Section 5
Authentication & Sessions
Many websites require you to log in before you can access their content. Playwright can
automate the login process and, even better, save your login session so you only have to
log in once and reuse it in all future tests.
Automating a Login (Google Example)
You can fill in the email and password fields just like a real user would. Below is an example for
Google Sign-In:
[Link]("[Link]
# Type your email address
page.get_by_label("Email").fill("myemail@[Link]")
page.get_by_role("button", name="Next").click()
# Type your password on the next screen
page.get_by_label("Password").fill("mypassword")
page.get_by_role("button", name="Next").click()
# Now you are logged in!
Saving Your Login Session
Logging in every single test is slow. Playwright can save your cookies and local storage (your
"session") to a file after you log in once, and then reuse that session in all future tests:
# ■■ STEP 1: Log in and save session ■■
context = browser.new_context()
page = context.new_page()
# ... perform your login steps here ...
# Save cookies and localStorage to a file called [Link]
context.storage_state(path="[Link]")
[Link]()
# ■■ STEP 2: Reuse session in future tests ■■
# Load [Link] — Playwright restores your login automatically
context = browser.new_context(storage_state="[Link]")
Microsoft Playwright • Python Automation • Beginner Friendly
Playwright Python — Complete Beginner's Notes Page 14
page = context.new_page()
# Go directly to the logged-in area — no login form needed!
[Link]("[Link]
■ Tip: Add [Link] to your .gitignore file so you never accidentally share your session credentials on
GitHub.
Microsoft Playwright • Python Automation • Beginner Friendly
Playwright Python — Complete Beginner's Notes Page 15
Section 6
Automated Mail Checker Project
This real-world project shows you how to combine everything you have learned so far.
You will build a script that automatically opens Gmail, reads your unread emails, and
prints their details to the terminal.
Project File Structure
mail_checker/
■■■ [Link] # your automation script
■■■ [Link] # saved Gmail login session
■■■ [Link] # list of Python packages
Reading Unread Emails
[Link]("[Link]
# Gmail marks unread emails with CSS class "zE"
# We find all of them and count how many there are
unread_emails = [Link]("[Link]")
count = unread_emails.count()
print(f"You have {count} unread emails")
Extracting Sender, Subject and Preview
# Look at the first unread email
first_email = [Link]("[Link]").first
# Extract the sender name
sender = first_email.locator(".yX").inner_text()
# Extract the subject line
subject = first_email.locator(".y6").inner_text()
# Extract the preview text
preview = first_email.locator(".y2").inner_text()
print(f"From: {sender}")
print(f"Subject: {subject}")
print(f"Preview: {preview}")
Microsoft Playwright • Python Automation • Beginner Friendly
Playwright Python — Complete Beginner's Notes Page 16
Looping Through All Unread Emails
emails = [Link]("table.F tbody tr")
for i in range([Link]()):
email = [Link](i) # get the i-th email row
sender = [Link](".yX").inner_text()
print(f"Email {i+1}: {sender}")
Microsoft Playwright • Python Automation • Beginner Friendly
Playwright Python — Complete Beginner's Notes Page 17
Section 7
Pytest — Writing and Running Tests
Pytest is the most popular Python testing framework. Instead of just running scripts
manually, Pytest lets you write structured tests that automatically check whether your
website behaves correctly. Playwright integrates seamlessly with Pytest.
What is Pytest?
A test is a small piece of code that checks one specific thing. For example: "After clicking Login,
is the user on the Dashboard page?" Pytest finds all your test functions, runs them, and tells you
which ones passed and which ones failed.
Writing Your First Test
# test_login.py
# File name MUST start with "test_" for Pytest to find it
from playwright.sync_api import Page
def test_login(page: Page):
# 1. Go to the login page
[Link]("[Link]
# 2. Fill in username and password
page.get_by_label("Username").fill("admin")
page.get_by_label("Password").fill("secret")
# 3. Click the Login button
page.get_by_role("button", name="Login").click()
# 4. Check that we ended up on the Dashboard
assert [Link] == "[Link]
Running Tests from the Terminal
pytest # run ALL test files in the folder
pytest test_login.py # run only this specific file
pytest -v # verbose — shows each test name
pytest -k "login" # run only tests whose name contains "login"
pytest -x # stop immediately on the first failure
Microsoft Playwright • Python Automation • Beginner Friendly
Playwright Python — Complete Beginner's Notes Page 18
Fixtures — Reusable Setup Code
A fixture is a function that sets up something your tests need — like a logged-in browser page —
and then cleans it up automatically afterwards. This avoids copy-pasting setup code into every test:
import pytest
from playwright.sync_api import sync_playwright
@[Link]
def logged_in_page():
with sync_playwright() as p:
browser = [Link]()
page = browser.new_page()
[Link]("[Link]
page.get_by_label("Username").fill("admin")
page.get_by_label("Password").fill("pass")
page.get_by_role("button", name="Login").click()
yield page # hand the ready page to the test
[Link]() # this runs AFTER the test finishes
# Use the fixture by adding it as a parameter
def test_dashboard(logged_in_page):
assert "Dashboard" in logged_in_page.title()
Fixture Scope — How Often is the Fixture Created?
• scope="function" (default): A fresh fixture is created for every single test. Most isolated but
slowest.
• scope="module": One fixture is shared across all tests in the same file.
• scope="session": One fixture is shared across the entire test run. Fastest.
Microsoft Playwright • Python Automation • Beginner Friendly
Playwright Python — Complete Beginner's Notes Page 19
Section 8
pytest-playwright Plugin
The pytest-playwright plugin is an official add-on that gives you a ready-made "page"
fixture and many useful command-line options, saving you from writing browser setup
code yourself. It is the recommended way to use Playwright with Pytest.
Installation
pip install pytest-playwright
playwright install
Using the Built-in page Fixture
Once installed, you get a "page" fixture for free — just add it as a parameter to your test function
and the plugin handles all browser setup and teardown:
# No browser setup needed — the plugin does it for you!
def test_title(page):
[Link]("[Link]
assert "Example Domain" in [Link]()
Useful Command-Line Options
pytest --headed # show the browser window while tests run
pytest --browser firefox # use Firefox instead of Chromium
pytest --browser webkit # use WebKit (Safari engine)
pytest --slowmo 500 # add 500ms delay between each action (easier to watch)
[Link] — Saving Your Preferred Options
Instead of typing options every time, save your defaults in a [Link] file in your project root:
# [Link]
[pytest]
addopts = --headed --browser chromium
timeout = 30
Microsoft Playwright • Python Automation • Beginner Friendly
Playwright Python — Complete Beginner's Notes Page 20
Section 9
Playwright Tools — Screenshots, Video, Traces &
Codegen
Playwright comes with powerful built-in tools to help you debug problems, record
videos of test runs, and even automatically generate test code just by clicking around in
the browser.
Take a Screenshot
# Screenshot of the visible portion of the page
[Link](path="[Link]")
# Full-page screenshot including content below the fold
[Link](path="[Link]", full_page=True)
# Screenshot of just one specific element
[Link]("#header").screenshot(path="[Link]")
Record a Video
Tell the context to record a video before you open the page. The video is saved when you close the
context:
context = browser.new_context(record_video_dir="videos/")
page = context.new_page()
# ... perform your actions ...
[Link]() # video is saved to the videos/ folder here
Trace Viewer — Full Debugging Replay
A trace records everything that happened during a test: screenshots of every step, the full HTML
at each moment, and all network requests. When a test fails, you can replay it like a video to see
exactly what went wrong:
context = browser.new_context()
[Link](screenshots=True, snapshots=True)
page = context.new_page()
[Link]("[Link]
# ... do more actions ...
[Link](path="[Link]")
Microsoft Playwright • Python Automation • Beginner Friendly
Playwright Python — Complete Beginner's Notes Page 21
# Open the trace viewer in your browser:
# playwright show-trace [Link]
Codegen — Auto-Generate Test Code
Codegen is a magic tool: it opens a browser, watches everything you do, and writes the Playwright
code for you in real time. Just interact with the site normally and copy the generated code:
# Run this in your terminal
playwright codegen [Link]
# A browser window and a code panel both open.
# Every click and type you do appears as code on the right side.
■ Tip: Codegen is the fastest way to learn correct selectors for a new website.
Microsoft Playwright • Python Automation • Beginner Friendly
Playwright Python — Complete Beginner's Notes Page 22
Section 10
Web-First Assertions
Assertions are how you CHECK that your website is behaving correctly. Playwright's
web-first assertions are special — they automatically retry the check up to 5 seconds
before giving up, which means they rarely fail due to timing issues.
Why Web-First Assertions?
A plain Python assert fails immediately. If the page takes 1 second to update, your test fails even
though it would have passed a moment later. Playwright's expect() keeps retrying automatically:
from playwright.sync_api import expect
Page-Level Assertions
# Check the page title
expect(page).to_have_title("Dashboard")
# Check the current URL
expect(page).to_have_url("[Link]
Element State Assertions
expect([Link]("#modal")).to_be_visible() # element is on
screen
expect([Link]("#submit")).to_be_enabled() # button can be
clicked
expect([Link]("#submit")).to_be_disabled() # button is
greyed out
expect([Link]("#modal")).to_be_hidden() # element is not
visible
Text and Content Assertions
# Element must contain EXACTLY this text
expect([Link]("h1")).to_have_text("Welcome!")
# Element must contain this text SOMEWHERE inside it
expect([Link](".message")).to_contain_text("success")
Microsoft Playwright • Python Automation • Beginner Friendly
Playwright Python — Complete Beginner's Notes Page 23
Input and Attribute Assertions
# Check an HTML attribute
expect([Link]("img")).to_have_attribute("alt", "Logo")
# Check the current value inside an input field
expect(page.get_by_label("Email")).to_have_value("test@email.c
om")
# Check a checkbox is ticked
expect(page.get_by_label("Remember me")).to_be_checked()
# Check a dropdown's selected value
expect([Link]("#country")).to_have_value("IN")
■ Note: All expect() assertions wait up to 5 seconds by default before failing. You can customise this:
expect(locator, timeout=10000).
Microsoft Playwright • Python Automation • Beginner Friendly
Playwright Python — Complete Beginner's Notes Page 24
Section 11
UI Testing Playground — Practice Challenges
Ui [Link] is a free website specifically designed to challenge your
Playwright skills. It presents tricky real-world situations you will encounter on real
websites. Here is how to solve each challenge.
Dynamic ID — ID Changes Every Page Load
Never rely on an ID that changes. Use visible text or role instead.
# BAD: [Link]("#btn-12345").click() <- ID changes every time!
# GOOD: use the visible button text instead
page.get_by_role("button", name="Button with Dynamic
ID").click()
Hidden Layer — Another Element is Blocking the Click
Wait for the blocking overlay to disappear, or force the click.
page.wait_for_selector("#hidingLayer", state="hidden") # wait for
blocker to go
[Link]("#greenButton").click(force=True) # or force it
Load Delay — Element Appears After a Delay
Playwright waits automatically, but you can increase the timeout for slow pages.
# Increase timeout to 10 seconds for very slow elements
[Link]("#ajaxButton").click(timeout=10000)
AJAX — Content Loads Asynchronously After a Click
Click the button, then wait for the new content to appear.
[Link]("#ajaxButton").click()
page.wait_for_selector(".bg-success") # wait for result
text = [Link](".bg-success p").inner_text()
print(text)
Microsoft Playwright • Python Automation • Beginner Friendly
Playwright Python — Complete Beginner's Notes Page 25
Scrollbars — Element is Off-Screen
Scroll the element into view before clicking.
[Link]("#scrollBtn").scroll_into_view_if_needed()
[Link]("#scrollBtn").click()
Progress Bar — Wait Until Completion
Use wait_for_function to poll the value and stop at the right moment.
[Link]("#startButton").click()
page.wait_for_function(
'[Link]("#progressBar")
.getAttribute("aria-valuenow") >= 75'
)
[Link]("#stopButton").click()
Microsoft Playwright • Python Automation • Beginner Friendly
Playwright Python — Complete Beginner's Notes Page 26
Section 12
Playwright Fixtures — Advanced Browser Setup
Fixtures control how browsers are created and shared across tests. Choosing the right
scope balances isolation (each test is independent) against speed (sharing a browser is
faster).
Function Scope — One Browser Per Test (Most Isolated)
@[Link]
def page():
with sync_playwright() as p:
browser = [Link]()
page = browser.new_page()
yield page
[Link]() # browser closes after EACH test
Session Scope — One Browser for ALL Tests (Fastest)
@[Link](scope="session")
def browser_session():
with sync_playwright() as p:
browser = [Link]()
yield browser
[Link]() # browser closes only at the END of all tests
Cross-Browser Testing
# Run the same test on multiple browsers automatically
@[Link](params=["chromium", "firefox", "webkit"])
def cross_browser(request):
with sync_playwright() as p:
browser = getattr(p, [Link]).launch()
yield browser.new_page()
[Link]()
Microsoft Playwright • Python Automation • Beginner Friendly
Playwright Python — Complete Beginner's Notes Page 27
Browser Launch Arguments
browser = [Link](
headless=False,
slow_mo=100, # add 100ms pause between every action
args=["--start-maximized"]# start the browser in full screen
)
# Set viewport size and locale
context = browser.new_context(
viewport={"width": 1280, "height": 720},
locale="en-IN",
timezone_id="Asia/Kolkata"
)
Microsoft Playwright • Python Automation • Beginner Friendly
Playwright Python — Complete Beginner's Notes Page 28
Section 13
Page Object Model (POM)
The Page Object Model is a design pattern that makes your tests cleaner, easier to read,
and much easier to maintain. Instead of putting all your locators and actions directly
inside each test, you create a separate Python class for each page of your website.
Why Use POM?
• Cleaner tests: Test functions read like plain English — no messy locator strings.
• Easy maintenance: If a button changes, you update it in ONE place (the class), not in every
test that uses it.
• Reusability: The same page class can be used by dozens of different tests.
Creating a Page Class
# pages/login_page.py
class LoginPage:
def __init__(self, page):
[Link] = page
# Define locators as class attributes
self.username_field = page.get_by_label("Username")
self.password_field = page.get_by_label("Password")
self.login_button = page.get_by_role("button", name="Login")
def navigate(self):
"""Go to the login page."""
[Link]("[Link]
def login(self, username, password):
"""Fill in credentials and click login."""
self.username_field.fill(username)
self.password_field.fill(password)
self.login_button.click()
Microsoft Playwright • Python Automation • Beginner Friendly
Playwright Python — Complete Beginner's Notes Page 29
Using POM in Tests
# test_login.py
from pages.login_page import LoginPage
from playwright.sync_api import expect
def test_valid_login(page):
login = LoginPage(page) # create the page object
[Link]() # go to login page
[Link]("admin", "secret") # perform login
# Verify we reached the dashboard
expect(page).to_have_url("[Link]
Microsoft Playwright • Python Automation • Beginner Friendly
Playwright Python — Complete Beginner's Notes Page 30
Section 14
Network Events — Monitoring & Intercepting
Traffic
Playwright lets you listen to every HTTP request and response that the browser makes.
You can also intercept requests to block certain resources, speed up tests, or return fake
data for testing purposes.
Listening to Requests and Responses
# Print every URL the browser requests
[Link]("request", lambda req: print("Request:",
[Link]))
# Print every response the browser receives
[Link]("response", lambda resp: print("Response:", [Link], [Link]))
Blocking Unwanted Requests (Speed Optimisation)
# Block all PNG and JPG images — makes tests faster
[Link]("**/*.png", lambda route: [Link]())
[Link]("**/*.jpg", lambda route: [Link]())
Intercepting API Calls
def handle_api(route):
print("API call intercepted:", [Link])
route.continue_() # let the real request through
[Link]("**/api/**", handle_api)
Mocking API Responses — Returning Fake Data
Instead of calling a real server, you can intercept an API request and return fake data. This is useful
for testing edge cases or working without a backend:
def mock_user(route):
[Link](
status=200,
content_type="application/json",
body='{"user": "FakeUser", "id": 99}'
Microsoft Playwright • Python Automation • Beginner Friendly
Playwright Python — Complete Beginner's Notes Page 31
[Link]("**/api/user", mock_user)
# Now when the page calls /api/user, it gets fake data
[Link]("[Link]
Microsoft Playwright • Python Automation • Beginner Friendly
Playwright Python — Complete Beginner's Notes Page 32
Section 15
API Testing with Playwright
Playwright is not only for browser automation — it also has a powerful HTTP client
built in for testing REST APIs directly without opening a browser. You can make GET,
POST, PUT, and DELETE requests and assert on the responses.
Making a Basic API Request
from playwright.sync_api import sync_playwright
with sync_playwright() as p:
# Create an API request context (no browser needed)
api = [Link].new_context()
# Make a GET request
response =
[Link]("[Link]
print([Link]) # 200
print([Link]()) # {"id": 1, "title": ...}
CRUD Operations — GET, POST, PUT, DELETE
api =
[Link].new_context(base_url="[Link]
[Link]")
# GET — Read a resource
r = [Link]("/posts/1")
print([Link]()["title"])
# POST — Create a new resource
r = [Link]("/posts", data={"title": "My Post", "body":
"Content", "userId": 1})
print([Link]) # 201 Created
# PUT — Update an existing resource
r = [Link]("/posts/1", data={"title": "Updated Title"})
# DELETE — Remove a resource
r = [Link]("/posts/1")
Microsoft Playwright • Python Automation • Beginner Friendly
Playwright Python — Complete Beginner's Notes Page 33
assert [Link] == 200
Query String Parameters
# Get posts by userId=1, limited to 5 results
r = [Link]("/posts", params={"userId": 1, "_limit": 5})
# This sends: GET /posts?userId=1&_limit=5
Microsoft Playwright • Python Automation • Beginner Friendly
Playwright Python — Complete Beginner's Notes Page 34
Section 16
Optimisation & Speed Tips
As your test suite grows, speed becomes important. This section covers techniques to
make your Playwright tests run significantly faster without sacrificing quality.
Block Unnecessary Resources
Images, fonts, and stylesheets are not needed for most tests. Blocking them can cut page load time
by 50% or more:
def block_resources(route):
if [Link].resource_type in ["image", "font",
"stylesheet"]:
[Link]() # do not load this
else:
route.continue_() # load everything else normally
[Link]("**/*", block_resources)
Disable JavaScript
If you only need to read static HTML content, disabling JavaScript speeds up page loads:
context = browser.new_context(java_script_enabled=False)
page = context.new_page()
[Link]("[Link] # loads without executing any
JavaScript
Run Tests in Parallel
By default Pytest runs tests one by one. With pytest-xdist you can run many tests simultaneously,
using all your CPU cores:
# Install the parallel runner
pip install pytest-xdist
# Run with 4 parallel workers
pytest -n 4
# Automatically use all available CPU cores
Microsoft Playwright • Python Automation • Beginner Friendly
Playwright Python — Complete Beginner's Notes Page 35
pytest -n auto
■ Tip: Parallel tests MUST be independent. Each test should not rely on data created by another test.
Section 17
Tips, Tricks & Debugging
This section collects the most useful debugging techniques, CLI shortcuts, and
advanced features that will save you hours of frustration.
Essential Pytest CLI Options
pytest -v # verbose — show each test name and result
pytest -s # show print() output in the terminal
pytest --tb=short # shorter, readable error tracebacks
pytest -x # stop on the first failing test
pytest --lf # re-run only the tests that failed last time
pytest -k "login or signup" # filter tests by name keyword
Python Debugger (pdb) — Pause and Inspect
If a test is failing and you cannot figure out why, you can pause execution and inspect the browser
state manually:
def test_debug(page):
[Link]("[Link]
import pdb; pdb.set_trace() # pauses here
# In terminal: type "n" for next line, "c" to continue, "q" to quit
Device Emulation — Test on Mobile
with sync_playwright() as p:
# Use a pre-defined device profile
iphone = [Link]["iPhone 13"]
context = [Link]().new_context(**iphone)
page = context.new_page()
[Link]("[Link] # renders as if on an iPhone 13
Microsoft Playwright • Python Automation • Beginner Friendly
Playwright Python — Complete Beginner's Notes Page 36
Evaluate JavaScript in the Browser
# Read the page title via JavaScript
title = [Link]("[Link]")
# Count how many list items are on the page
count =
[Link]('[Link]("li").length')
# Pass a Python value into JavaScript and get a result back
result = [Link]("(x) => x * 2", 21) # returns 42
`Generate HTML Test Reports
pip install pytest-html
pytest --html=[Link] --self-contained-html
# Open [Link] in your browser to see a visual test report
Microsoft Playwright • Python Automation • Beginner Friendly
Playwright Python — Complete Beginner's Notes Page 37
Section 18
GitHub API Testing
Combining Playwright's API client with GitHub's REST API lets you write tests that
create repositories, manage issues, and verify your CI pipeline — all from Python.
Setting Up Your GitHub Token Safely
Never paste your token directly in code — store it as an environment variable:
import os
# Read the token from the environment (set it in your terminal first)
# export GITHUB_TOKEN=ghp_yourtokenhere
token = [Link]["GITHUB_TOKEN"]
Authorised API Context
api = [Link].new_context(
base_url="[Link]
extra_http_headers={
"Authorization": f"token {token}",
"Accept": "application/[Link].v3+json"
}
)
Sample GitHub API Tests
def test_create_repo(api_context):
r = api_context.post("/user/repos", data={
"name": "my-test-repo",
"private": True
})
assert [Link] == 201
assert [Link]()["name"] == "my-test-repo"
def test_delete_repo(api_context):
r = api_context.delete("/repos/myuser/my-test-repo")
assert [Link] == 204
Microsoft Playwright • Python Automation • Beginner Friendly
Playwright Python — Complete Beginner's Notes Page 38
Section 19
Continuous Integration (CI/CD) with GitHub
Actions
CI automatically runs your Playwright tests every time you push code to GitHub. This
means bugs are caught immediately — before they reach production. GitHub Actions is
free for public repositories and provides this feature out of the box.
How CI Works
• You push code to GitHub (git push).
• GitHub Actions automatically detects the push.
• It spins up a virtual computer, installs your dependencies, and runs your tests.
• You see a green tick (passed) or red cross (failed) on your pull request.
• If tests fail, you fix the bug before merging.
GitHub Actions Workflow File
# .github/workflows/[Link]
name: Playwright Tests
on: [push, pull_request] # run on every push and pull request
jobs:
test:
runs-on: ubuntu-latest
steps:
# 1. Download your repository code
- uses: actions/checkout@v3
# 2. Install Python
- uses: actions/setup-python@v4
with:
python-version: "3.11"
# 3. Install Playwright and Pytest
- run: pip install pytest playwright pytest-playwright
# 4. Download the browsers
- run: playwright install --with-deps
Microsoft Playwright • Python Automation • Beginner Friendly
Playwright Python — Complete Beginner's Notes Page 39
# 5. Run the tests!
- run: pytest
Using Secrets Safely in CI
# In GitHub: Settings > Secrets > Actions > New Secret
# Add: Name = GITHUB_TOKEN, Value = your_actual_token
# Reference it in your workflow file:
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
Microsoft Playwright • Python Automation • Beginner Friendly
Playwright Python — Complete Beginner's Notes Page 40
Section 20
Data-Driven Testing
Data-driven testing means running the SAME test with DIFFERENT inputs
automatically.
Instead of writing 10 nearly identical tests, you write 1 test and provide 10 sets of data.
Pytest's parametrize decorator makes this simple.
Using [Link]
import pytest
@[Link]("username, password,
expected_message", [
("admin", "correct", "Dashboard"), # valid login
("admin", "wrongpass", "Invalid credentials"), # wrong password
("nouser", "anypass", "User not found"), # unknown user
])
def test_login(page, username, password, expected_message):
[Link]("[Link]
page.get_by_label("Username").fill(username)
page.get_by_label("Password").fill(password)
page.get_by_role("button", name="Login").click()
expect([Link](".message")).to_contain_text(expected_mess
age)
What the Output Looks Like
pytest -v
# Output:
# test_login[admin-correct-Dashboard] PASSED
# test_login[admin-wrongpass-Invalid...] PASSED
# test_login[nouser-anypass-User not found] PASSED
Microsoft Playwright • Python Automation • Beginner Friendly
Playwright Python — Complete Beginner's Notes Page 41
Section 21
Behaviour-Driven Development (BDD)
BDD is a way of writing tests in plain English so that even non-developers (project
managers, testers, business analysts) can read and understand them. It uses a specific
format called Gherkin with Given / When / Then steps.
What is the Given / When / Then Format?
• Given: The starting state — what is true before the action. Example: "Given I am on the
login page"
• When: The action the user takes. Example: "When I enter my username and password"
• Then: The expected result. Example: "Then I should see the dashboard"
Setup
pip install pytest-bdd
Feature File (Plain English Scenario)
# features/[Link]
Feature: User Login
Scenario: Successful login with valid credentials
Given I am on the login page
When I enter "admin" and "secret"
Then I should see the dashboard
Step Definitions (Python Code that Runs Each Step)
# test_login_bdd.py
from pytest_bdd import given, when, then, scenario
from playwright.sync_api import expect
@scenario("features/[Link]", "Successful login with
valid credentials")
def test_login(): pass
Microsoft Playwright • Python Automation • Beginner Friendly
Playwright Python — Complete Beginner's Notes Page 42
@given("I am on the login page")
def go_to_login(page):
[Link]("[Link]
@when('I enter "admin" and "secret"')
def enter_credentials(page):
page.get_by_label("Username").fill("admin")
page.get_by_label("Password").fill("secret")
page.get_by_role("button", name="Login").click()
@then("I should see the dashboard")
def check_dashboard(page):
expect(page).to_have_url("[Link]
Microsoft Playwright • Python Automation • Beginner Friendly
Playwright Python — Complete Beginner's Notes Page 43
Quick Reference Cheat Sheet
A handy one-page summary of the most commonly used Playwright commands. Keep
this as a bookmark or print it out for your desk.
Task Code
Launch browser (visible) [Link](headless=False)
Open a new tab browser.new_page()
Go to a URL [Link]("[Link]
Click button by role page.get_by_role("button", name="X").click()
Fill an input field page.get_by_label("Email").fill("a@[Link]")
Get element text [Link]("h1").inner_text()
Wait for element to appear page.wait_for_selector("#id")
Take a screenshot [Link](path="[Link]")
Take full-page screenshot [Link](path="[Link]", full_page=True)
Assert page title expect(page).to_have_title("Home")
Assert element visible expect([Link]("#el")).to_be_visible()
Assert element text expect([Link]("p")).to_have_text("Hi")
Mock an API endpoint [Link]("**/api", lambda r: [Link](...))
Block image loading [Link]("**/*.png", lambda r: [Link]())
Check a checkbox page.get_by_label("I agree").check()
Select a dropdown option [Link]("#dd").select_option("India")
Upload a file [Link]("input[type=file]").set_input_files("[Link]")
Accept a dialog popup [Link]("dialog", lambda d: [Link]())
context = [Link]().new_context(**[Link]["iPhone
Emulate iPhone 13 13"])
Run JavaScript [Link]("[Link]")
Microsoft Playwright • Python Automation • Beginner Friendly
Playwright Python — Complete Beginner's Notes Page 44
Run all tests pytest -v --headed
Run specific file pytest test_login.py -v
Run in parallel pytest -n auto
Generate code automatically playwright codegen [Link]
View a trace file playwright show-trace [Link]
Generate HTML report pytest --html=[Link] --self-contained-html
Save login session context.storage_state(path="[Link]")
Reuse login session browser.new_context(storage_state="[Link]")
Task Code
Record a video browser.new_context(record_video_dir="videos/")
Block all images+fonts [Link]() if resource_type in ["image","font"]
Microsoft Playwright • Python Automation • Beginner Friendly