Automation QA Testing Course Content

JavaScript-Playwright

 Playwright With Javascript | Introduction 

Playwright:

Playwright enables reliable end-to-end testing for modern web apps

  • Application Supported - Web browser apps, Mobile Web apps,     API
  • Languages  Supported - Javascript , Typescript , Java , Python & .Net (C#)
  • Browsers Supported - Chromium , WedKit(Safari) and Firefox(headed / headless)
  •  OS Supported - Windows , MacOS , Linux , Support CI Runs

  • Features Playwright:
  • Free & Open source
  • Multi-Browser , Multi-Language , Multi-OS support
  •  Easy setup and configuration
  • Functional , API accessibility testing
  • Built-in reporters , custom reports
  • CI , CD , Docker
  • Parallel testing
  • Auto wait
  • Built-in assertions
  • Multi tab &multi window
  • Frames , shadow dom
  • Test Parameter
  • Emulate mobile devices
  • Faster
Selenium Vs Playwright Vs Cypress differences:

Playwright With Javascript | Installation 

  • Nodejs: Install nodejs if not available in your computer
  • to find node version use
  • node -v or node --version
  • to find the npm version use 
  • npm -v or npm --version

  •                 
  • VS Code : download VScode editor
  • Create project folder--> open in VS code
  • install playwright using terminal       
  • npm init playwright@latest

    Run the install command and select the following to get started:

    • Choose between TypeScript or JavaScript (default is TypeScript)
    • Name of your Tests folder (default is tests or e2e if you already have a tests folder in your project)
    • Add a GitHub Actions workflow to easily run tests on CI
    • Install Playwright browsers (default is true)

What's Installed

Playwright will download the browsers needed as well as create the following files.

playwright.config.ts
package.json
package-lock.json
tests/
example.spec.ts
tests-examples/
demo-todo-app.spec.ts

The playwright.config is where you can add configuration for Playwright including modifying which browsers you would like to run Playwright on.

 If you are running tests inside an already existing project then dependencies will be added directly to your package.json.

The tests folder contains a basic example test to help you get started with testing. For a more detailed example check out the tests-examples folder which contains tests written to test a todo app.

  • package . json ---node project management file
  • playwright . config . js -- playwright configuration
  • tests --- we can  wright  all the playwright tests
  • npm playwright -v    -- return installed version of playwright .


  • install playwright using vs code extension

  •  run the playwright test
  • npx playwright test
  • npx playwright test --headed
  • npx playwright show-report

  • How to Create and Run playwright Tests:

  • npx playwright test           runs all tests on all browsers in headless mode 
  • npx playwright test     My Test . spec . js         runs a specific test file
  • npx playwright test     My Test . spec . js  --project=chromium           runs on specific browser
  • npx playwright test     My Test . spec .js  --project =chromium  --headed
  • npx playwright test     My Test . spec .js  --project =chromium  --headed  --debug

Writing or Generating first Test in Playwright:
What is mean by Asynchronous?
Asynchronous programming in JavaScript means that operations can run independently of the main program flow, allowing the program to continue running without waiting for these operations to complete. This approach is essential for handling tasks that may take some time to finish, such as fetching data from a server, reading files, or interacting with APIs.

In JavaScript Playwright test blocks, async and await are used to handle asynchronous operations in a clear and concise manner. They enable the test to pause execution until an asynchronous operation (such as navigating to a page, clicking a button, or waiting for an element) completes, which is essential for ensuring that the browser state is ready for the next test step.


What is async?

The async keyword is used to declare a function as asynchronous. This means the function will return a Promise, allowing the use of await within it to pause execution until the Promise is resolved.

Example:

test('Sample Test', async () => {

  console.log('Before wait');

  await new Promise(resolve => setTimeout(resolve, 2000)); // Waits for 2 seconds

  console.log('After wait');

});

What is await?

The await keyword is used inside an async function to pause the execution of the function until the Promise it is waiting for is resolved. It makes the code appear synchronous, even though the operations are asynchronous.

Why use async and await in Playwright?

Playwright's APIs are asynchronous because they interact with the browser in real time. Actions like navigating to a page, clicking an element, or waiting for a condition all return Promises. Using async and await ensures that each action completes before moving on to the next, which is crucial for test reliability.

const { test, expect } = require('@playwright/test');


test('Example Playwright Test', async ({ page }) => {

  // Navigate to a page

  await page.goto('https://example.com');


  // Type text into an input field

  await page.fill('#username', 'testuser');


  // Click a button

  await page.click('#login');


  // Wait for an element to be visible

  await page.waitForSelector('#dashboard');


  // Assert that the dashboard title is correct

  const title = await page.title();

  expect(title).toBe('Dashboard');

});

Key Points:

  1. async Function: Makes the function return a Promise, enabling the use of await.
  2. await Keyword: Waits for the resolution of a Promise before proceeding to the next line.
  3. Readability: Simplifies the handling of sequential asynchronous operations, avoiding deeply nested .then() calls (callback hell).

Benefits in Playwright:

  • Ensures each action completes before the next starts, avoiding race conditions.
  • Improves test readability and maintainability.
  • Makes debugging easier by mimicking a synchronous flow for asynchronous operations.

=================================

  • Locating Element in Playwright

  • Property
  • css
  • xpath

  • Locate single element
  • link/button:
  • await page . locator ('locator').click()
  • await page . click(locator);
  • inputbox:
  •  await page. locator('locator').fill('value')
  • await page .locator('locator') .type('value')
  • await page . fill('locator', 'value')
  • await page . type('locator' , 'value')
  • Locate multiple wed element
  • const element=await page.$$(locator)

  • Built-in  in locators.
  • page. getByRole() to locate by explicit and implicit accessibility attributes.
  • page. getByText() to locate by text content.
  • page. getByLabe() to locater a form control by associated label's text.
  • page . getByPlaceholder() to locate an input by placeholder.
  • page . getByAltText() to locate an element, usually image , by its text alternative.
  • page . getByTitle() to locate an element by its title attribute.
  • page . getByTestId() to locate an element based on its data-testid attribute.
  • program with above locators;
  • const{test,expect} = require('@playwright/test');
  • test('Verify opencart homepage title', async ({ page }) =>{

        //navigate to the url
       await page.goto('https://opensource-demo.orangehrmlive.com/web/index.php/auth/login
  • ');
  • //page . getByAltText () - to locate an element , usually image , by its text alternative.
  • const logo=await page. getByAltText('company-branding')
  • await expect(logo). toBeVisible();
  • //page. getByPlaceholder() - locate an input by placeholder.
  • await page. getByplaceholder('Username') .fill("Admin")
  • await page. getByPlaceholder('Password') . fill("admin123")
  • await page . getByRole('button' ,{type: 'submit'} ) .click()
  • const name =await page . locator('//p[@class="oxd-userdropdown-name"]') .textContent()
  • await expect(await page . getByText(name) ) .toBeVisible()
  • })

Select options:

Handle Dropdowns:
URL:https://testautomationpractice.blogspot.com///Multiple ways to select option from the dropdown//
await page.laocator("#country").selectOption({lable:'India'}); //lable/ visible text//
await page.lacator("#country").selectOption('India');//visible text//
await page.locator("#country").selectOption({value: 'uk'}); // by using value//
await page.locator("#country").selectOption{index:1});// by using index//
await page.selectOption("#country",'India'); //by text
//Assertion//1) check number of option in Dropdown Approach1
const option=await page.locator('#country option')
await expect(option).toHaveCount(10);
//2) check number of option in dropdown Approach2
const options=await page.$$('#country option')
console.log("Number of option:", option.length)
//await expect(options.length).toBe(10);
//3)check presence of value in the dropdown - Appraoch1
const content=await page.locator('#country').textContent()
await expect(content.includes('India')).toBeTruthy();
//4)check presence of value in the dropdown - Approach2
const options=await page.$$('#country option')
let status=false;
for(const option of options){
//console.log(await option.textContent())
let value=await option.textContent();
{status=true;  }}
//5)select option from dropdown using loop
const options=await page.$$('#country option')
for(const option of options)
{let value=await option.textContent();
if(value.inludes('France'))
{await page.selectOption("country",);break;  }}
-------------------------------------------------------------
Handle Multi Select DropDown:
const {test, expect}=require('@playwright/test')test("Handle dropdown",async ({page})=>{   await page.goto('https://testautomationpractice.blogspot.com/');//Select multiple options from multi select dropdown
await page.selectOption('#colors',['Bule','Red','Yellow'])
//Assertions//1) check number of option in dropdown
//const options=await page.locator('#colors option')
//await expect(options).toHaveCount(5);
//2) check number of options in dropdown usingJS array
//const options=await page.$$('#colors option')
//console.log("Number of option:",options.length)
//await expect(options.length).toBe(5);
//3) check presence of value in the dropdown
const content=await page.locator('#colors').textContent()
await expect(content.includes('Blue')).toBeTruthy();}

const  {test, expect}=require( '@playwright/test' )

test('Boostrap  dropdown',  async   ({page}) =>{
     await  page.goto( 'https://www.jquery-az.com/boots/demo.php?ex=63.0_2' )



Assertion:

Playwright Assertions validate the behaviour of App Under Test. It decides whether the application is behaving as per expectations or not, based on which it assigns Fail or Pass to a Test Case.

By incorporating assertions into Playwright tests, you can confirm that the application performs as intended, enhancing the reliability and quality of the software.

What are Playwright Assertions?

Playwright Assertions are a set of built-in functions which includes expect() functionprovided by the Playwright testing framework to validate the behavior and state of a web application during automated tests.

Playwright Assertions are used to verify whether specific conditions are met, such as checking if an element exists, contains certain text, or has a particular state. These assertions are essential for confirming that the application behaves as expected during end-to-end testing.

Playwright provides diverse assertion types:

  • Element States: Check the visibility, availability, and interactivity of UI elements.
  • Content Validation: Ensure elements display the correct text, values, or match specific patterns.
  • Page Properties: Assertions can confirm page details like URLs, titles, or cookie presence.
  • Network Interactions: Verify the outcomes of network requests and responses to ensure proper data loading and form submissions.

Playwright Expect() Function

Playwright provides assertion using expect() function. Using expect() you can assert the expected result with actual result 



Playwright includes test assertion in the form of expect function.

https://playwright.dev/docs/test-assertions

1)expect(page).toHaveURL()                 page has URL

2)expect(page).toHaveTitle()                  page has title

3)expect(locator).toBeVisible()                Element is visible

4)expect(locator).toBeEnabled()               Control is enabled
    expect(locator).toBeDisabled()               Element is disabled

