Back to Courses
Playwright JS — From Complete Beginner to Automation Engineer in 33 Days

Playwright JS —
From Beginner to Automation Engineer in 33 Days

33 days of real automation work — learn to find, click, verify, and report with Playwright and JavaScript, finishing with a complete professional framework that you built yourself.

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

Who Is This Course For?

Built for manual testers, career changers, and developers who want to master Playwright automation with JavaScript. You will learn to find any element on any page, perform every type of browser action, verify results professionally, and finish the course with a complete B149_AFW framework — POM, Allure reports, GitHub, and Jenkins — that you can show in an interview on day one.

Manual Testers Selenium Users Switching to Playwright QA Job Seekers SDET / Automation Engineers JavaScript Developers

33-Day Learning Path

Week 1 — Setup, all locator types, CSS & XPath
Week 2 — Actions, filtering, iFrames, special elements
Week 3 — API testing, sync, assertions, data-driven
Week 4–5 — B149_AFW: POM, Allure, GitHub, Jenkins
01
Introduction & Setup
Days 1–2  |  Node.js, VS Code, npm init playwright@latest, your first test
What You Will Learn
  • Playwright — free, open-source, by Microsoft; vs Selenium comparison
  • Browser support: Chromium (Chrome/Edge), Firefox, WebKit (Safari)
  • Install Node.js and verify with node --version
  • Set up a project with npm init playwright@latest and select JavaScript
  • Set "type":"module" in package.json for modern ES imports
  • Playwright ships its own browsers — not the ones installed on your laptop
  • Every Playwright method is async — always use await
  • Run: npx playwright test demo.spec.js --headed
  • test(title, async({page}) => {}) — the {page} fixture is auto-injected
  • Install "Playwright Test for VS Code" extension — run tests with one click
  • Configure headless/headed in playwright.config.js
  • Available fixtures in tests: {page}, {context}, {browser}
Why This Module

Playwright is now the industry's fastest-growing automation tool — adopted by Microsoft, Google, and hundreds of product companies globally. Setting it up correctly from the start, especially understanding async/await and the {page} fixture, means every module that follows will make immediate sense. Companies hiring for Playwright roles expect you to be able to initialise and run a project from scratch in an interview.

02
Web Elements & Page Navigation API
Day 2  |  HTML basics, object hierarchy, page.* methods, slowMo
What You Will Learn
  • Every button, input, and link is built from HTML — tag + attributes + text
  • Open DevTools (F12) and inspect the code behind any element
  • Object hierarchy: playwright → browser → context → page
  • page.goto(url) — open a web address; page.title() — read current title
  • page.goBack() / goForward() / reload() / close()
  • page.setViewportSize({width, height}) — resize the browser window
  • page.evaluate(fn) — run JavaScript directly inside the browser
  • launchOptions:{slowMo:2000} — slow down each action by 2 seconds
Why This Module

You cannot automate something you do not understand. Knowing that every web element is a piece of HTML with a tag, attributes, and text is what makes every locator module possible. The playwright → browser → context → page hierarchy is also how Playwright isolates test sessions — understanding it prevents hours of debugging when you later run tests in parallel or manage multiple tabs.

03
Smart Locators — 7 Semantic APIs
Days 3–4  |  getByRole, getByLabel, getByTestId and 4 more
What You Will Learn
  • getByAltText() — matches the alt attribute on images; not case-sensitive, allows partial match
  • getByTitle() — matches the title tooltip attribute; not CS, allows PM
  • getByPlaceholder() — matches the greyed-out hint text inside inputs
  • getByLabel() — finds an input by the label that describes it
  • getByText() — finds any element by its visible text content
  • getByTestId() — matches data-testid; case-sensitive, no partial match
  • getByRole(role, {name}) — finds by ARIA type: button, textbox, link, etc.
  • {exact:true} — force case-sensitive exact match (works for locators 1–5, 7)
  • Timeout error after 30s when locator finds zero elements
  • Strict mode violation when locator matches more than one element
  • Debug live with Playwright Inspector: npx playwright test --headed --debug
  • Open inspector mid-test: await page.pause()
Why This Module

Playwright's smart locators (getByRole, getByLabel, getByText) are designed to survive UI redesigns — they look for meaning, not HTML structure. Using them means your tests keep working even when developers change a button's class or ID. This is the approach Playwright's own documentation recommends first, and what experienced automation engineers reach for before writing a single XPath.

