QA Graphic
March 20, 2025

Parametrization in PlayWright

Test Uptime Status of Multiple Sites

Yesterday, I showed how to use Parametrization in Pytest. Here's an example of how you would run that same code in PlayWright with TypeScript:

This code checks to make sure the four websites are up and running. This is just a quick sanity test, it doesn't do any critical path testing.


import { test, expect } from '@playwright/test';
// List of websites to test
const WEBSITES = [
  "https://www.company.com",
  "https://qa1.company.com",
  "https://qa2.company.com",
  "https://stage.company.com",
];
// Configure Playwright to run in headless mode globally
test.use({ headless: true });
test(`Check Websites Status`, async ({ page }) => {
// Iterate over websites to create a test for each
    for (const website of WEBSITES) {
        test(`Check if ${website} is up and running`, async ({ page }) => {
            try {
                // Attempt to load the website
                await page.goto(website, { waitUntil: 'domcontentloaded' });
                // Check if page title exists and is not empty
                const title = await page.title();
                expect(title).not.toBe('');
                // Check if body element exists
                const body = page.locator('body');
                await expect(body).toBeVisible();
                // Log success
                console.log(`? ${website} is up and running (Title: ${title})`);
            } catch (error) {
                // Oh the Horror: Fail the test with a detailed message
                throw new Error(`Website ${website} failed: ${error.message}`);
            }
        });
    }
})

Permalink
March 13, 2025

Get Random Line from a File

Add some variety to your PlayWright tests

In PlayWright, you can easily get the contents of a file to include in a form. This is the getRandomLineFromFile function that I use to open up a local file and get a random line:


async function getRandomLineFromFile(filePath: string): Promise {
    try {
        // Read file content and split into lines
        const fileContent = await fs.readFile(filePath, 'utf8');
        const lines = fileContent.split('n').filter(line => line.trim() !== ''); // Remove empty lines
        
        if (lines.length === 0) {
            throw new Error('File is empty');
        }
        
        // Get random line
        const randomIndex = Math.floor(Math.random() * lines.length);
        return lines[randomIndex].trim();
    } catch (error) {
        console.error(`Error reading file: ${error}`);
        throw error;
    }
}

I would use this to open up a file that has a random sentence to fill in a feedback form. Here's an example PlayWright with TypeScript entry that I would use:


test('Random Feedback Form Test', async ({ page }) => {
    const filePath = '/Users/dict/srand.txt';
    const randomLine = await getRandomLineFromFile(filePath);
    await page.goto('https://www....');
    await page.waitForLoadState("networkidle");
    await page.getByLabel('name').fill('Chris Ryan');
    await page.getByLabel('comment').fill(testText);
....
})

You could also do this to randomize names, locations etc. This is just handy to have when you want to add some variety to a test run.

Permalink
March 6, 2025

XPath with Playwright page.locator

A Practical Guide

Playwright page.locator method is designed to find elements dynamically, with built-in support for multiple selector types - including CSS, text, and yes, XPath. While CSS selectors are great for straightforward queries, XPath shines when you need more flexibility or when dealing with complex DOM structures.

Here's why XPath might be your go-to:

  • Structural Navigation: XPath lets you traverse the DOM based on relationships (e.g., parent, sibling, child) rather than just classes or IDs.

  • Attribute Precision: Target elements by any attribute, not just class or id.

  • Text-Based Selection: Easily find elements containing specific text, even partial matches.

  • Dynamic Pages: XPath can handle scenarios where CSS selectors falter, like when class names are auto-generated or unpredictable.

Playwright's page.locator makes XPath a first-class citizen, so let's see it in action.

Getting Started with page.locator and XPath

The syntax for using XPath in page.locator is simple: prefix your XPath expression with xpath= or use the double-slash shorthand //. Here's the basic structure:


await page.locator('xpath=//tag[@attribute="value"]').click();

Playwright will evaluate the XPath expression and return a Locator object, which you can then interact with (e.g., click(), fill(), textContent()).

Practical Example

Let's walk through a real-world scenario where XPath and page.locator save the day.

Targeting an Element by Attribute

Imagine a login form with a button lacking a unique ID or class:


<button type="submit" data-test="login-btn">Sign In</button>

With XPath, you can target it by its data-test attribute:


const { test } = require('@playwright/test');
test('click login button', async ({ page }) => {
  await page.goto('https://example.com/login');
  await page.locator('xpath=//button[@data-test="login-btn"]').click();
});

The //button[@data-test="login-btn"] means "find any <button> element with a data-test attribute equal to login-btn."

When to Avoid XPath

While XPath is powerful, it?s not always the best choice:

  • Simple Selectors: Use CSS for #id or .class?it?s faster and more readable.
  • Dynamic IDs: If attributes change frequently, text-based or role-based selectors (role=) might be more stable.
  • Maintenance: Complex XPath expressions can become brittle if the DOM structure shifts.

Permalink
December 10, 2024

PlayWright URL Scraping

Sample Code to get all URLs

While experimenting with Playwright this week, I put together a script that grabs all the URLs from a website and writes them to a file. Here's the code that I finally came up with:

This approach is particularly useful when you need to ensure that all the anchor tags on the homepage are functioning as expected. By verifying the anchor tags separately, you can isolate any issues related to broken or misconfigured links, making it easier to pinpoint and address problems.

Additionally, I'll create another test specifically to validate that the URLs associated with these anchor tags are correct. This two-pronged strategy ensures that both the structure and the destinations of your links are accurate.

Pro Tip: The reason for separating these tasks, instead of validating the URLs while scraping the homepage, is to enhance the efficiency of your test execution. By dividing the workload into smaller, targeted tests, you can leverage parallel execution to speed up the overall testing process. This approach not only reduces the total runtime of your test suite but also provides clearer insights into potential issues, allowing you to debug faster and more effectively.

Permalink

About

Welcome to Playwright Tips and Tricks, your go-to resource for mastering the art of web automation and testing with Playwright! Whether you're a seasoned developer looking to streamline your workflows or a curious beginner eager to dive into the world of browser automation, this blog is designed with you in mind. Here, I'll share a treasure trove of practical insights, clever hacks, and step-by-step guides to help you harness the full power of Playwright - a modern, open-source tool that's revolutionizing how we interact with web applications.

Check out all the blog posts.

Blog Schedule

Friday 21 Macintosh
Saturday 22 Internet Tools
Sunday 23 Misc
Monday 24 Media
Tuesday 25 QA
Wednesday 26 Pytest
Thursday 27 PlayWright