5)expect(locator).toBeChecked()                Radio/Checkbox ischecked

6)expect(locator).toHaveAttribute()        Element has attribute

7)expect(locator).toHaveText()                 Element matches text

8)expect(locator).toContainText()           Element contains text

9)expect(locator).toHaveValue(value)     Input has a value

10)expect(locator).toHaveCount()          List of elements has given length

//open app url
await page.goto('https://demo.nopcommerce.com/register')

//1) expect(page).toHaveURL()           page has URL
await expect(page).toHaveURL('https://demo.nopmmerce.com/register')

//2) expect(page).toHaveTitle()           page has title
await expect(page).toHaveTitle('nopCommerce demo store. Register')

//3) expect(locator).toBeVisible()            Element is visible
        const logoElement=await page.locator('.header-logo')
         await expect(logoElement).toBeVisible()

//4) expect(locator).toBeEnabled()         Control is enabled
const searchStoreBox=await page.locator('#small-searchterms')
await expect(searchStoreBox).toBeEnabled()

//5) expect(locator).toBeChecked()      Radio/Checkbox is checked
const maleRadioButton=await page.locator('#gender-male')
await maleRadioButton.click()

//check box
const newsletterCheckbox=await page.locator('#Newsletter')
await expect(newsletterCheckbox).toBeChecked()