04
CSS Selectors
Day 4  |  tag[attr='val'], #id, .class, AND/OR, ^= *= $=, pseudo-classes
What You Will Learn
  • Standard CSS syntax: tag[attributeName='value']
  • ID shortcut: input[id='u1']input#u1#u1
  • Class shortcut: button[class='btn']button.btn
  • Multi-class element: button.btn.btn-lg.btn-primary
  • AND condition: [name='n1'][title='click me'] — both must match
  • OR condition: [name='n1'],[title='click me'] — either matches
  • Partial match: ^= starts-with · *= contains · $= ends-with
  • Text support via Playwright pseudo-classes: :has-text(), :text(), :text-is()
  • Verify selectors in DevTools → Elements → Ctrl+F before using in code
  • CSS limitation: no native text support — use pseudo-classes or smart locators instead
Why This Module

CSS is the fastest locator strategy in Playwright and often the most readable — #login-btn is much cleaner than a long XPath. Understanding partial match operators like *= and ^= is especially useful when element IDs are generated dynamically (e.g. item-4892) — you can target just the stable prefix. CSS is also the backbone of the :has-text() pseudo-class used heavily in test filtering.

05
XPath — Basic
Day 5  |  Absolute vs relative, @attr, text(), and/or/not, starts-with/contains
What You Will Learn
  • XPath describes the path to an element like an address in the HTML tree
  • Absolute: /html/body/div[2]/input — full path from root (fragile)
  • Relative: //input[@name='username'] — search anywhere in the tree (preferred)
  • Index (1-based): //input[1], (//img)[last()], (//img)[3]
  • Wildcard: //*[@id='a1'] — any tag with that attribute
  • Logical AND: //input[@name='x' and @type='text']
  • Logical OR: //input[@name='x' or @id='y']
  • Logical NOT: //input[not(@placeholder='p')]
  • Exact text: //a[text()='Google'] — or dot notation: .//a[.='Google']
  • Partial text — starts-with: //p[starts-with(text(),'User')]
  • Partial text — contains: //button[contains(.,'Login')]
  • Browser supports XPath 1.0 only — ends-with() is not available natively
Why This Module

XPath is the most powerful locator strategy — it is the only one that can match elements by their text content, navigate up to parent elements, and handle completely dynamic pages. Every automation engineer needs it in their toolkit for situations where CSS and smart locators fall short. Understanding contains() and and/or/not is what separates a capable engineer from one who gets stuck on hard pages.

06
XPath — Axes & Dependent Element Traversal
Days 6–7  |  parent, ancestor, following-sibling, IE & DE pattern
What You Will Learn
  • child / — navigate to a direct child element
  • parent /.. — move up to the containing element
  • descendant // — reach any element inside a container, any depth
  • ancestor — find the table that contains a specific cell: //th[text()='Subject']/ancestor::table
  • following-sibling — move sideways to the next element: //th[text()='Subject']/following-sibling::th
  • preceding-sibling — move back to an earlier sibling with optional index
  • IE & DE pattern — start from a stable known element (IE) and navigate to a dynamic one (DE)
  • Full traversal example: //td[.='Pencil']/../td[8] — find price next to product name
  • Checkbox by row: //td[.='soap']/..//input[@type='checkbox']
  • Real-world practice on Flipkart, Amazon, and MakeMyTrip dynamic listings
Why This Module

Most real-world automation challenges involve dynamic tables, e-commerce listings, and dashboards where prices, statuses, and IDs change every page load. The IE & DE pattern — anchor to something stable, navigate to something dynamic — is the professional solution. This is exactly the technique used in production frameworks to automate complex data-entry and verification tasks that no other locator strategy can handle.

07
Locator Filtering, Chaining & iFrames
Days 7–9  |  hasText, first(), and(), all(), frameLocator, Shadow DOM
Filtering & Multi-Element Handling
  • .filter({hasText:'...'}) — keep only elements containing this text
  • .filter({hasNotText:'...'}) — exclude elements containing this text
  • .filter({has: page.locator('...')}) — keep elements that contain a child element
  • .filter({visible:true}) — visible elements only
  • .first() / .last() / .nth(n) — pick by position (0-indexed)
  • .count() — total number of matched elements
  • .and(locator) / .or(locator) — combine two locator conditions
  • .all() — get all matches as an array to loop through
  • .allTextContents() / .allInnerTexts() — all visible text as string array
