Before pytest takes over the browser for you, it is worth writing one complete Playwright script by hand. You will see exactly what happens when a test starts: Playwright launches a browser, creates an isolated context, opens a page, performs actions and closes everything down. Once you understand these four objects, every fixture and error message later in the course will make sense.
๐ What You’ll Learn: the Playwright object hierarchy (Playwright, Browser, BrowserContext, Page), launch options like headless and slow_mo, interacting with a real form, reading text and attributes, taking screenshots, proving that contexts are isolated, and closing resources safely with with and try/finally.
๐งฑ The Four Objects You Always Use
Every Playwright program, including every pytest test you will write, uses the same chain of objects:
| Object | Created by | What it represents |
|---|---|---|
Playwright | sync_playwright() | The running Playwright driver. Gives you chromium, firefox, webkit and devices. |
Browser | p.chromium.launch() | One browser process (expensive to start, so reuse it). |
BrowserContext | browser.new_context() | An isolated, incognito-like session with its own cookies, storage and cache. Cheap to create. |
Page | context.new_page() | One tab inside a context. Almost all actions happen here. |
Think of the browser as a building, each context as a separate flat with its own key, and each page as a room in a flat. Two contexts never share cookies or logins, which is why Playwright tests do not leak state into each other.
โ๏ธ A Complete First Script
This script opens the SQA Tools Automation Practice Page, fills in some fields, reads values back and saves a screenshot. Save it as first_script.py:
๐ first_script.py
from playwright.sync_api import sync_playwright
with sync_playwright() as p:
# 1. Launch a browser and create an isolated context with one page
browser = p.chromium.launch()
context = browser.new_context()
page = context.new_page()
# 2. Open the practice page
page.goto("https://sqatools.in/automation-practice-page/")
print("Page title:", page.title())
# 3. Type into text fields, located by their placeholder text
page.get_by_placeholder("Enter username").fill("qa_learner")
page.get_by_placeholder("Enter password").fill("Secret@123")
# 4. Choose a radio button, tick a checkbox and pick a dropdown option
page.get_by_role("radio", name="Female").check()
page.get_by_role("checkbox", name="Python").check()
page.locator("select").first.select_option("India")
# 5. Read the values back from the page
print("Username field:", page.get_by_placeholder("Enter username").input_value())
print("Python checked:", page.get_by_role("checkbox", name="Python").is_checked())
print("Second table row:", page.get_by_role("row").nth(2).inner_text())
# 6. Save a screenshot of the visible part of the page
page.screenshot(path="practice-page.png")
# 7. Close everything
context.close()
browser.close()
Run it:
โจ๏ธ Terminal
python first_script.py
๐ฅ๏ธ Output
Page title: Automation Practice Page - SQA Tools
Username field: qa_learner
Python checked: True
Second table row: 2 Rahul Tester
A file named practice-page.png appears next to your script.
What each part does
- Launch and open.
launch()starts the browser,new_context()creates a clean session, andnew_page()opens a tab in it. - **
page.goto(url)** navigates and waits for the page’sloadevent before the next line runs. - **
get_by_placeholder("Enter username")creates a locator**: a description of how to find an element. Nothing is searched yet.fill()then finds the input, waits until it is editable, clears it and types the text. - **
get_by_role("radio", name="Female")** finds the element the way a screen reader would: by its role (radio) and accessible name (Female).check()ticks radios and checkboxes.select_option("India")picks the option whose value or label is India from the first<select>on the page. - Reading values.
input_value()returns what is typed in a field,is_checked()returnsTrueorFalse, andinner_text()returns the visible text.nth(2)picks the third match (counting starts at 0, and row 0 is the header row). The cells in the printed row are separated by tab characters. - **
page.screenshot(path=...)** saves a PNG. Addfull_page=Trueto capture the whole scrolling page. - Closing. Closing the context closes its pages; closing the browser ends the process. Leaving the
withblock then stops the Playwright driver.
๐ก Tip: Notice there is no time.sleep() anywhere. Every action waits automatically until its element is ready. This auto-waiting is the main reason Playwright tests are stable.
๐ Watch the Browser: headless and slow_mo
By default the browser is headless: it runs without a window, which is faster and works on servers. While learning, it helps to watch. Save this as launch_options.py:
๐ launch_options.py
from playwright.sync_api import sync_playwright
with sync_playwright() as p:
browser = p.chromium.launch(
headless=False, # show the browser window
slow_mo=500, # wait 500 ms after each Playwright action
)
page = browser.new_page()
page.goto("https://sqatools.in/automation-practice-page/")
page.get_by_role("button", name="Hover Over Me").hover()
print("Hovered on Chromium version", browser.version)
browser.close()
headless=Falseopens a real window so you can follow along.slow_mo=500adds a 500 millisecond pause after every action. Use it only for watching or demos, never in real test suites.browser.new_page()is a shortcut that creates a context and a page in one call. It is handy in quick scripts; when you need several pages in the same session, create the context yourself.browser.versionreturns the browser’s version string.
๐ฅ๏ธ Output
Hovered on Chromium version 156.0.8078.4
To try another engine, change one word: p.firefox.launch(...) or p.webkit.launch(...). The rest of the script stays the same.
๐ Contexts Are Isolated
This is one of Playwright’s most useful ideas. Two contexts in the same browser behave like two different people on two different computers. Save this as contexts.py:
๐ contexts.py
from playwright.sync_api import sync_playwright
with sync_playwright() as p:
browser = p.chromium.launch()
# Two independent sessions in ONE browser process
alice = browser.new_context()
bob = browser.new_context()
alice_page = alice.new_page()
bob_page = bob.new_page()
alice_page.goto("https://sqatools.in/automation-practice-page/")
bob_page.goto("https://sqatools.in/automation-practice-page/")
# Alice stores a value in her browser's localStorage
alice_page.evaluate("localStorage.setItem('user', 'alice')")
# Bob cannot see it: each context has its own storage
print("Alice sees:", alice_page.evaluate("localStorage.getItem('user')"))
print("Bob sees: ", bob_page.evaluate("localStorage.getItem('user')"))
print("Contexts open:", len(browser.contexts))
browser.close()
๐ฅ๏ธ Output
Alice sees: alice
Bob sees: None
Contexts open: 2
page.evaluate("...")runs JavaScript inside the page and returns the result to Python. JavaScriptnullarrives in Python asNone.- Bob’s context has no idea what Alice stored, even though both pages are in the same browser.
This is exactly what pytest-playwright does for you: one browser for the whole test session, and a brand-new context and page for every test. Tests start fast, and no cookies or logins leak from one test into the next.
๐งน Closing Safely When Something Fails
If a line throws an error, the code after it never runs. The with sync_playwright() block already guarantees that the driver stops. To make sure the browser itself is closed too, wrap your steps in try/finally. Save this as try_finally.py:
๐ try_finally.py
from playwright.sync_api import sync_playwright, TimeoutError as PlaywrightTimeoutError
with sync_playwright() as p:
browser = p.chromium.launch()
page = browser.new_page()
try:
page.goto("https://sqatools.in/automation-practice-page/")
# This button does not exist, so the click times out after 2 seconds
page.get_by_role("button", name="Does Not Exist").click(timeout=2000)
except PlaywrightTimeoutError as error:
print("Step failed:", error.message.splitlines()[0])
finally:
browser.close() # always runs, even after an error
print("Browser closed")
๐ฅ๏ธ Output
Step failed: Locator.click: Timeout 2000ms exceeded.
Browser closed
TimeoutErrorfromplaywright.sync_apiis Playwright’s own timeout error. Importing it with an alias avoids confusing it with Python’s built-inTimeoutError.timeout=2000overrides the default action timeout of 30 seconds for this one click.error.messageholds the full message; the first line names the action and the timeout. The lines after it (the call log) show what Playwright was waiting for.- The
finallyblock always runs, so the browser never stays open.
๐ Note: You will rarely write try/finally around tests yourself. pytest fixtures handle cleanup automatically, even when a test fails. You will see this in the next lesson.
โ Frequently Asked Questions
๐ค What is the difference between browser.new_page() and context.new_page()?
browser.new_page() creates a new context and a page in it in one step, and closing that page also closes its context. context.new_page() adds a page to an existing context, so several pages can share the same cookies and login.
๐ค Why does my script finish but the browser window closes immediately?
The script reached browser.close(). To pause and look around, add page.pause() (it opens the Playwright Inspector) or run with slow_mo. You will learn debugging in Module 11.
๐ค Can I connect to a browser that is already open?
Yes, with p.chromium.connect_over_cdp("http://localhost:9222") for Chromium browsers started with remote debugging enabled. Most test suites launch their own browser instead, because it is more reliable.
๐ค What is the default timeout for actions?
30 seconds for actions such as click() and fill(), and 30 seconds for navigation. You can change it per call with timeout= or for a whole context with context.set_default_timeout().
๐ Summary
sync_playwright()gives you aPlaywrightobject; it launches aBrowser, which createsBrowserContexts, which openPages.- Locators such as
get_by_placeholder()andget_by_role()describe elements; actions such asfill()andcheck()wait automatically. headless=Falseandslow_mohelp you watch a script; contexts keep sessions fully isolated.- Close what you open, or let
withblocks and pytest fixtures do it for you.
Next, you will turn this knowledge into your first real pytest test, where the page fixture does all of this setup and cleanup for you.
๐ฌ Watch the Playwright tutorial series on YouTube!
Subscribe to SqaToolsByDY • Practice on the SQA Tools Automation Practice Page
1 thought on “Your First Playwright Python Script with sync_playwright (Browser, Context and Page)”