//6) expect(locator).toHaveAttribute()      Element has attribute
const regButton=await page.locator('register-button')
await expect(reButton).toHaveAttribute('type','submit')

//7) expect(locator).toHaveText()           Element matches text

await expect(await page.locator('.page-title h1')).toHaveText(Register')

//8) expect(locator).toContainText()       Element contains text
await expect(awit page.locator('.page-title h1')).toContainText('Reg')

//9) expect(locator).toHaveValue(value)      Input has a value
const emailInput=await page.locator('#Email')
await emailInput.fill('test@demo.com');
await expect(emailInput).toHaveValue('test@demo.com')

//10) expect(locator).toHaveCount()      List of element has given length
const options=await page.locator('select[name="DateofBirthMonth"] option')
await expect(options).toHaveCount(13)
})


Soft Assertion:

By default Assertion will abort the test as soon as the expected result is not matched with the actual result. There are cases where we have to check multiple assertions and at the end of the test throw the assertion error.

Soft assertion is good for cases where we want to assert multiple cases and then fail the test at the end.

Below is the example test. The test has two assertions and both will execute and fail.

test("example for soft assertion", async ({page}) => {

 await page.goto("https://bstackdemo.com/")

 const filter = await page.locator(".filters .filters-available-size")

 const filterCount = await filter.count()

 await expect.soft(filterCount).not.toEqual(4)

 await expect.soft(page).toHaveTitle("StackDemo!")

})
------------------------------------------------------------------------
test("Soft  assertion",async ({page})=>{

  await page.goto("https:/www.demoblaze.com/index.html")

//Hard assertions
/*await  expect(page).toHaveTitle('STORE123');
await  expect(page).toHaveURL("https://www.demoblaze.com/index.html");
await  expect(page).locator('.navbar-brand')).toBeVisible():

*/
      //soft assertions
          await  expect.soft(page).toHaveTitle('STORE123');
          await  expect.soft(page).tohaveURL("https://www.demoblaze.com/index.html");
await  expect.soft(page.locator('.navbar-brand')).toBeVisible();
})

------------------------------------------------

Negating Matchers

Negating Matchers are used when we want to check that a certain condition does not hold true. It essentially reverses the condition you’re checking for, enabling you to assert the absence of a condition or element. Negating Matchers are especially helpful for ensuring that a web page or application is free from errors, incorrect states, or unwanted elements.

Below is the example test. The test will assert for filter count not matching the value 3

test("example for negating matcher", async ({page}) => {

 await page.goto("https://bstackdemo.com/")

 const filter = await page.locator(".filters .filters-available-size")

 const filterCount = await filter.count()

 await expect(filterCount).not.toEqual(3)

})
---------------------------------------------------------------------

 Assert Non-Presence

 Assert the non-presence of elements or messages, particularly useful in validating error handling and user feedback:

await expect(page.locator('text=Error')).not.toBeVisible();

-----------------------------------------------------

 Assert Network Interactions

Capture and assert network responses to ensure backend integration is functioning as expected:

const [response] = await Promise.all([
  page.waitForResponse(resp => resp.url().includes('/api/submit') && resp.status() === 200),
  page.click('button#submit')
]);
await expect(response).toBeOK();

------------------------------------------------------------

handle checkboxes

const {test, expect}=require('@palywright/test')

