Back to Courses
PLAYWRIGHT PYTHON — BUILD A PROFESSIONAL AUTOMATION FRAMEWORK IN 26 DAYS

Playwright Python: Zero to Job-Ready Automation Engineer

Learn Microsoft Playwright with Python from scratch — smart locators, auto-waiting, iFrames, Shadow DOM — and finish with a complete framework used in real companies: Page Objects, Excel data driving, Allure reports, and Jenkins CI/CD.

25+
Days of Training
14
Modules
100+
Topics Covered
Live
Framework

Who Is This Course For?

This course is for QA testers and Python learners who want to master the fastest-growing automation tool in the industry. You will go from writing your first sync_playwright() script all the way to building a professional B148_AFW framework — with config-driven browser selection, Page Object Model, Excel data driving, and an automated Jenkins pipeline that runs your tests on every code push.

Python Playwright Pytest Page Object Model Allure Reports Jenkins CI/CD BrowserStack Job-Ready

Your Learning Journey

First Script
Locators & Actions
Assertions & Sync
Pytest + Allure
AFW + Jenkins
01
Module 1
Introduction & Environment Setup
Days 1–2  |  10 Topics
WHY THIS MODULE

Playwright is Microsoft's answer to the flakiness and complexity of older automation tools. Before touching any code, you need to understand what it does differently and why companies are switching. Getting the environment right in Day 1 means zero setup headaches for the remaining 25 days.

What Makes Playwright Different
  • Playwright is a free Microsoft tool — supports Chromium (Chrome/Edge), Firefox, and WebKit (Safari) in one framework
  • Auto-waiting built in — it retries every action until the element is ready, unlike Selenium
  • Works with Desktop, Web, and Mobile browser testing from one API
  • Playwright vs older tools — modern locators, Shadow DOM support, built-in assertions
Setting Up and Your First Script
  • pip install playwright then playwright install — downloads all three browser binaries
  • with sync_playwright() as p: — context manager handles Playwright lifecycle safely
  • browser = p.chromium.launch(headless=False) — opens visible Chrome
  • page = browser.new_page() — create the page object you'll interact with throughout
  • page.goto(url), page.title(), browser.close() — the three-line test pattern
Browser Navigation
  • page.go_back(), page.go_forward(), page.reload() — browser control methods
  • page.set_viewport_size({"width": 1280, "height": 720}) — consistent screen size for all tests
  • Switching browsers: p.firefox, p.webkit — no code change needed beyond this line
02
Module 2
Playwright Smart Locators
Days 3–4  |  12 Topics
WHY THIS MODULE

Playwright's built-in locators are its biggest differentiator from Selenium. They describe elements the way a user sees them — "the button called Login" or "the field labelled Email" — not by internal IDs that change every release. This makes your tests readable and far more resilient to UI changes.

8 Built-in Smart Locators
  • page.get_by_placeholder("Search here...") — matches the greyed hint text inside an input
  • page.get_by_label("Username") — finds the input associated with a visible label
  • page.get_by_alt_text("company logo") — finds images by their description text
  • page.get_by_title("Close dialog") — matches the tooltip text on hover
  • page.get_by_text("Submit") — finds any element by its visible text
  • page.get_by_test_id("login-btn") — the most stable: a special attribute added by developers for automation
  • page.get_by_role("button", name="Sign In") — finds by what the element IS and what it SAYS
  • page.locator("css-or-xpath") — universal fallback when nothing else fits
How Matching Works
  • Partial match — searching "Log" returns "Login" and "Logout"; good for dynamic text
  • Exact match — exact=True — only returns a perfect match; good for specific buttons
  • Case-insensitive — useful when text capitalisation varies between environments
  • Role-based locators map to browser accessibility roles — your tests work even if styling changes
03
Module 3
CSS Selectors in Playwright
Days 4–6  |  12 Topics
WHY THIS MODULE

CSS selectors are the same rules used to style websites — repurposed as locators. They are faster to write and evaluate than XPath, and every front-end developer knows them. Playwright extends standard CSS with text-searching abilities, making it more powerful than CSS in any other tool.

Standard CSS Selectors
  • Tag: button, input, div — match all elements of that type
  • ID: #username — the sharpest selector, one element only
  • Class: .btn-primary, .btn-primary.active — match by CSS styling class
  • Attribute: input[type='password'], a[href='/login']
  • Child: form > button — direct child only; Descendant: form button — any depth
  • The standard CSS limitation: you cannot select an element by its text content
