📘 Playwright
Features of Playwright
- Free | Open Source
- Multi-Browser | Multi-Language | Multi-OS
- Easy Setup and Configuration
- Functional | API | Accessibility testing
- Built-in Reporters | Custom Reporters
- CI CD | Docker support
- Recording | Debugging | Explore selectors
- Parallel testing
- Auto-wait
- Built-in assertions | Less Flaky tests
- Test retry, logs, screenshots, videos
- Multi-tab and multi-window
- Frames | Shadow DOM
- Emulate mobile devices, geolocations
- Test parameterization
- Fast
Primary Purpose
- node - JavaScript runtime environment; executes JavaScript code.
- npm (Node Package Manager) - Package manager; installs, manages, and publishes project dependencies.
- npx (Node Package Execute) - Package runner; executes packages and their binaries directly without permanent installation.
✅ Phase 1: TypeScript Basics
Run typscript files with command
🔹 Variables & Data Types
- number
- string
- boolean
- null & undefined
- any & unknown
- Arrays
- Tuple
- Objects / Dictionary (
Record<string, any>)
🔹 Files and Folders CRUD
Ref: TypescriptBasics/FileAndFoldersCRUD/index.ts
🔹 Conditions & Loops
- if / else
- switch
- for loop
- while / do-while
- for...of
- for...in
Ref: TypescriptBasics/ConditionsAndLoops.ts
🔹 Functions & Classes
- Function declaration
- Arrow functions
- Parameters & return types
- Classes & constructors
- Access modifiers (
public,private,protected) - Interfaces & Types
Ref: TypescriptBasics/FunctionsAndClasses.ts
🔹 Object-Oriented Programming
- Objects
- Inheritance
- Polymorphism
- Encapsulation
🔹 Exception Handling
- try / catch / finally
- Throwing errors
- Custom errors
Ref: TypescriptBasics/ExceptionHandling.ts
🔹 Dependency Management
- npm & npx
- package.json
- devDependencies
- tsconfig.json
| topic | explanation |
|---|---|
| npm | Node Package Manager used to install, manage, and version project dependencies. |
| npx | Executes packages directly without globally installing them. |
| package.json | Project configuration file that defines dependencies, scripts, and metadata. |
| devDependencies | Packages needed only during development (e.g., testing, TypeScript, build tools). |
| tsconfig.json | TypeScript configuration file that controls how TypeScript compiles your code. |
✅ Phase 2: Playwright Fundamentals
🔹 Setup & Configuration
- Install Playwright
- Playwright project structure
- playwright.config.ts
- Browser configuration
🔹 Writing Tests
- test()
- expect()
Ref: https://playwright.dev/docs/test-assertions - Test structure
- Debugging tests
when we run tests in debug mode, it runs in headed mode
- page.pause()
🔹 Assertions
- Visibility assertions
- Text assertions
- URL assertions
- Attribute checks
- Soft vs Hard assertions
🔹 Reporting & Logging
- HTML reports
- Screenshots on failure
- Video recording
- Trace viewer
- Console logging
🔹 Setup & Teardown
- beforeAll
- beforeEach
- afterEach
- afterAll
Ref: tests/Hooks/File-Level Hooks.spec.ts
Key takeaways
| Hook | Throws | What happens to tests in the file |
|---|---|---|
| beforeAll | Throws | All tests skipped / not executed; first test may briefly appear in report depending on reporter |
| beforeEach | Throws | Only current test fails; other tests run normally |
| afterEach | Throws | Current test may fail; other tests run normally |
| afterAll | Throws | Last test marked failed; other tests already run normally |
| --- |
- Test fixtures
A wrapper around the test function where you control what runs before and after the test.