iFrames & Shadow DOM
  • An <iframe> is a page embedded inside another page — common in payment forms and ads
  • page.frameLocator('#f1').locator('#t2') — locate the frame then the element inside
  • page.locator('#f1').contentFrame().locator('#t2') — via content frame method
  • page.frame({name:'n1'}) / page.frame({url:'...'}) — access by name or URL
  • page.frames()[1] — access frame by index in the frames array
  • Shadow DOM — Playwright can reach elements inside shadow roots directly without special handling
Why This Module

Real applications show dozens of similar elements — rows in a table, cards in a grid, items in a list. Filtering lets you target the exact one you need without brittle index numbers. And iFrames appear constantly in the real world: payment gateways, embedded maps, chat widgets, and social login buttons all live inside frames. Being comfortable with frameLocator is what makes you productive on real projects from day one.

08
Element Actions & Page API (33 Methods)
Days 10–13  |  click, fill, dragTo, state checks, screenshots, trace viewer
Interaction Methods
  • click() — left-click; click({button:'right'}) — right-click; dblclick() — double-click
  • fill(value) — clear and type into an input; clear() — clear only
  • pressSequentially(text) — type one character at a time like a human
  • press('Enter') — press any keyboard key
  • check() / uncheck() — tick or untick a checkbox
  • hover() — mouse over to reveal tooltip or dropdown
  • dragTo(targetLocator) — drag and drop one element onto another
  • scrollIntoViewIfNeeded() — scroll until element is visible
  • boundingBox() — get position and size as {x, y, width, height}
  • focus() / blur() / highlight() — focus management and debug aid
State Checks & Data Extraction
  • isVisible() / isHidden() / isEnabled() / isChecked()
  • getAttribute(name) — read any HTML attribute value
  • textContent() — all text including hidden; innerText() — visible text only
  • inputValue() — current value of an input or textarea
  • screenshot({path}) — capture just this element; page.screenshot() — full page
  • page.dragAndDrop(src, dst) — drag between two selectors on the page
  • page.mouse.move(x,y) / page.keyboard.down('Control') — raw input APIs
  • Run with trace: --trace=on; replay at trace.playwright.dev
Why This Module

Locators tell Playwright what to interact with — actions tell it how. The 33 methods here cover every possible browser interaction. Knowing the difference between fill() (clear then type) and pressSequentially() (key by key) matters for applications that validate input as you type. The trace viewer is the single most powerful debugging tool in Playwright — being able to replay a failure step by step is what makes you fast at fixing broken tests.

09
Special Element Handling
Days 14–18  |  selectOption, JS popups (page.once), HTML modals, new tabs
Select / Listbox Handling
  • Listbox built with <select> and <option> tags
  • selectOption({value:'b'}) — select by the option's value attribute
  • selectOption({label:'Chennai'}) — select by visible label text
  • selectOption({index:3}) — select by zero-based position
  • Multi-select: selectOption(['val1','val2']) — select multiple options at once
  • Read available options in DevTools console: $("option[value='a']")
JavaScript Popups, HTML Modals & New Tabs
  • JS popup types: alert (message only), confirm (OK/Cancel), prompt (text input)
  • page.once('dialog', d => d.accept()) — handle the next popup once
  • dialog.dismiss() — click Cancel; dialog.fill(text) — type into a prompt
  • HTML modal popups — interact as normal elements using regular locators
  • New tab from a click: const [newPage] = await Promise.all([context.waitForEvent('page'), button.click()])
  • Switch between tabs by using the newPage object for subsequent actions
Why This Module

Dropdowns, alerts, and popups appear in virtually every real application — login pages, forms, confirmation dialogs, and checkout flows all use them. selectOption is simpler than Selenium's Select class. And the trick to handle JS alerts — registering a listener with page.once before triggering the action — is one of the most commonly asked interview questions for Playwright roles.

10
API Testing & Browser Context
Day 19  |  request fixture, GET/POST, broken links, context isolation
What You Will Learn
  • Playwright can test backend APIs — not just browser pages
  • request.get(url) — send a GET request and capture the response
  • request.post(url, {data:{...}}) — send a POST with a JSON body
  • response.status() — check the HTTP status code (200, 404, 500)
  • response.json() — parse and read the response body
  • Verify broken links: collect all <a href> elements, request each, check for non-200 status
  • Browser context — isolated session with its own cookies and storage
  • Multiple contexts per browser = multiple isolated sessions running in parallel
  • Combine API + browser testing in the same test — e.g. create a user via API, verify via browser
  • Use API to set up login state without clicking through the login UI