Playwright's CSS Extensions
  • :text('Submit') — Playwright's own CSS extension for partial text matching
  • :text-is('Submit') — exact text match (case-sensitive)
  • :has-text('Welcome') — container that contains the text anywhere inside it
  • Attribute wildcards: [class*='btn'] contains, [href^='https'] starts-with, [src$='.png'] ends-with
  • When CSS outperforms XPath — for well-structured apps, CSS is shorter and faster
04
Module 4
XPath — Navigating the DOM Structure
Days 5–7  |  14 Topics
WHY THIS MODULE

XPath is the most powerful locator when you need to navigate relationships between elements — like "find the input next to the label that says Password". Every senior automation engineer uses XPath daily for complex pages where IDs don't exist and CSS can't traverse up the tree.

XPath Basics
  • Relative XPath: //input[@id='username'] — start searching from anywhere in the page
  • Absolute XPath: /html/body/form/input — full path from root; breaks when page structure changes
  • Match by text: //button[text()='Login'] — CSS can't do this; XPath can
  • Combine conditions: //tr[@class='active' and @data-id='5'] — must match both
XPath Functions for Dynamic Pages
  • starts-with(@id, 'user_') — for auto-generated IDs with a predictable prefix
  • contains(@class, 'btn') — partial class name match
  • contains(text(), 'Welcome') — partial text match, works around whitespace
  • Browser XPath 1.0 limitation — ends-with() requires 2.0; use contains() workaround
XPath Tree Navigation (Axes)
  • following-sibling — "the input that comes after the label saying Email"
  • parent — "the div that directly contains this button"
  • ancestor — "the form that wraps this field no matter how deep"
  • preceding-sibling — "the cell to the left of the cell showing John"
  • I&D XPath — Independent & Dependent: anchor on a stable element, then traverse to the target
  • Real-world practice: complex product tables on Flipkart, Amazon, and Myntra
05
Module 5
Filters, Chaining & Indexing
Days 7–10  |  12 Topics
WHY THIS MODULE

Real application pages return dozens of matching elements — all rows in a table, all buttons in a form. Playwright's filter and chaining system lets you narrow down to exactly the one element you need with clean, readable code. This is the difference between fragile tests and robust ones.

Filtering Results
  • .filter(has_text="Active") — "from all matching rows, keep only those showing Active"
  • .filter(has_not_text="Disabled") — exclude rows that contain this text
  • .filter(has=page.get_by_role("checkbox")) — keep only elements that contain a checkbox inside
  • .filter(visible=True) — exclude hidden elements from your results
Chaining Conditions
  • .and_(other_locator) — must match both locators simultaneously — AND logic
  • .or_(fallback_locator) — try the first; if not found, try the second — OR logic
  • Scoped locator: page.locator("table").locator("tr") — find rows only inside that table
Position and Collection Methods
  • .first — the top-most matching element; avoids index errors on single results
  • .last — the bottom-most match; useful for "most recent" records
  • .nth(2) — third element (0-based index); for known position in a list
  • .count() — how many elements matched? Use in assertions to verify result counts
  • .all() — returns a Python list of all matches — iterate to check each one
06
Module 6
iFrames & Shadow DOM
Day 8  |  9 Topics
WHY THIS MODULE

Payment forms, video players, map embeds, and rich text editors are all loaded inside iFrames — separate documents within the main page. Shadow DOM is increasingly common in modern component libraries. Failing to handle them is the reason many automation projects get stuck on "element not found" for days.

iFrames — Playwright's Elegant Approach
  • An iFrame is a web document embedded inside the main document — a page within a page
  • page.frame_locator("iframe[name='payment']") — scope directly into the frame
  • Work inside the frame just like the main page — no switching, no context juggling
  • page.frame("frame-name") — access a frame object by its name attribute
  • page.frames — list all frames — useful for discovery when names are unknown
  • Nested frames: page.frame_locator("outer").frame_locator("inner") — one line for two levels
Shadow DOM — Playwright's Auto-Pierce Advantage
  • Shadow DOM encapsulates web component internals — normal selectors can't see inside
  • Playwright automatically pierces Shadow DOM for most locator types — no manual code needed
  • This is a major advantage over Selenium which requires manual shadow_root extraction
07
Module 7
Element Actions — The Full Interaction Toolkit
Days 9–12  |  18 Topics
WHY THIS MODULE

