Guide to Playwright - Automation Testing
What is Playwright
Free & Open source Framework for web automation testing | created by Microsoft
Applications- Web browser | Mobile web apps | API
Languages - JavaScript, TypeScript, Java, Python, .NET (C#)
Browsers - All modern engines chromium , WebKit, and FireFox(headless or headed) 4.1. Headless - will not see browser running physically 4.2. Headed - Can see a physical browser running
OS - Windows, MacOS, Linux, | Supports CI Runs
Features of Playwright
Free and Open source
Multi Browser | Multi Language | Multi OS
Easy setup and Configuration
Functional Testing | API Testing | Accessibility Testing
Built-in Reporters | Custom Reporters
CI CD | Docker support
Recording | Debugging | Explore selectors
Parallel testing
Auto wait (timers)
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
Prerequisities
Node js
ways to install
-> Install using command as npm package
npm init playwright@latest
To check the version
npm playwright -v
for help
npx playwright --help
-> using vs code extension
File structure
package.json and package.lock.json - node project management file
playwright.config.ts - Configuration file ( we need to change this according to our needs)
tests folder - basic example test
tests-examples folder - detailed example test
.gitignore - to be used during git commit and push
playwright.yml - to be used during ci cd pipeline
How to run tests
command to run tests
npx playwright test
Add workers to test in command so that parallel execution is done
npx playwright test --workers 3
To show report run
npx playwright show-report
To run a specific file
npx playwright test <filepath_name>
we can run multiple files as below
npx playwright test <file_name> <file_name>
we can run file with keyword . If we add keyword in the file it will try to find the word in test file name and it has the name it will run that test
npx playwright test <example>
To run the test with test title
npx playwright test -g <"title">
To run test on specific browser
npx playwright test --project=chromium
To run the test in headed mode
npx playwright test --project=chromium --headed
To debug
npx playwright test --debug
To debug specific file
npx playwright test example.spec.js --debug
To debug specific line in a file
npx playwright test example.spec.js:21 --debug
How to write test
import { test, expect } from "@playwright/test";
test("my first test", async ({ page }) => {
await page.goto("https://google.com");
await expect(page).toHaveTitle("Google");
});
To record test
what is code gen - Test generator
Playwright comes with a tool - Codegen also called test generator Can be used to record test and generate test scripts It Opens 2 windows
A browser window to interact with the website
Playwright Inspector window to record test To run recording
npx playwright codegen
To open browser with pre url
npx playwright codegen <google.com | url>
steps to run code gen and work with it
Open terminal and run codegn
npx playwright codegen
Check 2 windows open - Browser and Playwright Inspector
Record your test steps and check the test scripts getting started
Save the recorded script in a test file | Run and check
open open specific broswer
npx playwright codegen --browser firefox
need to create file before running below command
Record and save to a file
npx playwright codegen --target javascript -o <filename |path>
set viewport - screen resolution size
npx playwright codegen --viewport-size=800,600
Emulate devices
npx playwright codegen --device="iPhone11"
To add color scheme
This will open the site in dark mode
npx playwright codegen --color-sheme=dark <url>
Trace viewer
GUI tool that helps viewing the executed test along with snapshots, timeline and other details (traces)
How to use Trace viewer
Open the config file and set trace: 'on-first-retry'
It means - Collect the trace when retrying the failed test for the 1st time only
add code for 1 retry in the config file
retries:1
Save and Run a test to fail
Check trace.zip file created under test results folder
View trace -
npx playwright show-trace trace.zip
This will show an UI like this
We can change the timeouts in the config file for retries
We can also navigate to trace viewer from the reports also
Trace Viewer Options
'on-first-retry' - Record a trace only when retrying a test for the first time
'off' - Do not record trace
'on' - Record a trace for each test ( not recommended as its performance heavy)
'retain-on-failure'- Record a trace for each test , but remove it from successfull test runs
To set trace on from command line
npx playwright test --trace <options>
Different ways to view trace
Using command -
npx playwright show-trace trace.zip
using HTML report
using utility - https://trace.playwright.dev/ here we need add our trace our file to view it
How to set Trace programmaticaly
We need to add the code at which part of the execution we want to start trace and at which part we need to stop the trace
test('title',async ({page,context})=>{
await context.tracing.start({screenshots:true,snapshots:true})
await page.goto('https://google.com')
await expect(page).toHaveTitle('Google')
await context.tracing.stop({path:<filename.zip>})
})
We can start and stop this tracing before the test and after the test by using hooks
beforeAll
and afterAll
let context
let page
test.beforeAll(async({browser})=>{
context=await browser.newContext()
await context.tracing.start({screenshots:true,snapshots:true})
page= await context.newPage()
})
test.afterAll(async()=>{
await context.tracing.stop({pathe:<filename.zip>})
})
Here we are declaring global context and page which should be using in our test rather than individual param
How to find web Objects
what are Selectors and Locators
Selectors are the strings/properties of the web objects
Selectors are used to create Locators
Selectors example: CSS, Class, Name, ID,Text,XPath
To find an object or element we use the syntax
page.locator(selector[,options])
Locator is class in playwright library
How to find web objects with Playwright
To find an object or element we use the syntax page.locator(selector[,options])
How to find objects using XPath ,CSS, Text, ID etc
Using inspect
How to find and record object locators using Playwright Inspector
There are 3 ways to open playwright inspector
With pause in code
With
--debug
in commandWith codegen tool
import { expect, test } from "@playwright/test";
test("selectors demo", async ({ page }) => {
await page.goto("https://www.saucedemo.com");
await page.click("id=user-name");
await page.locator("id=user-name").fill("Edison"); //or
await page.locator('[id="user-name"]').fill("vishnu");
//CSS selector
//inspect element and right and copy the css selector
//use explore in inspector to get best possible object
await page.locator("#login-button").click();
//XPath
await page.locator('xpath=//input[@name="password"]').fill("hello");
await page.locator('//input[@name="password"]').fill("Ramanujan");
//Text
await page.pause();
await page.locator("text=LOGIN").click();
await page.locator('input:has-text("LOGIN")').click();
});
Demo Login Test
import { test, expect } from "@playwright/test";
test("Demo login test 1", async ({ page }) => {
await page.goto("https://demo.applitools.com/");
await page.pause();
await page.getByPlaceholder("Enter your username").fill("vishnu");
await page.getByPlaceholder("Enter your password").fill("password");
//if we want to wait for a ui object and the perform operation we can use this below method
await page.waitForSelector("text=Sign in", { timeout: 5000 });
//we can also add expect selector
await expect(page.locator("text=Sign in")).toHaveCount(1);
await page.getByRole("link", { name: "Sign in" }).click();
});
//This only will make this the below test only run when we refer this file for test
// we can create test using record also
test.only("Demo login test 1", async ({ page }) => {
await page.goto("https://demo.applitools.com/");
await page.pause();
await page.getByPlaceholder("Enter your username").fill("vishnu");
await page.getByPlaceholder("Enter your password").fill("password");
//if we want to wait for a ui object and the perform operation we can use this below method
await page.waitForSelector("text=Sign in", { timeout: 5000 });
//we can also add expect selector
await expect(page.locator("text=Sign in")).toHaveCount(1);
await page.getByRole("link", { name: "Sign in" }).click();
});
Assertions
Check or verification
Check actual =expected
Assertions in playwright
Playwright Test uses expect library for test assertions
How to add assertions
Present / Not present
Visible / Hidden
Enabled / Disabled
Text matches / not matches value
Element attribute
Url
title
Page matches screenshot ( visual validation)
soft assertions ( continue testing even its fails)
import { test, expect } from "@playwright/test";
test("Assertions demo", async ({ page }) => {
await page.goto("https://kitchen.applitools.com/");
await page.pause();
//assertions
//check element present or not
await expect(page.getByRole("heading", { name: "The Kitchen" })).toHaveCount(
1,
);
// for conditions we use this $
if (await page.$("text=The Kitchen")) {
await page.locator("text=The Kitchen").click();
}
// check element hidden or visible
await expect(page.locator("text=The Kitchen")).toBeVisible();
await expect.soft(page.locator("text=The Kitchen")).toBeHidden();
//check element enabled or disabled
await expect(page.locator("text=The Kitchen")).toBeEnabled();
await expect.soft(page.locator("text=The Kitchen")).toBeDisabled();
//check text
await expect(page.locator("text=The Kitchen")).toHaveText("The Kitchen");
await expect
.soft(page.locator("text=The Kitchen"))
.not.toHaveText("The Kitchen");
//to check for element attribute
// we can use regex in this as the classes might be dynamic
await expect(page.locator("text=The Kitchen")).toHaveAttribute(
"class",
"class_name",
);
await expect(page.locator("text=The Kitchen")).toHaveClass(/.*css-dpmy2a/);
// url
await expect(page).toHaveURL("some url");
await expect(page).toHaveTitle("title");
// screenshot visual validation
// This will first take a screenshot then it will compare the ui with prev screenshot
await expect(page).toHaveScreenshot();
});
How to run tests in Slow motion (decrease speed of test execution)
How to record video
from config file and from with the test
- From the config file change the use object as below
video: "on",
launchOptions: {
slowMo: 1000,
},
slowMo slows down Playwright operations by the specified milliseconds
Video:
'on' - Record video for each test
'off' - Do not record video for each test
'retain on failure' - Record for each test, but remove from successful test runs
'on-first-retry' - Record only when retrying a test for the first time
Save & run
Check video files will appear in the test results folder
How to set video recording and slow motion from test (Browser context)
In Playwright we can create isolated incognito browser sessions using browser context
Create a test and create browser context
Add Options for slowmotion in browser
Add options for video recording in new context
Close context
import { test, expect, chromium } from "@playwright/test";
test("slowmotion video recording", async () => {
const browser = await chromium.launch({
slowMo: 500,
headless: false,
});
const context = await browser.newContext({
recordVideo: {
dir: "videos/",
size: { width: 800, height: 600 },
},
});
const page = await context.newPage();
await page.goto("url");
//rest of the test
await context.close();
});
Hooks and groups
Hooks :
beforeAll
beforeEach
afterAll
afterEach
Groups:
- describe
import { test, expect } from "@playwright/test";
//this will group all the tests and the hooks inside the block the tests will run only for this group
test.describe("Tests all", () => {
test.beforeEach(async ({ page }) => {
await page.goto("https://saucedemo.com");
//await page.pause();
await page.locator('[data-test="username"]').click();
await page.locator('[data-test="username"]').fill("standard_user");
await page.locator('[data-test="password"]').click();
await page.locator('[data-test="password"]').fill("secret_sauce");
await page.locator('[data-test="login-button"]').click();
//await page.getByRole("button", { name: "Open Menu" }).click();
//await page.locator('[data-test="logout-sidebar-link"]').click();
//await page.close();
});
test("home page", async ({ page }) => {
// await page.goto("https://saucedemo.com");
// await page.locator('[data-test="username"]').click();
// await page.locator('[data-test="username"]').fill("standard_user");
// await page.locator('[data-test="password"]').click();
// await page.locator('[data-test="password"]').fill("secret_sauce");
// await page.locator('[data-test="login-button"]').click();
await page.locator('[data-test="add-to-cart-sauce-labs-backpack"]').click();
await page
.locator('[data-test="add-to-cart-sauce-labs-bike-light"]')
.click();
await page.locator('[data-test="item-1-title-link"]').click();
await page.locator('[data-test="add-to-cart"]').click();
// await page.close();
});
test("logout", async ({ page }) => {
// await page.goto("https://saucedemo.com");
// //await page.pause();
// await page.locator('[data-test="username"]').click();
// await page.locator('[data-test="username"]').fill("standard_user");
// await page.locator('[data-test="password"]').click();
// await page.locator('[data-test="password"]').fill("secret_sauce");
// await page.locator('[data-test="login-button"]').click();
await page.getByRole("button", { name: "Open Menu" }).click();
await page.locator('[data-test="logout-sidebar-link"]').click();
//await page.close();
});
test.afterAll(async ({ page }) => {
await page.close();
});
});
Annotations & Tags
Annotations
only
skip
skip with condition
fail
fixme
slow
Tags:
smoke
reg
...
import { test } from "@playwright/test";
//will skip the test
test.skip("test1", async ({ page }) => {});
//will fail the fail wantedly
test("fail", async ({ page }) => {
test.fail();
});
// test to be fixed
test.fixme("test to be fixed", async ({ page }) => {});
//marks the test as slow and triples the timeout
test("slow", async ({ page }) => {
test.slow();
});
// when we want to run only specific test in a file
//
test.only("only", async ({ page }) => {});
//skip conditionally
//Tags
// we will add tags to title , when ever we run the tests from command we will add tags to which we want it
// to run , only those will be run
//
test("Test login page @fast", async ({ page }) => {});
test("Test login page @slow", async ({ page }) => {});
//To run this with tags we will run below command
/// npx playwright test --grep "@smoke"
// To skip the tag we have given try below command
//
// npx playwright test --grep-invert "@smoke"
To run with tags we will run the command below
npx playwright test --grep "@smoke"
To skip the tag we have given try below command
npx playwright test --grep-invert "@smoke"
Playwright Page Object Model Project
Create a new folder and open IDE or Vs code
Initialize a new nodejs project by running
npm init -y
to create a package.jsonInstall & setup Playwright by running
npm init playwright@latest
Create a demo login test, can use Test generator to record
npx playwright codegen
Run tests and check results -
npx playwright test && npx playwright show-report
Create new folder "pages" in your project , this will contain all page objects
Create a new file and class for each page e.g : login.ts and LoginPage
In the class create element locators & action methods for login page
In test file, import the pages class, create instance of it, & use methods from LoginPage class
Run the test
npx playwright test
and check results
Login.ts
import { Page, Locator } from "@playwright/test";
export default class LoginPage {
username_textBox: Locator;
pasword_textBox: Locator;
login_button: Locator;
page: Page;
constructor(page: Page) {
this.page = page;
this.username_textBox = page.getByLabel("Username");
this.pasword_textBox = page.getByLabel("Password");
this.login_button = page.getByRole("button", { name: " Login" });
}
async goToLoginPage(url: string) {
await this.page.goto(url);
}
async login(username: string, password: string) {
await this.username_textBox.fill(username);
await this.pasword_textBox.fill(password);
await this.login_button.click();
}
}
login.spec.ts
import { test } from "@playwright/test";
import LoginPage from "../pages/login";
test(" login test", async ({ page }) => {
const login = new LoginPage(page);
await login.goToLoginPage("https://the-internet.herokuapp.com/login");
await login.login("tomsmith", "SuperSecretPassword");
});
API Testing
To open ui of playwright
npx playwright test --ui
The ui will look as below
import { test, expect } from "@playwright/test";
//reqres dummy api
test("API GET Request", async ({ request }) => {
const response = await request.get("https://reqres.in/api/users/2");
expect(response.status()).toBe(200);
const text = await response.text();
expect(text).toContain("J");
});
test("API POST Request", async ({ request }) => {
const response = await request.post("https://reqres.in/api/users", {
data: {
name: "vishnu",
job: "engineer",
},
});
expect(response.status()).toBe(201);
const text = await response.text();
expect(text).toContain("vishnu");
});