test("Handle checkboxes",async  ({page})=>{

await page.goto( 'https://itera-qa.azurewebsites.net/home/automation');

//single  checkboxs
await page. locator("//input[@id='monday' and @type='checkbox']").check();
//await page.check("//input[@id='monday' and @type='checkbox']") ;

expect(await page.locator("// input [@id='monday' and @type=' checkbox']") ).
toBeChecked();
expect(await page. locator("// input [@id='monday ' and @type='checkbox']") ).
isChecked() .ked();toBeTruthy();
expect(await page. locator("//input [@id='sunday' and @type='checkbox']") ).
isChecked() ).toBeTruthy();

//Multiple  checkboxes
const  checkboxLocators=[
                            "//input [@id='monday' and @type='checkbox']",
                             "//input [@id='sunday' and @type='checkbox']",
                              "//input [@id='saturday' and @type='checkbox']"
                   ];
for(const  locator  of  checkboxLocators)    //  select multiple  checkboxes
{
         await page. locator(locator).check();
}

for(const  locator  of  checkboxLocators)       // unselect multiple checkboxes which are already selected
{

       if(await  page.locator(locator) .isChecked() )
        {
        await  page.locator(locator).uncheck();
         }
}
     await page.waitForTimeout(5000);
})

=====================================================

// Enabling confirm dialog handler
  page.on('dialog', async (dialog) => {
    // Asserting dialog type and message
    expect(dialog.type()).toBe('confirm');
    expect(dialog.message()).toContain('Press a button');
    // Accepting the dialog
    await dialog.accept();
//await dialog.dismiss(); // close by using cancel button
  });

  // Clicking the button to trigger the alert dialog
  await page.click('button[normalize-space()="Confirm Box"]');

await expect(page.locator('//p[@id='demo']')).toHaveText('You pressed Ok!')


------------------------------------------------

Prompt dialog

// Enabling confirm dialog handler
  page.on('dialog', async (dialog) => {
    // Asserting dialog type and message
    expect(dialog.type()).toBe('prompt');
    expect(dialog.message()).toContain('Please enter your name:');
  expect(dialog.defaultValue()).toContain('Harry Potter');
    // Accepting the dialog
    await dialog.accept('John');
//await dialog.dismiss(); // close by using cancel button
  });

  // Clicking the button to trigger the alert dialog
  await page.click('button[normalize-space()="Prompt"]');

await expect(page.locator('//p[@id='demo']')).toHaveText('Hello John! How are you doing today')
-----------------------------------------------------------------------


// Importing Playwright test module
const { test, expect } = require('@playwright/test');

// Test to handle frames and interact with elements inside them
test('Handle Frames and iFrames', async ({ page }) => {
  // Navigating to the test page
  await page.goto('https://ui.vision/demo/webtest/frames/');

  // Getting all frames on the page
  const frames = page.frames();
  console.log('Number of frames:', frames.length);

  // Approach 1: Using frame name or URL
  const frame1 = page.frame({ url: /frame_1.html/ }); // Match frame by partial URL
  if (frame1) {
    await frame1.fill('[name="mytext1"]', 'Hello using frame name/URL');
  } else {
    console.error('Frame with URL "frame_1.html" not found');
  }

  // Approach 2: Using frame locator
  const inputBox = page
    .frameLocator('iframe[src="frame_1.html"]')
    .locator('[name="mytext1"]');
  await inputBox.fill('Hello using frame locator');
});

--------------------------------------------------------------------------------------

Inner frames:

const { test, expect } = require('@playwright/test');

test('Handle Inner and Nested Frames', async ({ page }) => {
  // Navigate to the test page
  await page.goto('https://ui.vision/demo/webtest/frames/');

  // Handle the parent frame using its URL
  const parentFrame = page.frame({ url: /frame_3.html/ });
  if (parentFrame) {
    // Fill input field inside the parent frame
    await parentFrame.fill('input[name="mytext3"]', 'Welcome');
  } else {
    console.error('Parent frame with URL "frame_3.html" not found');
    return;
  }

  // Handle nested frames within the parent frame
  const nestedFrames = parentFrame.childFrames();
  if (nestedFrames.length > 0) {
    // Interact with the first nested frame
    await nestedFrames[0]
      .locator('//*[@id="i5"]/div[3]/div')
      .check();
  } else {
    console.error('No nested frames found within the parent frame');
  }

  // Wait for a few seconds to observe the result
  await page.waitForTimeout(5000);
});

--------------------------------------------------------------------------------------------------------

Handle Web Tables:

const { test, expect } = require('@playwright/test');

test('Handling Web Table', async ({ page }) => {
  // Navigate to the target page
  await page.goto('https://testautomationpractice.blogspot.com/');

  // Locate the table
  const table = page.locator('#productTable');

  // Validate table presence
  if (!(await table.isVisible())) {
    console.error('Table not found on the page');
    return;
  }

  // Get the total number of columns
  const columns = table.locator('thead tr th');
  const columnCount = await columns.count();
  console.log('Number of columns:', columnCount);

  // Get the total number of rows
  const rows = table.locator('tbody tr');
  const rowCount = await rows.count();
  console.log('Number of rows:', rowCount);

  // Validate the expected number of columns and rows
  expect(columnCount).toBe(4); // Update this based on the expected column count
  expect(rowCount).toBe(5); // Update this based on the expected row count

  // Log table content (optional)
  for (let i = 0; i < rowCount; i++) {
    const row = rows.nth(i);
    const rowData = await row.allTextContents();
    console.log(Row ${i + 1}:, rowData.join(' | '));
  }
});

-------------------------------------------------------------------------

How to start with Playwright Debugging?


What is Debugging, and why is it important? 