Finding an element is only step one. Playwright has 12+ ways to interact with elements — from simple clicks to character-by-character typing, drag-and-drop, hover menus, and right-clicks. These methods cover everything a real user can do, ensuring your automation test reflects actual application behaviour.

Standard Element Interactions
  • locator.click() — the single most-used action in every test
  • locator.fill("value") — wipe field and type at once; fastest for simple inputs
  • locator.press_sequentially("value") — type character by character, triggering keydown/keyup events — for autocomplete fields
  • locator.clear() — clear without typing; for conditional form interactions
  • locator.press("Enter") — press a key; Enter submits, Tab moves focus, Escape cancels
  • locator.check() / locator.uncheck() — checkboxes; Playwright only acts if state needs to change
  • locator.select_text() — select all text in a field (Ctrl+A equivalent)
  • locator.focus() / locator.blur() — trigger form field validation events
  • locator.scroll_into_view_if_needed() — bring off-screen element into viewport first
  • locator.hover() — reveal dropdown menus and tooltips without clicking
Advanced Interactions
  • locator.dblclick() — double-click for inline editing or file selection
  • locator.click(button="right") — right-click to open context menus
  • page.drag_and_drop("source-css", "target-css") — Kanban cards, file upload zones
  • locator.bounding_box() — returns {"x", "y", "width", "height"} — for coordinate-based interactions
Keyboard, Mouse & JavaScript API
  • page.keyboard.press("Control+A") — shortcut keys and combinations
  • page.keyboard.down("Shift") / page.keyboard.up("Shift") — hold modifier keys
  • page.mouse.wheel(0, 300) — scroll the page programmatically
  • page.evaluate("() => window.scrollTo(0, 500)") — JavaScript execution as ultimate fallback
08
Module 8
Verification Methods — Is the Right Thing Showing?
Days 13–14  |  16 Topics
WHY THIS MODULE

Clicking things is easy. Knowing whether the result was correct is the hard part — and it's what makes automation useful as testing. These verification methods let you read back exactly what the page shows, enabling you to write tests that actually catch real bugs.

Element State Checks
  • locator.is_visible() — True if element is in DOM and visible to users
  • locator.is_hidden() — True if element is absent or set to display:none
  • locator.is_enabled() — True if the element can be interacted with
  • locator.is_disabled() — True if the element has the disabled attribute
  • locator.is_checked() — True for checked checkboxes and selected radio buttons
Reading Page Content
  • locator.text_content() — all text including any hidden nodes in the subtree
  • locator.inner_text() — only the text visible to users — respects CSS visibility
  • locator.input_value() — the current value typed into an input or textarea
  • locator.get_attribute("class") — any HTML attribute value for assertions
  • locator.inner_html() — the HTML inside an element — for structure assertions
  • locator.all_text_contents() — list of text from every matching element — for table verification
Debugging & Evidence Collection
  • locator.highlight() — visually marks the found element — debugging without console
  • locator.aria_snapshot() — accessibility tree — verify how screen readers perceive the element
  • locator.screenshot(path="element.png") — screenshot of just this element
  • page.screenshot(path="full.png") — full-page screenshot for bug reports
  • page.screenshot(type="jpeg", quality=80) — optimised size for CI/CD artifact storage
09
Module 9
Listbox & Select Options
Days 14–15  |  8 Topics
WHY THIS MODULE

Dropdown menus appear in almost every application — country selection, role assignment, status updates. Playwright's select_option() method provides three different ways to choose from a list, covering every scenario from simple text selection to database-coded values.

Selecting from HTML Dropdowns
  • locator.select_option("CA") — select by the hidden value attribute in HTML
  • locator.select_option(label="Canada") — select by the text users see on screen
  • locator.select_option(index=2) — select the third item (0-based) by position
  • locator.select_option(["option1", "option2"]) — select multiple options at once
  • locator.all_text_contents() — read all available option texts as a Python list
Dynamic Option Lists
  • Iterating over all options — check which are enabled, disabled, or selected
  • Using page.evaluate() as a fallback for custom (non-HTML) dropdown widgets
  • Assignment exercises: display options in reverse order, alphabetically sorted — interview-ready skills
10
Module 10
Popups, Dialogs & Permissions
Day 16  |  10 Topics
WHY THIS MODULE

Browser popups interrupt the test flow — the entire page is blocked until you respond. Permissions like geolocation and camera access appear on login and mapping applications. Without knowing how to handle these, your test simply freezes, and you can't automate those features at all.

