Brief On Playwright
Key Features:
Cross-Browser Compatibility: Playwright offers a consistent API for controlling web browsers, making it easier to write tests that work across different platforms and browsers.
Headless and Headful Testing: You can run tests in headless mode (without a visible browser window) for faster execution or in headful mode for visual inspection.
Page Object Model (POM): Playwright supports the POM pattern, which helps you organize your tests and make them more maintainable.
Assertions: Playwright provides built-in assertions for verifying the state of web pages, making it easy to write robust tests.
Network Interception: You can intercept network requests and responses to simulate network conditions or modify data.
Mobile Emulation: Playwright allows you to emulate mobile devices and their specific characteristics, making it suitable for mobile testing.
Accessibility Testing: You can use Playwright to test the accessibility of your web applications, ensuring that they are usable by people with disabilities.
Use Cases:
End-to-End Testing: Playwright is well-suited for testing web applications from start to finish, simulating user interactions and verifying the correctness of the application's behavior.
Integration Testing: You can use Playwright to test the integration between different components of your web application.
API Testing: Playwright can be used to test APIs by making HTTP requests and verifying the responses.
Visual Regression Testing: Playwright can help you identify visual changes in your web application, ensuring that the user interface remains consistent.
Setup Playwright
Playwright can be setup in 4 languages
Javascript (Node.js runtime) | Playwright Documentation
Setup environment
npm install -D playwright ## yarn create playwright
Setup First Test
import { test, expect } from '@playwright/test'; test('has title', async ({ page }) => { await page.goto('https://playwright.dev/'); // Expect a title "to contain" a substring. await expect(page).toHaveTitle(/Playwright/); }); test('get started link', async ({ page }) => { await page.goto('https://playwright.dev/'); // Click the get started link. await page.getByRole('link', { name: 'Get started' }).click(); // Expects page to have a heading with the name of Installation. await expect(page.getByRole('heading', { name: 'Installation' })).toBeVisible(); });
Python | Playwright Documentation
Setup environment
pip install pytest-playwright
Setup First Test
import re from playwright.sync_api import Page, expect def test_has_title(page: Page): page.goto("https://playwright.dev/") # Expect a title "to contain" a substring. expect(page).to_have_title(re.compile("Playwright")) def test_get_started_link(page: Page): page.goto("https://playwright.dev/") # Click the get started link. page.get_by_role("link", name="Get started").click() # Expects page to have a heading with the name of Installation. expect(page.get_by_role("heading", name="Installation")).to_be_visible()
Java | Playwright Documentation
Setup environment
package org.example; import com.microsoft.playwright.*; public class App { public static void main(String[] args) { try (Playwright playwright = Playwright.create()) { Browser browser = playwright.chromium().launch(); Page page = browser.newPage(); page.navigate("http://playwright.dev"); System.out.println(page.title()); } } }
<?xml version="1.0" encoding="UTF-8"?> <project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> <modelVersion>4.0.0</modelVersion> <groupId>org.example</groupId> <artifactId>examples</artifactId> <version>0.1-SNAPSHOT</version> <name>Playwright Client Examples</name> <properties> <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding> </properties> <dependencies> <dependency> <groupId>com.microsoft.playwright</groupId> <artifactId>playwright</artifactId> <version>1.46.0</version> </dependency> </dependencies> <build> <plugins> <plugin> <groupId>org.apache.maven.plugins</groupId> <artifactId>maven-compiler-plugin</artifactId> <version>3.10.1</version> <!-- References to interface static methods are allowed only at source level 1.8 or above --> <configuration> <source>1.8</source> <target>1.8</target> </configuration> </plugin> </plugins> </build> </project>
Setup First Test
package org.example; import java.util.regex.Pattern; import com.microsoft.playwright.*; import com.microsoft.playwright.options.AriaRole; import static com.microsoft.playwright.assertions.PlaywrightAssertions.assertThat; public class App { public static void main(String[] args) { try (Playwright playwright = Playwright.create()) { Browser browser = playwright.chromium().launch(); Page page = browser.newPage(); page.navigate("http://playwright.dev"); // Expect a title "to contain" a substring. assertThat(page).hasTitle(Pattern.compile("Playwright")); // create a locator Locator getStarted = page.getByRole(AriaRole.LINK, new Page.GetByRoleOptions().setName("Get Started")); // Expect an attribute "to be strictly equal" to the value. assertThat(getStarted).hasAttribute("href", "/docs/intro"); // Click the get started link. getStarted.click(); // Expects page to have a heading with the name of Installation. assertThat(page.getByRole(AriaRole.HEADING, new Page.GetByRoleOptions().setName("Installation"))).isVisible(); } } }
C# (.Net Runtime) Playwright Documentation
Setup environment with NUnit
dotnet new nunit -n PlaywrightTests cd PlaywrightTests
- Install Playwright
dotnet add package Microsoft.Playwright.NUnit
dotnet build ## Build project
Install browsers
pwsh bin/Debug/net8.0/playwright.ps1 install
Setup First Test
using System.Text.RegularExpressions; using System.Threading.Tasks; using Microsoft.Playwright; using Microsoft.Playwright.NUnit; using NUnit.Framework; namespace PlaywrightTests; [Parallelizable(ParallelScope.Self)] [TestFixture] public class ExampleTest : PageTest { [Test] public async Task HasTitle() { await Page.GotoAsync("https://playwright.dev"); // Expect a title "to contain" a substring. await Expect(Page).ToHaveTitleAsync(new Regex("Playwright")); } [Test] public async Task GetStartedLink() { await Page.GotoAsync("https://playwright.dev"); // Click the get started link. await Page.GetByRole(AriaRole.Link, new() { Name = "Get started" }).ClickAsync(); // Expects page to have a heading with the name of Installation. await Expect(Page.GetByRole(AriaRole.Heading, new() { Name = "Installation" })).ToBeVisibleAsync(); } }
Core Concepts and Features
For the context of understand these concepts we will be using JavaScript
- Browsers and Contexts
Launching different browsers (Chromium, Firefox, WebKit)
You can define the browsers you want to run tests on in your
playwright.config.js
import { defineConfig, devices } from '@playwright/test'; export default defineConfig({ projects: [ /* Test against desktop browsers */ { name: 'chromium', use: { ...devices['Desktop Chrome'] }, }, { name: 'firefox', use: { ...devices['Desktop Firefox'] }, }, { name: 'webkit', use: { ...devices['Desktop Safari'] }, }, /* Test against mobile viewports. */ { name: 'Mobile Chrome', use: { ...devices['Pixel 5'] }, }, { name: 'Mobile Safari', use: { ...devices['iPhone 12'] }, }, /* Test against branded browsers. */ { name: 'Google Chrome', use: { ...devices['Desktop Chrome'], channel: 'chrome' }, // or 'chrome-beta' }, { name: 'Microsoft Edge', use: { ...devices['Desktop Edge'], channel: 'msedge' }, // or 'msedge-dev' }, ], });
Output:
npx playwright test Running 7 tests using 5 workers ✓ [chromium] › example.spec.ts:3:1 › basic test (2s) ✓ [firefox] › example.spec.ts:3:1 › basic test (2s) ✓ [webkit] › example.spec.ts:3:1 › basic test (2s) ✓ [Mobile Chrome] › example.spec.ts:3:1 › basic test (2s) ✓ [Mobile Safari] › example.spec.ts:3:1 › basic test (2s) ✓ [Google Chrome] › example.spec.ts:3:1 › basic test (2s) ✓ [Microsoft Edge] › example.spec.ts:3:1 › basic test (2s)
- Pages and Selectors | Recorder
Interacting with pages
// Create a page. const page = await context.newPage(); // Registering a page await page.goto('http://devassure.io'); // Fill an input. await page.locator('#search').fill('query'); // Navigate implicitly by clicking a link. await page.locator('#submit').click(); // Expect a new url. console.log(page.url());
Multiple Pages
// Create two pages const pageOne = await context.newPage(); await pageOne.goto("https://devassure.io/features") const pageTwo = await context.newPage(); await pageTwo.goto("https://devassure.io/blogs") // Get pages of a browser context const allPages = context.pages();
Using CSS selectors and XPath
CodeGen Recorde: Record your automation test cases and run them
Interactions
Clicking, typing, and other `interactions
Check input checkbox:
locator.check()
Click element checkbox:
locator.click()
Press single key while selecting the element:
locator.press()
Fill text in the input area:
locator.fill()
Hover mouse over the element:
locator.hover()
Handling user inputs and form submissions
Upload files:
locator.setInputFiles()
Select options from in Drop Down:
locator.setInputFiles()
Assertions
Verifying conditions and expected results
Playwright uses
expect
function to make an assertion call. It also includes generics matchers like:toEqual
,toContain
,toBeTruthy
Example:
expect(success).toBeTruthy();
Common assertions
Checkbox is checked:
expect(locator).toBeChecked()
Checkbox is checked:
expect(locator).toBeVisible()
Checkbox is checked:
expect(locator).toHaveCount()
Checkbox is checked:
expect(locator).toHaveText()
Checkbox is checked:
expect(page).toHaveTitle()
Element is in view port:
await expect(locator).toBeInViewPort()
Page to have a title:
await expect(page).toHaveTitle()
Screenshots and Videos
Capturing screenshots of pages
await page.screenshot({ path: 'screenshot.png' }); #quick screenshot
await page.screenshot({ path: 'screenshot.png', fullPage: true }); ##full page screenshot
Recording videos of tests
Playwright helps you to record your test cases, you can make changes in your
playwright.config.ts
fileoptions available:
’off’
,‘on‘
,‘retain-on-failure‘
import { defineConfig } from '@playwright/test'; export default defineConfig({ use: { video: { mode: 'on-first-retry', size: { width: 640, height: 480 } } }, });
Handling Authentication
Managing user authentication
Make a file in
test/auth.setup.ts
import { test as setup, expect } from '@playwright/test'; import path from 'path'; const authFile = path.join(__dirname, '../playwright/.auth/user.json'); setup('authenticate', async ({ page }) => { // Perform authentication steps. Replace these actions with your own. await page.goto('https://github.com/login'); await page.getByLabel('Username or email address').fill('username'); await page.getByLabel('Password').fill('password'); await page.getByRole('button', { name: 'Sign in' }).click(); // Wait until the page receives the cookies. // // Sometimes login flow sets cookies in the process of several redirects. // Wait for the final URL to ensure that the cookies are actually set. await page.waitForURL('https://github.com/'); // Alternatively, you can wait until the page reaches a state where all cookies are set. await expect(page.getByRole('button', { name: 'View profile and more' })).toBeVisible(); // End of authentication steps. await page.context().storageState({ path: authFile }); });
Create a new setup project in
playwright.config.ts
import { defineConfig, devices } from '@playwright/test'; export default defineConfig({ projects: [ // Setup project { name: 'setup', testMatch: /.*\.setup\.ts/ }, ] . . });
Now all your tests will run with authentication first
import { test } from '@playwright/test'; test('test', async ({ page }) => { // page is authenticated await page.goto('https://github.com/nimit2801'); });
Visual Regression Testing
How to setup Visual testing
Code Snippet
import { test, expect } from '@playwright/test'; test('example test', async ({ page }) => { await page.goto('https://devassure.io'); await expect(page).toHaveScreenshot('landing.png'); });
Managing snapshots
Updating screenshots:
npx playwright test --update-snapshots
Keep a folder dedicated to store your snapshots
/tests/snapshots
- Relative Path:
expect().toHaveScreenshot(['relative', 'path', 'to', 'snapshot.png'])
- Relative Path:
Best Practices
Test Structure and Organisation | Test Suites
Explanation: Properly structuring and organizing your tests is crucial for maintainability and readability. Playwright supports test suites, which allow you to group related tests together.
Code snippet:
// tests/login.spec.js const { test, expect } = require('@playwright/test'); test.describe('Login Functionality', () => { test('should login with valid credentials', async ({ page }) => { // Test implementation }); test('should show error with invalid credentials', async ({ page }) => { // Test implementation }); }); // tests/search.spec.js test.describe('Search Functionality', () => { test('should return results for valid search term', async ({ page }) => { // Test implementation }); test('should show no results for invalid search term', async ({ page }) => { // Test implementation }); });
Maintaining Test Code | Page Object Model
Explanation: The Page Object Model (POM) is a design pattern that creates an object repository for storing all web elements. It helps reduce code duplication and improves test maintenance.
Code snippet:
// pages/LoginPage.js class LoginPage { constructor(page) { this.page = page; this.usernameInput = page.locator('#username'); this.passwordInput = page.locator('#password'); this.loginButton = page.locator('#login-button'); } async login(username, password) { await this.usernameInput.fill(username); await this.passwordInput.fill(password); await this.loginButton.click(); } } // tests/login.spec.js const { test, expect } = require('@playwright/test'); const { LoginPage } = require('../pages/LoginPage'); test('should login with valid credentials', async ({ page }) => { const loginPage = new LoginPage(page); await page.goto('https://example.com/login'); await loginPage.login('validuser', 'validpass'); await expect(page).toHaveURL('https://example.com/dashboard'); });
Debugging and Troubleshooting | Common issues
Explanation: Debugging Playwright tests can be challenging. Here are some common issues and solutions:
a. Timing issues: Replacing
waitFor
functions to withawait
andtoBeVisibl
Code snippet:
// Instead of: await page.click('#submit-button'); // Use: await page.waitForSelector('#submit-button'); await page.click('#submit-button');
b. Capturing screenshots on failure: Automatically capture screenshots when tests fail for easier debugging.
Code snippet:
// playwright.config.js module.exports = { use: { screenshot: 'only-on-failure', }, };
c. Using the Playwright Inspector: The Playwright Inspector is a powerful tool for debugging tests.
Explanation: Run your tests with the
--debug
flag to use the Inspector:npx playwright test --debug
This will open the Playwright Inspector, allowing you to step through your test, inspect the DOM, and more.
d. Handling dynamic content: Use
waitForFunction
to wait for specific conditions before proceeding.Code snippet:
await page.waitForFunction(() => { const element = document.querySelector('#dynamic-content'); return element && element.textContent.includes('Expected Text'); });
These best practices and code snippets should provide a good foundation for your Playwright blog post. Would you like me to elaborate on any specific area or provide more information on a particular topic?
Join Our Community
The Community is for people who want to get resources for Quality Engineering and Automation. We provide you with a space where you can ask questions, share projects and get mentorship. We've an amazing set of resources available for you.