Debugging is finding the root cause of the issue and resolving it. It is vital to the testing process because of it:

  • Helps resolve the issues quickly
  • Identify the exact root cause
  • Eliminates complexities
  • Serves as proof to analyze the software quality
  • It provides a way to validate functionality by applying dynamic values

A debugging process consists of the following steps:

  • Note down the error
  • Assume the error location based on the expertise
  • Set the debugger breakpoints
  • Analyze the code during run time step by step.
  • Find the exact location that is causing the issue
  • Fix the line of code which is causing the issue
  • Validate the code for a successful fix of the error.

How to run Playwright debug mode?

One of the key features of Playwright is debugging tests in the following ways:

  1. Playwright Inspector
  2. Playwright Trace Viewer​
  3. Browser Developer Tools​
  4. Visual Studio Code debugger
  5. Verbose API logs​

Debugging using Playwright Inspector

What is a Playwright Inspector?

Playwright Inspector is a GUI tool that comes with the framework by default, and no additional configuration is required to use this tool.

To launch your test with Playwright Inspector mode, you need to prefix the test command with PWDEBUG=1 depending on the command-line tool you are using, the syntax might differ.

Powershell

$env:PWDEBUG=1

npx run test

Bash

PWDEBUG=1 npx run test

Batch

set PWDEBUG=1

npx run test

Once you enter the command, the UI window also known as Inspector windows opens and shows the line is being executed. You can debug the test line by line using this window.

Debugging using Playwright InspectorPoints to Remember when using PWDEBUG=1 flag:

  1. The browser launches in headed mode by default.
  2. There will be no default time-out.
  3. Playwright Inspector window is split into two parts: the upper part shows your code, and the lower part code shows the execution log.
  4. The Playwright provides two options either you can resume the script or you can step over.
  5. If you want to pause the test at the desired line use await page.pause(); in your script.
  6. If you add the await page.pause() playwright automatically opens the Inspector Window even though you have not set the PWDEBUG=1 flag.

Any time, if you don’t want to attach the Playwright inspector, change the PWDEBUG flag to 0 i.e PWDEBUG=0

Recording Scripts using Playwright Inspector Tool

Sometimes you might find it challenging to understand how the locators simulate flows using scripts. Playwright Inspector Tool also provides a command called codegen, Popularly known as Playwright codegen that allows us to record the scripts automatically. 

The codegen is like a recording tool. However, you can use this to generate complex scripts and later paste them inside our test case.

From the terminal, you can enter the below command to record the tests using Playwright Codegen Tool

npx playwright codegen <webpage_url>

Example:

npx playwright codegen browserstack.com

Once the above command is entered in the terminal, the Playwright inspector window launches. The record button in the inspector window helps to start or stop the recording at any desired point. As the browser opens baseURL, you can navigate the actions or workflows. 

Upon completing the recording, stop the recording, Save/Copy the recorded script, and close the Playwright inspector window.

Debugging using Playwright Inspector

Playwright Debugging using Browser Developer Tools

DevTools are part of the browser, which enables easy debugging. While most browsers have this functionality, the shortcut key might differ from browser to browser. On your browser window right click and choose inspect element. If you are using a chromium-based browser, you can also choose CTRL +SHIFT + I or F12 to open DevTools windows.

Browser Developer tools are popularly known as DevTools. The browser developer tools are still accessible when running playwright tests.

Some of the actions that can be performed using Browser Developer Tools are:

  1. Inspect DOM Element
  2. Run commands in the browser console.
  3. Check console logs during execution
  4. Verify Network calls/requests in the Network tab

Apart from the actions listed above, any action that can be performed on a normal browser while working with webpages can be done using browser developer tools with Playwright.

Working with Playwright Object in Browser Developer Tools console (Chrome DevTools Console).

The Playwright allows you to highlight selectors in your browser console with the Playwright object. This is the most useful option, it helps debug locators and view the locators rendered during run time.

To utilize this option, Playwright needs to be launched in debug mode as explained above (using PWDEBUG=1 flag). Once you launch the Playwright test with debug mode, the Playwright Object is available in the browser console.

There are many ways to highlight the locators using playwright objects, such as:

  1. playwright.$(selector): Highlights the first occurrence of the selector. This is equivalent to a page.$ usage in the script.
  2. playwright.$$(selector): Highlights all occurrences of the selector. This is equivalent to a page.$$ usage in the script.
  3. playwright.inspect(selector): Inspects the selector in the Elements panel.
  4. playwright.locator(selector): Highlights the first occurrence of the locator.
  5. playwright.clear(): Clears existing highlights.
  6. playwright.selector(element): Generates a selector that points to the element.

Example:

playwright.$("a[href='/docs/intro']")

The above command in the browser console highlights the web element containing with locator a[href=’/docs/intro’]

=Debugging using Browser Developer Tools

Playwright Debugging using Visual Studio Code

Playwright works well with Visual Studio Code. Suppose you are familiar with Java or C# and seek to debug using IDE breakpoints or the traditional debugging style by setting and unsetting breakpoints. In that case, Playwright provides the same way of debugging options.

To debug Playwright scripts using VS Code, follow the below steps.

Step 1: Navigate to Visual Studio Code Run Menu > Click on Add Configuration

Configuring Visual Studio for Debugging Playwright

Step 2: Choose NodJS as an Environment

Setting NodeJS as Environment for Playwright Debugging

Step 3: The launch.json will be created inside our project folder automatically. You can check the file under