Closer comparison
✅ Phase 3: Test Management
🔹 Tagging
- @smoke
- @regression
- @sanity
import { test } from '@playwright/test';
test('login test @smoke @regression @sanity', async ({ page }) => {
// test code
});
#Run only smoke tests:
npx playwright test --grep @smoke
#Run everything except smoke:
npx playwright test --grep-invert @smoke
🔹 Parameterization
- test.describe()
- test.each()
- Data-driven testing
Generate Separate Parameterized Tests
✔ This creates 3 independent tests
✔ Each can run on a different worker
import { test, expect } from '@playwright/test';
const users = [
{ username: 'user1', password: 'pass1' },
{ username: 'user2', password: 'pass2' },
{ username: 'admin', password: 'admin123' },
];
users.forEach(({ username, password }) => {
test(`login test for ${username}`, async ({ page }) => {
await page.goto('https://example.com/login');
await page.fill('#username', username);
await page.fill('#password', password);
await page.click('#login');
await expect(page).toHaveURL(/dashboard/);
});
});
### 🔹 Skip tests
- Skip run based on specific browser. ✅ Below skip logic tested and works as expected
```typescript
test('Iterate over table and print 3 column value when first 2 columns matches with the given value', async({page}, testInfo)=> {
//condition to run only on google chrome browser.
test.skip(
testInfo.project.name !== 'Google Chrome',
'Runs only in Google Chrome'
);
} )
test('page title', async () => {
//skip test only on CI run
test.skip(process.env.CI === 'true', 'Skipping in CI');
let browser = await chromium.connectOverCDP('http://localhost:9222');
🔹 Execute tests
| Command | Comments |
|---|---|
| npx playwright tests | To run alls tests within playwright folder |
| npx playwright test tests/tests.spec.ts -g "testcase name/partial name" | To run specific testcase |
- how to run tests against different version of same browser
🔹 Parallel Execution
- Workers
- Parallel execution
- Serial execution
By default, Playwright runs test files in parallel.
//If tests are inside the same file and you want them parallel:
test.describe.configure({ mode: 'parallel' });
const users = ['user1', 'user2', 'admin'];
for (const user of users) {
test(`login test for ${user}`, async ({ page }) => {
// test logic
});
}
test.describe.configure({ mode: 'parallel' });
const testData = require('./users.json');
for (const data of testData) {
test(`@smoke login for ${data.username}`, async ({ page }) => {
// test logic
});
}
🔹 CI/CD Integration
- GitHub Actions
- Jenkins
- GitLab CI
- Headless execution
✅ Phase 4: Browser Automation
🔹 Locators
- getByRole
- getByText
- getByTestId
- CSS selectors
- XPath
🔹 User Actions
- Click
- Type
- Keyboard actions
- Mouse actions
- Drag & Drop
- File upload
🔹 Web Handling
- Dropdowns
- Checkboxes
- Radio buttons
- Auto-suggestions
🔹 Data Retrieval
- Get text
- Get attributes
- Tables
🔹 Browser Events
- Alerts
- Frames
- Windows
- Popups
🔹 Cheat sheet
| Element | Best Locator | Common Actions | Retrieve Value | Assertions |
|---|---|---|---|---|
| Button | getByRole('button', { name: 'Submit' }) |
click() dblclick() hover() |
innerText() |
toBeVisible() toBeEnabled() toHaveText() |
| Textbox / Input | getByRole('textbox', { name: 'First Name' }) |
fill() type() clear() |
inputValue() |
toHaveValue() toBeEditable() |
| Textarea | locator('textarea') |
fill() |
inputValue() |
toHaveValue() |
| Checkbox | getByRole('checkbox', { name: 'Accept' }) |
check() uncheck() |
isChecked() |
toBeChecked() |
| Radio Button | getByRole('radio', { name: 'Male' }) |
check() |
isChecked() |
toBeChecked() |
| Dropdown (select) | locator('select#country') |
selectOption() |
inputValue() / locator('option:checked').innerText() |
toHaveValue() |
| Link | getByRole('link', { name: 'Home' }) |
click() |
getAttribute('href') |
toHaveAttribute() |
| Table Rows | locator('table tbody tr') |
nth(i) count() |
allTextContents() |
toHaveCount() |
| Table Cell | row.locator('td').nth(i) |
— | innerText() |
toContainText() |
| Div / Span | locator('#id') |
click() hover() |
innerText() |
toHaveText() toContainText() |
| Image | locator('img') |
— | getAttribute('src') |
toHaveAttribute() |
| File Upload | locator('input[type=file]') |
setInputFiles() |
inputValue() |
toHaveValue() |
| Dialog (Alert) | page.on('dialog', handler) |
accept() dismiss() |
dialog.message() |
— |
| Page | page |
goto() reload() |
url() title() |
toHaveURL() toHaveTitle() |
🪟 Browser Window Handling
| Scenario | Code |
|---|---|
| New browser context | const context = await browser.newContext(); |
| New window manually | const newPage = await context.newPage(); |
| Get window title | await page.title(); |
| Get window URL | page.url(); |
| Close window | await page.close(); |
🖼 Frames / iFrames
| Scenario | Code |
|---|---|
| Get frame by name | const frame = page.frame({ name: 'frameName' }); |
| Get frame by URL | const frame = page.frame({ url: /partial-url/ }); |
| Locate inside iframe (recommended) | const frameLocator = page.frameLocator('#frameId'); |
| Click inside iframe | await frameLocator.locator('button').click(); |
| Fill inside iframe | await frameLocator.locator('#input').fill('text'); |
✅ Phase 5: Framework Design
🔹 Page Object Model
- Page classes
- Reusable methods
🔹 Test Data Management
- JSON
- Environment variables
- .env file
🔹 Screenshots & Videos
- On failure
- Manual screenshots
✅ Phase 6: Advanced Topics
🔹 Authentication
- Storage state
- Token login
🔹 Network Handling
- API mocking
- Request interception
🔹 Performance
- Page load metrics
🔹 Accessibility
- WCAG checks
🔹 Cross Browser
- Chromium
- Firefox
- WebKit
✅ Phase 7: Best Practices
- Use data-testid
- Avoid hard waits
- Clean code
- Reusable components
- Maintainable framework
Practise sites
https://ecommerce-playground.lambdatest.io/index.php?route=account/login
Certification
2) How to use functions and selectors
const browser = await chromium.launch({
headless: false
});
const context = await browser.newContext();
const page = await context.newPage();
// Navigate to homepage
await page.goto("https://ecommerce-playground.lambdatest.io/");
// Hover over 'My account' dropdown
await page.hover("//a[@data-toggle='dropdown']/span[contains(.,'My account')]");
// Click Login button
// await page.click("text=Login")
await page.click("input[value='Login']");
// Fill login form
await page.fill("input[name='email']", "koushik350@gmail.com");
await page.fill("input[name='password']", "Pass123$");
await page.click("input[value='Login']");
// Wait for 5 seconds
await page.waitForTimeout(5000);
// Create a new browser context
const newContext = await browser.newContext();
// Open a new page in the new context
const newPage = await newContext.newPage();
await newPage.goto("https://ecommerce-playground.lambdatest.io/index.php?route=account/account");
// Wait for 5 seconds
await newPage.waitForTimeout(5000);
3) Playwright Testing Features
- playwright.config.ts
const config: PlaywrightTestConfig = { testMatch: ["tests/recorded.test.ts"], use: { headless: false, screenshot: "only-on-failure", video: "retain-on-failure", timeout: 30000, // Timeout for each test in milliseconds (30s) }, retries: 2, reporter: [["dot"], ["json", { outputFile: "jsonReports/jsonReport.json" }], ["html", { open: "never" }]] } - Auto wait
https://playwright.dev/docs/actionability
4) How to Handle Alerts and Dropdowns
page.on("dialog", async (alert) => {
const text = alert.defaultValue();
console.log(text);
await alert.accept("koushik");
});
await page.locator("button:has-text('Click Me')").nth(2).click();
// expect(page.locator("id=confirm-demo")).toContainText("Cancel!");
expect(page.locator("id=prompt-demo")).toContainText("'koushik'");
await page.selectOption("#select-demo", {
// label: "Tuesday"
// value: "Friday"
index: 5
})
await selectCountry();
async function selectCountry() {
await page.click("#countryspan");
await page.locator("ul#select2-country-results")
.locator("li", {
hasText: "India"
}).click();
}
Ref: https://github.com/ortoniKC/playwright-ts-lambdatest