JavaScript Dialog Handling
  • Three popup types: alert (just OK), confirm (OK/Cancel), prompt (text input + OK/Cancel)
  • page.once("dialog", lambda d: d.accept()) — handle the very next popup only
  • page.on("dialog", my_handler) — handle ALL popups for the entire test automatically
  • dialog.accept() — click OK; works for alert and confirm dialogs
  • dialog.dismiss() — click Cancel on a confirm or prompt
  • dialog.message — read the popup message before deciding how to respond
  • dialog.type — check whether it's an alert, confirm, or prompt
  • dialog.accept("typed text") — fill in the prompt text box and confirm
Browser Permission Popups
  • context.grant_permissions(["geolocation"]) — pre-approve before navigating
  • Notifications, camera, microphone — granted at the browser context level, not mid-test
11
Module 11
API Testing & Synchronization
Days 18–19  |  14 Topics
WHY THIS MODULE

Test flakiness — tests that randomly pass and fail — is the single biggest frustration in automation. It almost always comes from not waiting for the page to be ready. Playwright offers five different waiting strategies, each suited to a specific scenario. Choosing the right one makes your tests reliable on every run.

API Testing Built In
  • Playwright can make direct HTTP requests without a browser — faster than UI for validation
  • Broken link verification — scan a page's links via API calls instead of clicking each one (100x faster)
  • JSON response handling — assert API data directly from Playwright test code
Synchronization Strategies
  • Playwright's default 30-second action timeout — it retries automatically before failing
  • page.set_default_timeout(5000) — tune the global patience for your app's speed
  • locator.wait_for(state="visible") — wait for: attached, visible, hidden, detached
  • page.wait_for_url("**/dashboard") — wait until URL matches a pattern after navigation
  • page.wait_for_load_state("networkidle") — wait until all background XHR requests settle
  • page.wait_for_load_state("domcontentloaded") — HTML is parsed; images may still be loading
  • page.wait_for_load_state("load") — everything including images and stylesheets done
  • page.wait_for_selector("css-or-xpath") — legacy-style wait by selector string
  • page.wait_for_event("download") — wait for a specific browser event to fire
  • page.wait_for_function("() => window.isLoaded === true") — custom JavaScript condition
  • page.set_default_navigation_timeout(10000) — separate timeout just for page navigation
12
Module 12
Expect API — Built-in Playwright Assertions
Day 20  |  12 Topics
WHY THIS MODULE

Plain Python assert fails immediately if the element isn't ready yet. Playwright's expect() API automatically retries the assertion for 5 seconds before failing — meaning your checks are resilient to tiny timing differences without adding sleep timers everywhere.

Page-Level Assertions
  • from playwright.sync_api import expect — import once, use everywhere
  • expect(page).to_have_title("Dashboard") — fails if title doesn't match within 5 seconds
  • expect(page).to_have_url("https://app.example.com/home")
  • Regex matching: expect(page).to_have_title(re.compile("dash", re.IGNORECASE)) — partial, case-insensitive
  • 5-second default — long enough to handle slow renders without hard-coded sleeps
Element-Level Assertions
  • expect(locator).to_be_visible() — fail if element is absent or hidden
  • expect(locator).to_be_hidden() — fail if element is showing when it should be hidden
  • expect(locator).to_be_enabled() — fail if button is greyed out
  • expect(locator).to_be_disabled() — fail if field is editable when it shouldn't be
  • expect(locator).to_be_focused() — fail if cursor is not in this field
  • expect(locator).to_be_checked() — fail if checkbox is unchecked
  • expect(locator).to_have_text("Welcome, John") — fail if message doesn't match
13
Module 13
Remote Testing & Cross-Browser
Day 21  |  9 Topics
WHY THIS MODULE

No company runs tests on a developer's laptop. Tests run on dedicated CI/CD servers — often headless Linux machines with no screen. Playwright can run as a server on any machine, and your Python script connects to it over a WebSocket. This is the same architecture used in every professional testing setup.

Playwright Remote Server
  • Install Node.js Playwright on the remote machine: npm install playwright
  • Start the browser server: npx playwright run-server --port=8082 --host=0.0.0.0
  • Connection URL: ws://server-ip:8082/ — WebSocket protocol
  • Python connects: p.chromium.connect("ws://server-ip:8082/") — rest of your code unchanged
  • Remote machine runs headless — no display required; perfect for CI/CD servers