<Project_Folder>/.vscode/launch.json

JSON File for Playwright Demo

Step 4: Edit launch.json file and enter the below code to it.

{

    "version": "0.2.0",

    "configurations": [

        {

            "type": "pwa-node",

            "request": "launch",

            "name": "Launch Program",

            "skipFiles": [

                "<node_internals>/**"

            ],

            "program": "${file}",

            "runtimeExecutable": "npm",

            "runtimeArgs": [

                "run-script",

                "test"

            ],

        }

    ]

}

Step 5: Add test command/value to script property in package.json. The package.json should be located in the project root directory (Note: If package.json is not available in your Project root directory, then you can create one using npm init command). Enter the below code to the package.json and save the configuration.

  "scripts": {

    "test": "npx playwright test --headed" 

  }

Step 6: Run with Configuration in Visual Studio Code by following the below steps

  • Set the breakpoint in your code with VSCode IDE
  • Launch the test with Run (Menu)  > Start Debugging or F5
  • The test starts with the debugger attached, the test execution should halt when it hits your breakpoint.

Running Visual Studio for Debugging

Debugging Playwright Tests with Trace Viewer

Trace Viewer is another functionality that can be used while Playwright debugging. Trace Viewer is a GUI tool that shows the traces recorded during test execution. Trace viewers can be opened using CLI or on the browser.

Recording Trace in Playwright

To record Trace, you need to configure it in the Global config file, and then follow these steps:

Step 1: Create a Global Playwright Config File i.e playwright.config.ts

Step 2: Place the playwright.config.ts under the Project root directory

Step 3: Add the below code in it

// playwright.config.ts

import { PlaywrightTestConfig } from '@playwright/test';

const config: PlaywrightTestConfig = {

  use:{

    trace:'on'

  },

};

export default config;

In the above code, the trace option has on value, likewise, you can provide different values from the list of available options for trace

  • ‘off’ – Do not record a trace.
  • ‘on’ – Record a trace for each test.
  • ‘retain-on-failure’ – Record a trace for each test, but remove it from successful test runs.
  • ‘on-first-retry’ – Record a trace only when retrying a test for the first time.

Once you run the test after configuring the trace option to the global config file. Trace will be recorded and stored in the test-results directory.

Traces of your tests will be recorded action-wise. Each action contains

  • action snapshots,
  • action log,
  • source code location,
  • network log for this action

Viewing Traces in Playwright

You can view the recorded Traces by following the below steps:

Step 1: Run/Execute your test

Step 2: Look for traces.zip inside your test-results folder

Step 3: From CLI you can enter the path to trace file in the following format 

npx playwright show-trace <path_to_trace.zip_directory>/trace.zip

For Example:

npx playwright show-trace test-results\tests-example-basic-test\trace.zip

Step 4: Open trace.zip from the browser (Optional). Navigate to https://trace.playwright.dev/ and then drag and drop the trace.zip folder as seen below

Debugging using Trace Viewer

Debug tests with Playwright Verbose Logging

Amidst the several debugging options provided by Playwright, Verbose logging is another way to debug Playwright tests where the QA can see the verbose logs and analyze the scripts.

Enabling Verbose Logging in Playwright
The enabling of verbose logging depends on which type of CLI/Terminal you use.

Enable Verbose Logging with Bash

DEBUG=pw:api npx playwright test

Enable Verbose Logging with Powershell

$env:DEBUG="pw:api"
npx playwright test

Enable Verbose Logging with Batch

set DEBUG=pw:api
npx playwright test

Once you enable the Verbose Log, Playwright continuously feeds the logs to the command line so you can see what’s happening during the script execution.

Debugging using Verbose Logging

======================================================

Page Object Model with Playwright: Tutorial

What is a Page Object Model?

Popularly known as POM, Page Object Model is a design pattern that creates a repository for storing all web elements. It is useful in reducing code duplication and improves test script maintenance.

In Page Object Model, consider each web page of an application as a separate class file. Each class file will contain only corresponding web page elements. Using these elements, testers can perform operations on the website under test.

What is Page Object Model in Playwright?

Page Object Model in Playwright is a design pattern used i to create a structured approach for managing interactions with web pages. POM in Playwright helps in organizing test code by separating the representation of the web pages from the test logic, making tests more maintainable and readable.

Advantages of Page Object Model in Playwright

  • Easy Maintenance: In web automation, everything depends on the DOM tree and selectors. The page object model makes maintenance easier even if there is a change in the DOM tree and selectors we don’t have to modify everywhere. 
  • Increased Reusability: Using POM, we can reuse the code which is written for another test. Also, we can create custom helper methods to achieve this. Code Reusability reduces the code, thus saving time and effort.
  • Readability: As the tests are independent, it increases the readability
  • Reduced Code Duplication: Common page interactions are encapsulated within the page objects, allowing multiple tests to reuse the same methods and reducing redundancy.
  • Better Test Management: Grouping related actions and elements within a page object leads to a more coherent and manageable test suite.
  • Enhanced Debugging: POM offer clear traceability. So, when a test fails, it’s easier to identify the issue within the specific page object rather than sifting through multiple test scripts.

BrowserStack Automate Banner 8