Why This Module

Modern QA roles expect you to test both the UI and the API. Playwright's built-in request fixture means you never need a separate tool like Postman in your automation suite. More practically, setting up test state via API (creating a user, seeding data) is dramatically faster than doing it through the browser — and it makes your UI tests more reliable because there is less setup that can go wrong.

11
Synchronisation & Timeouts
Days 20–22  |  waitFor, waitForURL, waitForLoadState, waitForFunction
Timeout Configuration
  • Default test timeout: 30,000ms — override with test.setTimeout(n)
  • test.slow() — triple the current test timeout with one call
  • page.setDefaultTimeout(n) — applies to every action on this page
  • page.setDefaultNavigationTimeout(n) — overrides only for goto/reload/goBack
  • Config-level: use:{actionTimeout:7000, navigationTimeout:5000}
  • Config-level test timeout: timeout:40000 in defineConfig()
Smart Wait Methods
  • page.waitForTimeout(ms) — blind wait (use only as last resort)
  • locator.waitFor() — wait until element is visible (default state)
  • States: attached · visible · hidden · detached
  • page.waitForSelector('#id') — CSS/XPath only version of waitFor
  • page.waitForURL('exact-url') — wait for full URL match after navigation
  • page.waitForURL(url => url.toString().endsWith('home')) — predicate match
  • page.waitForLoadState('networkidle') — page fully settled, no pending requests
  • page.waitForFunction(fn) — wait until a JavaScript condition returns true
Why This Module

Flaky tests — tests that sometimes pass and sometimes fail — are the biggest productivity killer in automation. The root cause is almost always a timing problem: the script moved faster than the page. Understanding the difference between a blind waitForTimeout (slow, unreliable) and a smart waitFor or waitForLoadState (fast, reliable) is what separates test suites that just work from ones that need constant babysitting.

12
Assertions — expect() API
Days 22–23  |  Page / element / value level, soft assert, .not, timeout
Page-Level Assertions
  • expect(page).toHaveTitle('...') — assert current page title
  • expect(page).toHaveURL('...') — assert current URL
  • expect(page).toHaveScreenshot() — visual regression for the full page
Element-Level Assertions
  • toHaveAttribute(name, value) — verify any HTML attribute
  • toBeVisible() / toBeHidden() / toBeEnabled() / toBeDisabled()
  • toBeChecked() / toBeEditable() / toBeAttached() / toBeEmpty()
  • toHaveValue('...') — current value of an input
  • toHaveText('...') — visible text content of an element
  • toHaveClass('...') / toHaveId('...')
  • toHaveCSS('color', 'rgb(0,0,0)') — verify computed CSS style
  • toHaveScreenshot() — visual regression for a single element
Value-Level Assertions & Configuration
  • toBe() / toEqual() / toContain() — strict, deep, and substring checks
  • toBeLessThan(n) / toBeGreaterThan(n) / toBeLessThanOrEqual(n)
  • Default expect timeout: 5000ms — override per-assertion: {timeout:7000}
  • Global: expect:{timeout:6000} in config applies to all assertions
  • Hard assert (default) — stops test immediately on failure
  • Soft assert: await expect.soft(el).toHaveValue('...') — continues, collects all failures
  • Negate any assertion: await expect(page).not.toHaveTitle('Wrong Title')
Why This Module

Without assertions a test cannot pass or fail — it can only run. Playwright's expect API is unique because it retries automatically until the condition is true or the timeout expires, eliminating most timing-related false failures. Knowing when to use soft assertions (collect all failures at once) versus hard assertions (stop at first failure) is a sign of professional test design that interviewers specifically ask about.

13
Data-Driven Testing & Remote Execution
Days 24–25  |  JSON, xlsx, SauceLabs, chromium.connect(), fullyParallel
What You Will Learn
  • Import JSON test data: import users from './users.json' assert{type:'json'}
  • Loop through JSON array with for...of — run one test per data row
  • Read Excel data: npm install xlsxXLSX.readFile()sheet_to_json()
  • Compatibility testing — same script runs on Chromium, Firefox, and WebKit
  • Remote server: npx playwright run-server --port 56789 on the remote machine
  • Connect from local: await chromium.connect('ws://localhost:56789/')
  • SauceLabs — cloud platform; configure via saucectl + .sauce/config.yml
  • Parallel execution: npx playwright test --headed — default workers = half your CPUs
  • Limit workers: --workers=1 or workers:2 in config
  • Run tests in same file in parallel: fullyParallel:true in config