Cloud & Cross-Platform Testing
  • Compatibility testing across Windows, macOS, and Linux without owning every machine
  • BrowserStack — cloud service with thousands of real browser/OS combinations on demand
  • Connect to BrowserStack exactly like your own remote server — identical Python code
  • Multi-browser parallel testing via remote grid — all browsers tested simultaneously
14
Module 14
B148_AFW Framework & Jenkins CI/CD
Days 22–26 + pytest Days 41–43  |  24 Topics
WHY THIS MODULE

This is the module that transforms you from someone who writes automation scripts into someone who builds automation frameworks. B148_AFW is a real, production-grade framework — the kind you'll find in every QA team at scale. Mastering this module is what justifies "Automation Engineer" on your CV.

Pytest — The Test Runner Powering the Framework
  • @pytest.fixture(autouse=True) — browser opens before each test, closes after, automatically
  • @pytest.mark.smoke, @pytest.mark.regression — tag tests and run subsets on demand
  • conftest.py — shared fixtures for all test files; no import needed
  • Fixture scopes: session opens browser once for all tests; function opens fresh for each
  • @pytest.fixture(params=[user1, user2]) — run fixture once per data item automatically
  • request.param — access the current data item inside the parametrized fixture
  • pytest -n 4 (pytest-xdist) — run 4 tests simultaneously on 4 CPU cores
  • pytest -vs --alluredir=allure-results — run tests and collect Allure data
B148_AFW — The Complete Framework
  • Three folders: generics/ (setup), page/ (page classes), tests/ (test files)
  • base_test.pyBaseTest class with @pytest.fixture(autouse=True) that reads config and launches browser
  • config.properties — one file controls browser choice, URL, timeout, local vs remote
  • pyjavaproperties: p = Properties(); p.load(...) — read config without hardcoding values
  • Switch from Chrome to Firefox: change one line in config — no code change
  • Encapsulation (DUI) — Declaration, Initialization, Utilization in every page class
  • login_page.pyset_username(), set_password(), click_go_button() — tests call methods, not locators
  • utility.pyopenpyxl-based Excel reader shared across all tests
GitHub, Allure & Jenkins Pipeline
  • GitHub SCM — version control, team collaboration, pull requests from PyCharm
  • allure serve allure-results — launch interactive dashboard with step details and screenshots
  • Jenkins Freestyle Project with Python builder step for running pytest
  • Build trigger: push to GitHub → Jenkins detects → tests run automatically
  • Scheduled builds: H * * * * cron syntax — run at specific times daily
  • Allure post-build action — publish report inside Jenkins UI after every run
  • Build stability tracking — see pass/fail trends across 10, 20, 50 consecutive runs
  • Full 21-step B148_AFW execution: config read → browser launch → test run → Allure report published
Bonus
Interview Preparation — Playwright Python

Playwright vs Selenium

Answer confidently: Playwright auto-waits on every action (no sleep timers), supports Chromium/Firefox/Safari from one API, automatically pierces Shadow DOM, and has expect() assertions with built-in retry. These are real daily advantages, not just marketing.

Locator Strategy Questions

Explain the 8 built-in locators and your priority order: get_by_test_id() when available (most stable), get_by_role() for semantic elements, get_by_placeholder() for inputs, CSS for well-structured pages, and XPath with axes for complex dynamic tables.

Test Flakiness Questions

Explain Playwright's five waiting strategies and when each applies: wait_for(state="visible") for appearing elements, wait_for_load_state("networkidle") after Ajax-heavy actions, wait_for_url() after redirects. And explain why expect() beats plain assert for resilience.

Framework Architecture

Describe B148_AFW's design: BaseTest reads config.properties and launches browser automatically, page classes encapsulate all locators (DUI pattern), tests only call methods, utility.py reads Excel data, and Jenkins publishes Allure reports on every push.

Pytest Integration Questions

Explain conftest.py as the automatically-discovered fixture file. Describe scope="session" — one browser for all 200 tests. Explain fixture parametrization with params=[] and request.param. Know how pytest-xdist -n 4 cuts test time by running in parallel.

Remote & CI/CD Questions

Describe the remote server setup: Node.js Playwright server started with npx playwright run-server, Python connects via p.chromium.connect("ws://..."). For Jenkins: Git push triggers build → pytest runs → Allure results collected → report published. Zero human intervention.