Disadvantages of Page Object Model in Playwright

  • Initial Setup Time: Initial design and building framework take some time.
  • Advanced Skillset: Good coding skills are required to set the POM framework
  • Higher Risk: Elements are stored in a shared file, so even a tiny mistake in the page object file can lead to breaking the whole test suite.
  • Increased Complexity: For simple applications or small test suites, the POM can introduce unnecessary complexity by requiring additional classes and methods.
  • Tight Coupling of Interdependencies: If page objects are not well-designed, they can become tightly coupled, making it difficult to modify one without affecting others.
  • Limited Flexibility: The rigid structured nature of POM can make it harder to adapt to new testing strategies or tools without significant rework.

Implementing Page Object Model in Playwright

Pre-Requisites:

  1. Install Visual Studio Code: Download and Install Visual Studio Code(VSCode).
  2. Install NodeJS: Download and Install Node JS

Steps to get started with Page Object Model in Playwright

Step 1: Create a fresh new directory (ex: PlaywrightDemo) in VSCode

Step 2: Open Directory in Visual Studio Code. From VS code

Click on File > Open Folder > Choose newly Created Folder (PlaywrightDemo)

Step 3: From the VS Code, Click on Terminal Menu > Click on New Terminal 

Step 4: Enter the below command to start the Playwright installation

npm init playwright@latest

Note: The above command asks a set of questions. Please provide appropriate inputs. In this tutorial, we are using typescript language.

Once you run the above command, the below set of files and folders will be automatically created

  • tests folder: This folder contains actual test scripts. By default, an example.spec.ts file will be created inside this folder.
  • .gitignore: This file helps if you are using git repository
  • package.json and package-lock.json: This file helps to track dependencies, create a shortcut for running tests, etc.
  • playwright.config.ts: This is the global configuration file for the Playwright, which you can configure with available options.

Set up/Add additional folders for Playwright page object model

  • pages folder: Since we are using Page Object Model (POM) pattern the pages folder contains all the relevant page objects
  • utility folder: The common code/function, which can be used in different tests can be placed here. For example, generating a random number, getting a date and time, etc.

Step 5: Install Browsers

Install browsers using the command

npx playwright install

Once you complete the above steps, your Playwright Test Automation Project/ Framework should look like the below.

Screenshot 2022 05 25 at 6.47.41 PM

Let’s consider a simple scenario.

Navigate to the Browserstack home page.

Click on Products Menu

Verify All Submenus are Present

Step 6: Create a page object file inside the pages folder and name it home.page.ts

To achieve the above flow, we need a URL, menu element, etc.

//home.page.ts
import { expect, Locator, Page } from '@playwright/test';
export class BrowserstackHomePage {
readonly url ="https://www.browserstack.com/";
readonly page: Page;
readonly browserstackLogo: Locator;
readonly productsMenu: Locator;
readonly productmenudropdown:Locator

constructor(page: Page) {
this.page = page;
this.browserstackLogo = page.locator('#logo');
this.productsMenu = page.locator('#product-menu-toggle');
this.productmenudropdown = page.locator('#product-menu-dropdown >div > ul >li >a >div[class="dropdown-link-heading"]');
}

async goto(){
await this.page.goto(this.url);
}
async clickOnProducts(){
await this.productsMenu.waitFor({state:"visible"});
await this.productsMenu.click();
}
}

Step 7: Create a test using the above page object file.

Create a test file inside the tests folder and name it home.test.ts

To create a test, we need to import the page object file. Like below.

import { BrowserstackHomePage } from '../pages/home.page';

Once we import, we need to write the script and verify the submenus.

// home.test.ts
import { test, expect } from '@playwright/test';
import { BrowserstackHomePage } from '../pages/home.page';
test('Browserstack homepage verification', async ({ page }) => {
const homepage = new BrowserstackHomePage(page);
await homepage.goto();
await homepage.clickOnProducts();
await expect(homepage.productmenudropdown).toContainText(["Live", "Automate", "Percy", "App Live", "App Automate"])
});

After the creation of the above test file, your project looks like below

Screenshot 2022 05 25 at 6.54.50 PM

Step 8: Execute your test.

Execute you are using the below command

npx playwright test

Talk to an Expert

By default Playwright test runs in headless mode, to run in headed mode use -– headed flag.

npx playwright test -–headed

Screenshot 2022 05 25 at 6.58.38 PM

Now that you have the tutorial in place, know that Playwright is supported by Browserstack which provides thousands of real devices where we can verify applications on real devices. A few advantages of Playwright are:

  • Easy Setup and Configuration
  • Multi-Browser Support
  • Multi-Language Support
  • Parallel Browser Testingomes in handy when multiple web pages have to be tested simultaneously.
  • Built-in Reporters: 
  • Typescript Support out of the box
  • CI/CD Integration Support
  • Debugging Tools Support

Using Browserstack Integration with Playwright we can integrate our Playwright tests and make automation testing easier through robust design patterns. Not only that, speed up your Playwright tests by 30x with parallel testing to expand your test and browser coverage without compromising on build times.

-----------------------------------------------------------------------------------------------------


 Mouse Clicks [Right Click, Double Click, Shift Click, Mouse Hover, Normal Click] in Playwright