Why This Module

Data-driven testing is what transforms a single test into a full test suite with one file. Instead of writing 50 login tests for 50 users, you write one and drive it from an Excel sheet. Parallel execution then runs all 50 in the time it used to take to run 5 — dramatically reducing the feedback loop for developers. These two skills together are what make automation genuinely useful to a team rather than a curiosity.

14
B149_AFW — Full Automation Framework
Days 26–33  |  POM, hooks, custom fixtures, Allure, GitHub, Jenkins, tags
Page Object Model (POM)
  • Encapsulation — declare page elements as #private fields, expose via getter/setter
  • JSDoc: /** @param {import("@playwright/test").Page} page */ — enables VS Code autocomplete
  • export class from each page file; import in spec files
  • One class per page: LoginPage, HomePage, ItemsPage, ItemKitsPage
  • Test file uses only human-readable method calls — no raw locators
  • Update one POM class when the UI changes — all tests fixed instantly
Hooks, Custom Fixtures & Login-Once Strategy
  • test.beforeEach / test.afterEach — auto-run before/after each test in a file
  • test.beforeAll / test.afterAll — run once per file
  • Hooks accept fixtures: ({page, browser, context, browserName, baseURL})
  • test.describe('group', () => {}) — group tests with shared setup
  • test.skip / test.only / test.describe.skip
  • Custom fixture in base_test.js — reusable beforeEach/afterEach across all files
  • global_setup.js — login once, save state to storageState.json
  • global_teardown.js — delete storageState.json after all tests complete
  • Config: storageState:'storageState.json' — every test inherits the logged-in session
Allure Reports, Tags, GitHub & Jenkins
  • npm install --save-dev allure-playwright allure-commandline
  • Config: [['allure-playwright',{outputFolder:'allure-results'}]]
  • Generate: npx allure generate allure-results -o allure-report; Open: npx allure open allure-report
  • Tags: test('login @smoke', async({page})=>{}) — word starting with @
  • Run by tag: npx playwright test --grep '@smoke' / --grep-invert '@smoke'
  • OR tags: --grep "@smoke|@sanity"
  • GitHub — git config, VS Code Source Control, publish to private repo
  • Jenkins Free Style project — SCM: GitHub URL + credentials + branch */main
  • Build steps: call npm install → call npx playwright install → call npx playwright test
  • Post-build: Allure plugin; Schedule cron: 00 20 * * * = 8 PM daily
  • Automatic trigger: "Build after other projects are built" — runs when dev deploys
Why This Module

This is where everything comes together. The B149_AFW framework is a production-ready automation suite — the same pattern used in professional QA teams at product companies. By the end you will have a real GitHub repository with POM classes, data-driven tests, Allure reports, and Jenkins integration. This is the portfolio project that turns interviews from "tell me about automation" into "let me show you my framework".

Interview Prep
What is the Page Object Model and how does it improve a test suite?
POM gives each page its own class with private locators and public action methods. Tests call methods like loginPage.login(user, pass) instead of repeating locators. One page change = one file update — all tests fixed without touching any test file.
How do you handle a JavaScript alert in Playwright?
Register a listener with page.once('dialog', d => d.accept()) before triggering the action that causes the alert. d.dismiss() clicks Cancel, and d.fill(text) types into a prompt dialog before accepting.
What is the difference between hard and soft assertions?
Hard expect stops the test immediately on failure. Soft expect.soft continues the test and collects all failures — the full report appears at the end. Use soft assertions when multiple independent checks should all be visible in one run.
How does storageState speed up a test suite?
global_setup.js logs in once and serialises cookies to storageState.json. Every subsequent test loads that file and skips the login page entirely. global_teardown.js deletes it afterwards. A 100-test suite saves the login time for 99 tests.
Why does Playwright have its own browsers and how does that differ from Selenium?
Playwright downloads specific browser versions that it has been tested against, ensuring consistent behaviour across every environment. Selenium uses whatever browser is already installed, which can cause version mismatch failures when Chrome auto-updates overnight.
What is the difference between action timeout and test timeout?
Test timeout is the budget for the entire test (default 30s). Action timeout is how long each individual method call waits for the element to be ready (default unlimited — best practice is to set it). test.slow() triples the test timeout when a specific test legitimately needs more time.