test( 'mouse click events', async()=>{
     const browser:Browser =  await chromium.launch({headless: false, channel: 'chrome'});
     const page:page =await browser.newPage() ;



  test('Perform Double Click', async () => {
    await page.goto('https://demo.guru99.com/test/simple_context_menu.html');
    const doubleClickElement = page.getByText('Double Click Me To See Alert');
    await doubleClickElement.dblclick();
    console.log('Double click performed');
    await page.waitForTimeout(3000); // Consider replacing with explicit waits
  });

  test('Perform Right Click', async () => {

    const rightClickElement = page.getByText('right click me');

    await rightClickElement.click({ button: 'right' });

    console.log('Right click performed');

    await page.waitForTimeout(3000); // Consider replacing with explicit waits
  });

  test('Perform Shift + Click', async () => {

    await page.goto('https://the-internet.herokuapp.com/context_menu');

    const shiftClickElement = page.getByText('Example 1: Menu Element');

    await shiftClickElement.click({ modifiers: ['Shift'] });

    console.log('Shift + Click performed');

//mouser  hover:

await page.goto("https://www.spicejet.com/");

await page.getByText( 'Add-ons').first().hover();

await page.getByText( 'Visa Services').first().click();


    await page.waitForTimeout(3000); // Consider replacing with explicit waits

      });
});
    

test( 'Type  characters sequentially' ,  async()=>{
      
       const browser:Browser = await  chromium.launch({headless: false, channel: 'chrome'});
       const page.page = await  browser.newPage() ;

await  page.goto("https://www.flipkart.com/");

await  page.getByPlaceholder('Search for  products, Brands and More') .pressSequentially
("macbook", {delay: 500});

await  page.waitForTimeout(5000);

-----------------------------------------------------------



//await  new promise( ()  => ; //  prevents  your  script  from  exiting!

});

function  createAuthHeader(username:any, password:any){
      return  'Basic  ' + btoa(username+' : '+password) ;




------------------------------------------------------------------------------------------


Playwright with Javascript | How to Handle WebTable/Pagination Table


const { test, expect } = require('@playwright/test');

test('Validate table rows and columns', async ({ page }) => {
    // Navigate to the webpage
    await page.goto('https://testautomationpractice.blogspot.com/');

    // Define table selector
    const table = page.locator('#productTable');

    // Validate the number of columns
    const columns = table.locator('thead tr th');
    const columnCount = await columns.count();
    console.log(Number of columns: ${columnCount});
    expect(columnCount).toBe(4);

    // Validate the number of rows
    const rows = table.locator('tbody tr');
    const rowCount = await rows.count();
    console.log(Number of rows: ${rowCount});
    expect(rowCount).toBe(5);

    // Additional improvement: Log row and column details for debugging
    console.log('Column headers:');
    for (let i = 0; i < columnCount; i++) {
        console.log(- ${await columns.nth(i).textContent()});
    }
});

//2) select check box for product 4

const machedRow= rows.filter({
         has: page.locator('td'),
         hasText: 'product 4'
    })
    await machedRow.locator( 'input') .check()
*/


//3) select multiple products by  re-usable function

await selectProduct(rows, page, 'product 1')

await selectProduct(rows, page, 'product 3')

await selectProduct(rows, page, 'product 5')

//4) print all product details using loop
for(let i=0; i<await  rows. count(); i ++)
{
   const  row=rows.nth(i);
   const  tds=row. locator('td')

for(let  j=0  ; j<  await  tds.count() -1; j ++)
{
   console. log(await  tds.nth(j) . textContent())
  }
}
*/
//5) read  data from all the pages in the table

const  pages=await  page. locator( 'pagination li a ')
console. log('Number  of  pages  in  the  table:',  await  pages.count())

for(let  p=0  ; p< await  pages. count() ; P++)
{
     if(p>0)
     {
         await  page . nth(p) . click
       }    
  const  row=rows.nth(i);
   const  tds=row. locator('td')

for(let  j=0  ; j<  await  tds.count() -1; j ++)
{
   console. log(await  tds.nth(j) . textContent())
  }
}

        await page.waitForTimeout(5000)
})


function selectProduct(rows, page, name)
{
       const macheRow= rows.filter({
            has: page.locator( 'td'),
            hasText:  name
      })
      await machedRow. locator( 'input').check()
}



 const { test, expect } = require('@playwright/test');

test("Date Picker", async ({ page }) => {
    // Navigate to the webpage
    await page.goto("https://testautomationpractice.blogspot.com/");

    // Desired date to select
    const targetYear = "2024";
    const targetMonth = "April";
    const targetDate = "20";

    // Open the date picker
    await page.click("#datepicker");

    // Navigate the calendar to the target month and year
    while (true) {
        // Get the displayed month and year
        const displayedMonthYear = await page.locator(".ui-datepicker-title").textContent();
        
        if (displayedMonthYear.includes(targetYear) && displayedMonthYear.includes(targetMonth)) {
            break; // Exit loop if target month and year are displayed
        }
        
        // Click the "Next" button to navigate the calendar
        await page.click(".ui-datepicker-next");
    }

    // Select the target date
    await page.locator(.ui-datepicker-calendar td >> text=${targetDate}).click();

    // Validate the selected date in the input field
    const selectedDate = await page.locator("#datepicker").inputValue();
    console.log(Selected date: ${selectedDate});
    expect(selectedDate).toBe(${targetMonth.slice(0, 3)}/${targetDate}/${targetYear});
});