Web Automation
Testing Using
Playwright
End-to-end, API, accessibility, and
visual testing using Playwright
Kailash Pathak
www.bpbonline.com
First Edition 2025
Copyright © BPB Publications, India
ISBN: 978-93-65898-002
All Rights Reserved. No part of this publication may be reproduced, distributed or transmitted in any
form or by any means or stored in a database or retrieval system, without the prior written permission
of the publisher with the exception to the program listings which may be entered, stored and executed
in a computer system, but they can not be reproduced by the means of publication, photocopy,
recording, or by any electronic and mechanical means.
LIMITS OF LIABILITY AND DISCLAIMER OF WARRANTY
The information contained in this book is true to correct and the best of author’s and publisher’s
knowledge. The author has made every effort to ensure the accuracy of these publications, but
publisher cannot be held responsible for any loss or damage arising from any information in this
book.
All trademarks referred to in the book are acknowledged as properties of their respective owners but
BPB Publications cannot guarantee the accuracy of this information.
www.bpbonline.com
Dedicated to
My father, my spouse
My daughter Garvita and
My son Praneel
About the Author
Kailash Pathak is currently working as a Senior QA lead manager at
3pillar. He has decades of experience in the tech industry, and his expertise
revolves around QA engineering and automation. He is PMI-ACP®, ITIL®,
PRINCE2 Practitioner®, ISTQB and AWS (CFL) certified.
Throughout his career, he has developed a deep expertise in quality
assurance and automation, leading QA teams to ensure adherence to quality
standards in software products and implementing effective testing
strategies.
In addition to his core work, he is actively involved in giving conferences,
webinars, and conducting workshops by sharing his expertise with others in
the field. He actively participates in knowledge-sharing through platforms
like LinkedIn, X, and medium to help the testing community.
About the Reviewer
As a passionate test automation engineer, Dániel specializes in both API
and UI testing, with a particular fondness for web UI automation. His
expertise lies in crafting robust, efficient test suites that ensure software
quality and reliability. While he is well-versed in API testing
methodologies, he finds the dynamic nature of web UI testing especially
engaging. He is always on the lookout for cutting-edge tools and
frameworks to enhance my automation arsenal. His approach combines
thorough test coverage with a keen eye for user experience. He thrives on
challenges and continuously seeks to expand his skill set and embrace new
technologies to stay ahead in the ever-evolving field of test automation. His
goal is to deliver high-quality, maintainable test solutions that contribute to
seamless software development cycles.
Acknowledgement
I want to extend my deepest gratitude to everyone who contributed to
making this book possible. Special thanks to my family for their patience
and support, my colleagues and mentors for their insights, and my readers
for inspiring me to share my knowledge. I would also like to thank the
reviewers, technical experts, and editors who provided valuable feedback
and guided me along the way.
This book would not be possible without the incredible support of the open-
source community and the Playwright team. Their dedication and
innovation have been a constant inspiration, and I am profoundly grateful
for the tools and resources they provide to make development and testing
accessible to everyone.
Heartfelt thanks to BPB Publications for their expertise and support in
making this book possible. They were very helpful, especially in explaining
the many processes that were involved in publishing the book.
Lastly, I would like to convey my sincere gratitude to readers interested in
this book. I would also like to thank all those people who helped me during
the process of writing and inspired me to continue.
Preface
When we adopt a new tool to automate any application, it is important that
we know about the tool in depth. Some of the following questions come
into mind when planning to use any new tool:
Is this tool user-friendly?
Does this tool meet my requirements?
What is the learning curve? What new concepts/ skills do I need to
learn?
How does this tool differ from others?
Cost and License
This book, Web Automation Testing Using Playwright answers all these
questions. Comprising of thirteen insightful chapters, this book covers
everything required for a beginner to an experienced person. The book's
journey starts with an explanation of automation testing and its benefits, an
overview of Playwright (a popular end-to-end automation testing
framework developed by Microsoft), how it differs from other tools, how to
set it up across the platform, and write the first script using Playwright.
As the book progresses, it explains how to handle various web elements
with detailed examples such as drag and drop, download files, various types
of alerts, tabs, iframe, and Shadow DOM using Playwright. This book will
also cover in detail various in-built tools that Playwright provides, like
Codegen, Playwright inspector, Trace view, and UI mode.
This book provides insights into various reports that Playwright supports
and how to integrate with third-party reports. It looks into assertion,
annotations, various in-build locators, and hooks in Playwright with
detailed examples of each. It also discusses how to implement Page Object
Model (POM) design pattern, Playwright Cucumber/BDD integration, API
testing, Mocking Data, Authentication, Session storage, accessibility
testing, and visual testing with Playwright.
Finally, this book will explain how to integrate Playwright tests with CI/CD
and run Playwright tests in the CI/CD pipeline in serial and parallel mode,
leveraging Artificial intelligence (AI) in test automation, script generation,
and debugging Playwright test.
Chapter 1: Introduction to Playwright - This chapter starts with an
understanding of automation testing and how it is an integral part of modern
software development. You will see the various benefits of automation
testing. Next, you will look into the overview of Playwright. This chapter
also delves into the comparison between Playwright vs. Selenium and
Playwright vs. Cypress. It will help readers understand various features of
the tool and decide why Playwright is the right choice.
Chapter 2: Getting Started with Playwright - This chapter starts with an
understanding of how to install Playwright and set up our development
environment across different platforms, as well as the basic architecture of
Playwright. We will take a comprehensive overview of the various features
of Playwright. Creating the framework with creating the first script using
Playwright. Finally, you will see how cross-browser browser testing can be
done using Playwright, along with practical examples. By the end of the
chapter, readers will have a good understanding of various features and how
to set up and create the basic script using Playwright.
Chapter 3: Locator Strategies and Handling Various Actions - In this
chapter, we will dig into the essential aspects of working with web elements
using Playwright. We will begin by understanding how to interact with
input boxes, buttons, checkboxes, and complex web elements like
dropdowns, menus, and radio buttons. We will cover handling multi-select
dropdowns and explore mouse drag-and-drop operations, which are vital for
tasks like reordering elements or arranging data on web pages. By the end
of the chapter, readers will get an understanding of how to handle web
elements and what should be our selector strategies for various web
elements.
Chapter 4: Handling Complex Elements - This chapter will start with
exploring more advanced web elements using Playwright. You will learn
how we can handle complex elements like upload and download files,
iframe, JavaScript alerts, tabs, Pop up, and finally, we will explore Shadow
DOM and learn about how to interact with elements within the Shadow
DOM. By the end of the chapter, readers will get an understanding of how
to handle complex web elements and handling elements inside Shadow
DOM and iframe.
Chapter 5: Exploring Playwright Tools in Depth - This chapter provides
an in-depth exploration of how to generate the code using the Codegen
record and playback tool for interactions with web pages. Next, we will
look into how to leverage the Inspector to inspect and analyze and how test
cases can be executed in UI mode. Finally, explore the feature trace viewer,
which helps debug the script easily. By the end of the chapter, readers will
have a solid understanding of how we can record and playback the script,
learn about UI mode, and how we can debug the code using trace viewer.
Chapter 6: Reporter, Assertion, Annotations, and Hooks in Playwright
- This chapter starts by exploring various reports that Playwright supports.
Next, we'll cover types of assertions e.g., soft and hard assertions. We will
look into annotations, a handy feature to tag and categorize our tests. The
final segment of this chapter explores about the different type of hooks with
detail example. By the end of the chapter, readers will have a solid
understanding of the various type of reports ,about various type of assertion
and annotation, and finally learn about the various type of hooks.
Chapter 7: Page Object Model Pattern in Playwright - This chapter
focuses on Page Object Model (POM) design pattern, a design pattern
widely used in end-to-end testing with Playwright. We will start with why
Page Object Model (POM) then we will delve into how POM simplifies our
test code structure. We will explore the Pros and Cons of POM. Finally, we
will explore the hands-on approach to creating Page Objects for end-to-end
testing using Playwright. By the end of the chapter, the reader can learn
how to implement POM design patterns in his project.
Chapter 8: Playwright Cucumber/BDD Framework Integration - This
chapter provides an understanding of what cucumber is and how it
facilitates collaboration between technical and non-technical team
members, promoting a shared understanding of requirements. Learn about
how BDD scenarios can be written in a Given-When-Then format, making
them easy to understand by all stakeholders. We will explore cucumber
background, data tables, and how to integrate cucumber with Playwright.
Finally, you will see how to generate cucumber HTML Report.
Chapter 9: API Testing Using Playwright, Authentication, and Session
Storage - This chapter focuses on exploring the capabilities of Playwright
for API testing. We will walk through API, why API testing is required, and
how Playwright helps us to automate API’s . You will learn how to perform
GET, POST, PUT, and DELETE requests. Finally, you will learn how to
handle authentication and manage session data effectively. By the end of
this chapter, readers will gain a comprehensive understanding of how to
automate API using Playwright and about handling authentication, and
session storage.
Chapter 10: Accessibility Testing with Playwright - This chapter begins
with an introduction to accessibility testing, what exactly accessibility
testing is, and how it plays a key role in ensuring that web applications are
accessible by all individuals, including disabled persons. In the next section,
you will read about the Web Content Accessibility Guidelines (WCAG).
Finally, you will learn how we can automate accessibility testing with
Playwright. By the end of the chapter, readers will have a solid
understanding of how we can automate accessibility testing using
Playwright.
Chapter 11: Visual Testing with Playwright - In this chapter, we will start
with what is visual testing and what are the various benefits of visual
testing. Furthermore, we will look into how visual testing is effective in
catching visual discrepancies and regressions that might be missed in
regular functional tests. By the end of the chapter, readers will have a solid
understanding of how to automate visual testing using Playwright.
Chapter 12: Integrate Playwright Tests with CI/CD and Run Tests in
Parallel - This chapter begins with the introduction of CI/CD and how
CI/CD benefits in facilitating rapid and reliable code delivery. Next, we will
focus on GitHub Actions, a powerful CI/CD tool provided by GitHub
Action. To further speed up the testing process, we explore the concept of
parallel test execution. By leveraging GitHub Actions' capabilities, you will
learn how to distribute tests across multiple virtual environments
simultaneously, significantly reducing the overall test execution time. By
the end of the chapter, readers will have a solid understanding of how to set
up the CI/CD pipeline for their project, execute the test case in parallel
mode, and how to use the concept of sharding to extend more parallelism in
CI/CD pipeline.
Chapter 13: Using AI with Playwright for E2E Testing - This chapter
provides an introduction to Artificial intelligence (AI) in test automation.
You will learn about various AI automation tools that we have in the market
and learn about Generative AI. Next, you will learn about how we can
leverage ChatGPT to create test scripts in Playwright. Additionally, we will
learn how to use ChatGPT to create and debug scripts. Furthermore, we will
explain how end-to-end scripts can be generated with the help of Generative
AI tools. Finally, you will see the limitation of Generative AI tools in the
context of ChatGPT. By the end of the chapter, readers will have an
understanding of how to leverage Generative AI tools in automation testing.
Code Bundle and Coloured Images
Please follow the link to download the
Code Bundle and the Coloured Images of the book:
https://rebrand.ly/24de24
The code bundle for the book is also hosted on GitHub at
https://github.com/bpbpublications/Web-Automation-Testing-Using-
Playwright. In case there’s an update to the code, it will be updated on the
existing GitHub repository.
We have code bundles from our rich catalogue of books and videos
available at https://github.com/bpbpublications. Check them out!
Errata
We take immense pride in our work at BPB Publications and follow best
practices to ensure the accuracy of our content to provide with an indulging
reading experience to our subscribers. Our readers are our mirrors, and we
use their inputs to reflect and improve upon human errors, if any, that may
have occurred during the publishing processes involved. To let us maintain
the quality and help us reach out to any readers who might be having
difficulties due to any unforeseen errors, please write to us at :
errata@bpbonline.com
Your support, suggestions and feedbacks are highly appreciated by the BPB
Publications’ Family.
Did you know that BPB offers eBook versions of every book published, with PDF and ePub files
available? You can upgrade to the eBook version at www.bpbonline.com and as a print book
customer, you are entitled to a discount on the eBook copy. Get in touch with us at :
business@bpbonline.com for more details.
At www.bpbonline.com, you can also read a collection of free technical articles, sign up for a
range of free newsletters, and receive exclusive discounts and offers on BPB books and eBooks.
Piracy
If you come across any illegal copies of our works in any form on the internet, we would be
grateful if you would provide us with the location address or website name. Please contact us at
business@bpbonline.com with a link to the material.
If you are interested in becoming an author
If there is a topic that you have expertise in, and you are interested in either writing or
contributing to a book, please visit www.bpbonline.com. We have worked with thousands of
developers and tech professionals, just like you, to help them share their insights with the global
tech community. You can make a general application, apply for a specific hot topic that we are
recruiting an author for, or submit your own idea.
Reviews
Please leave a review. Once you have read and used this book, why not leave a review on the site
that you purchased it from? Potential readers can then see and use your unbiased opinion to make
purchase decisions. We at BPB can understand what you think about our products, and our
authors can see your feedback on their book. Thank you!
For more information about BPB, please visit www.bpbonline.com.
Join our book’s Discord space
Join the book’s Discord Workspace for Latest updates, Offers, Tech
happenings around the world, New Release and Sessions with the Authors:
https://discord.bpbonline.com
Table of Contents
1. Introduction to Playwright
Introduction
Structure
Objectives
Understanding automation testing
Manual testing
Record and playback tools
Script-based tools
Framework based tools
AI/ML-based tools
Journey into the future
Testim
Applitools
Mabl
Functionize
testRigor
TestCraft
Benefits of automation testing
Overview of Playwright
Playwright vs. Selenium
Feature comparison
Playwright vs. Cypress
Feature comparison
Conclusion
Multiple choice questions
Answers
2. Getting Started with Playwright
Introduction
Structure
Objectives
Installing Playwright in a different environment
Install Playwright on Mac
Install Playwright in Window
Install Playwright using VS Code extension
Understanding the architecture of Playwright
Understanding why Playwright is faster than Selenium
Overview of Playwright's features
Creating Playwright project
Writing your first Playwright script
UI testing with Playwright and JavaScript
Different ways of running the script
Cross-browser testing with example
Importance of cross-browser testing
Cross-browser testing with desktop
Cross-browser testing with mobile viewports
Conclusion
Multiple choice questions
Answers
3. Locator Strategies and Handling Various Actions
Introduction
Structure
Objectives
Locator's strategies
Understanding what is locator
Locate element in Playwright using CSS, XPath selector
CSS selectors in Playwright
Example of CSS selectors
XPath selectors in Playwright
Example of XPath selectors
Built-in locators from Playwright
Locating elements by role
Locating elements by text
Locating elements by label
Locating elements by placeholder
Locating elements by AltText
Locating elements by title
Locating elements by Test-ID
Chaining selectors
Chaining with more complex selectors
Filtering locators
Filter by text
Filter by not having text
Filter by child/descendant
Best practices for locator strategies
Handling element input box, buttons, and checkboxes
Handling input boxes and buttons
Handling checkbox
Handling single checkbox
Handling multi-checkboxes
Handling element dropdowns, and multi-select dropdown
Handling single-select dropdowns
Handling multi-select dropdowns
Handling radio buttons, and mouse drag and drop
Handling radio buttons
Handling drag and drop
Conclusion
Multiple choice questions
Answers
4. Handling Complex Elements
Introduction
Structure
Objectives
Handling upload and download the file
Handling upload file
Upload single file
Upload multiple file
Handling download file
Single file download
Multiple file download
Handling iframe
Handling JavaScript alerts
Standard alert
Confirm alert
Prompt alert
Handling tabs and window pop-ups
Handling new tabs
Handling multi page using browser context
Handling window pop up model
Handling shadow DOM
Conclusion
Multiple choice questions
Answers
5. Exploring Playwright Tools in Depth
Introduction
Structure
Objectives
Understanding of record and playback script
About record and playback tools
Advantages of using record and playback tool
Disadvantages of using record and playback tool
Learn how to generate code in VS Code
Record the test
Record at cursor
Pick locator
Learn about test generator with Codegen
About test generator Codegen
Recording Script with Playwright Codegen
Steps for recording test in Playwright
Learn about Playwright Inspector and debug script
Running tests in UI Mode
Learn about Trace Viewer
Recording a trace locally
Recording a trace on CI
Conclusion
Multiple choice questions
Answers
6. Reporter, Assertion, Annotations, and Hooks in Playwright
Introduction
Structure
Objectives
Reporters in Playwright
Third-party reporter
Reasons to use Allure Report
Setting up Allure Report
Learn about assertions in Playwright
Need for assertions
About hard and soft assertions
Assertions in Playwright
Auto-retrying assertions
Soft assertions
Annotations in Playwright
Learn about hooks in Playwright
Conclusion
Multiple choice questions
Answers
7. Page Object Model Pattern in Playwright
Introduction
Structure
Objectives
Introduction to Page Object Model
Essential components of POM
Playwright and POM for test automation
Benefits of using POM
Implications of not using Page Object Model
Implementing Page Object Model with Playwright
Use case one
Use case two
Data-driven from external files to Playwright tests
Conclusion
Multiple choice questions
Answers
8. Playwright Cucumber/BDD Framework Integration
Introduction
Structure
Objectives
Introduction to behavior-driven development
BDD lifecycle
Benefit of using BDD
Introduction to Cucumber
Understanding features and scenarios
Understanding Cucumber hooks and tags
Add multiple tags in feature file
Understanding Cucumber Background, Data Tables
Integrating Cucumber with Playwright
Cucumber HTML reporter
Conclusion
Multiple choice questions
Answers
9. API Testing Using Playwright, Authentication, and Session Storage
Introduction
Structure
Objectives
About application programming interface
API workflow
Application programming interface testing
API layer in test automation pyramid
Various components of the API
Benefits of API testing
Playwright for API testing
Setup Playwright for API testing
GET request test with example
POST request test with example
PUT request test with example
API testing with authentication
Authentication and session storage
Mock the APIs
Mock API requests
Modify API responses
Conclusion
Multiple choice questions
Answers
10. Accessibility Testing with Playwright
Introduction
Structure
Objectives
About accessibility testing
Importance of accessibility testing
Web Content Accessibility Guidelines
Tools for accessibility testing
Browser extensions
Playwright for accessibility testing
Writing accessibility tests using Playwright
Scanning an entire page
Code walkthrough
Output
Scanning a specific part of a page
Output
Scanning a specific element of a page
Scanning with WCAG violations
Output
Excluding elements from a scan
Output
Disabling particular scan rules
Output
Conclusion
Multiple choice questions
Answers
11. Visual Testing with Playwright
Introduction
Structure
Objectives
About visual testing
Ways to perform visual testing
Tools to perform visual testing
Benefits of visual testing
Benefits of automating visual testing
Create visual tests using Playwright
Create visual tests for single page
Code walkthrough
Test execution
Create visual tests for full page
Code walkthrough
Test execution
Create visual tests with masking the element or object
Code walkthrough
Test execution
Create visual tests with maxDiffPixels
Code walkthrough
Create visual tests with difference in entered text
Conclusion
Multiple choice questions
Answers
12. Integrate Playwright Tests with CI/CD and Run Tests in Parallel
Introduction
Structure
Objectives
About CI/CD
Benefits of CI/CD
Popular CI/CD tools
Introduction to CI/CD tool GitHub Actions
Setting up basic GitHub Actions workflow
Running Playwright tests in GitHub Actions
Cross browser testing with Playwright
Benefits of using Playwright for cross-browser testing
Running test scripts on different browsers
Running test scripts on a particular browser
Running test scripts across browsers from VS Code
Test execution in serial and parallel mode
Running test scripts in serial mode
Running test scripts in parallel mode
Execute test scripts in serial parallel mode
Sharding tests between multiple machines
Sharding tests in Playwright with GitHub action
Execution result in CI/CD GitHub action
Conclusion
Multiple choice questions
Answers
13. Using AI with Playwright for E2E Testing
Introduction
Structure
Objectives
Artificial intelligence in test automation
Benefits of AI in test automation
AI automation tools
About generative AI
Leveraging ChatGPT to automate tests with Playwright
About prompts
ChatGPT prompts to generate the script
Leveraging ChatGPT to debug Playwright test
ChatGPT prompts to debug the script
E2E script generation with ChatGPT
Limitations with ChatGPT
Conclusion
Multiple choice questions
Answers
Index
CHAPTER 1
Introduction to Playwright
Introduction
In this chapter, we will lay the foundation for understanding automation
testing and why it has become an integral part of modern software
development practices. Next, we will look into the various benefits of
automation testing, outlining how it enhances testing efficiency, accelerates
development cycles, and improves overall software quality.
Next, we will get an overview of Playwright, a popular automation testing
framework developed by Microsoft. Furthermore, we will give a detailed
comparison between Playwright and the widely used Selenium and Cypress
frameworks.
Structure
This chapter covers the following topics:
Understanding automation testing
Benefits of automation testing
Overview of Playwright
Playwright vs. Selenium
Playwright vs. Cypress
Objectives
By the end of this chapter, you will be able to understand about automation
testing and why automation is an integral part of modern software
development practices. You will get an overview of Playwright and a
detailed comparison between Playwright and the widely used Selenium and
Cypress frameworks.
Understanding automation testing
Automation does not need to be our enemy. I think machines can make life easier for men, if
men do not let the machines dominate them.
-John F. Kennedy
The origin of automation testing can be traced back to the late 1990s and
early 2000s when software development was undergoing a rapid
transformation due to the emergence of new technologies, methodologies,
and paradigms. Automation testing is the process of using software tools to
execute predefined test cases, compare actual outcomes with expected
outcomes, and report the results. It aims to reduce the manual effort, time,
and cost involved in testing software products and improve the quality,
reliability, and consistency of software testing.
The growth of agile software development, which favored frequent and
Iterative software product delivery based on client input and shifting
requirements, was one of the major forces behind the rise of automation
testing. Agile software development required faster and more frequent
testing cycles, which posed a challenge for manual testing methods that
were often slow, tedious, and error-prone. Automation testing offered a
solution to this challenge by enabling testers to run tests more quickly,
efficiently, and accurately, as well as to perform regression testing, load
testing, performance testing, and other types of testing that were difficult or
impossible to do manually.
Another driver of automation testing was the increasing complexity and
diversity of software products, which involved multiple platforms,
languages, frameworks, and technologies. Software products also had to
meet higher standards of functionality, usability, security, and compatibility.
Automation testing helped testers cope with this complexity and diversity
by allowing them to test various aspects of software products across
different environments and scenarios, as well as to reuse test scripts and
data for different software versions and releases.
As automation testing evolved over time, new tools and technologies
emerged that offered more capabilities and flexibility for testers. Some of
the current tools for automation testing include Selenium, Appium, Ranorex,
Katalon Studio, TestComplete, Playwright, Cypress, and Robot Framework.
These tools support web, desktop, and mobile-based applications with
supporting various programming languages such as Java, Python, C#, Ruby,
and JavaScript. They also support different types of testing, such as
functional testing, BDD testing, UI testing, API testing, and TDD testing.
Automation testing has become an integral part of software development
and quality assurance in the modern era. It has enabled testers to deliver
high-quality software products faster and more efficiently. Automation
testing has also opened new opportunities and challenges for testers in
terms of skills development, career growth, and collaboration with other
stakeholders in the software development lifecycle.
The history of QA test automation is a story of adaptation and innovation.
From manual testing to sophisticated AI-driven techniques, it reflects the
ongoing quest to enhance software quality while keeping pace with the
ever-changing technology landscape. As technology advances, QA test
automation is poised to play an increasingly pivotal role in delivering
reliable, high-quality software products to users worldwide.
We will explore the history of test automation with different stages of QA
test automation, from the early days of manual testing to the current state of
AI/ML-based tools.
Manual testing
Before test automation, software testing was primarily a manual process.
Testers would execute test cases manually, step by step, to identify defects
and ensure the softwar’'s functionality and quality. While this approach
allowed for thorough testing, it was time-consuming, resource-intensive,
and prone to human errors.
Manual testing involves several key steps to ensure a software application
meets its requirements and is ready for deployment.
Here is a general outline of the manual testing process:
Requirement analysis: Understand the project requirements,
specifications, and user expectations to establish a clear testing
strategy.
Test planning: Create a detailed test plan outlining the scope,
objectives, resources, timelines, and testing techniques to be used.
Test case design: Develop detailed test cases that outline the steps,
input data, expected outcomes, and any preconditions for each test
scenario.
Test environment setup: Configure the testing environment, which
includes hardware, software, network settings, and any other
necessary configurations.
Test execution: Manually execute the test cases based on the test
plan. Testers interact with the software, input data, and follow the
steps outlined in the test cases.
Defect reporting: When defects or issues are encountered, testers
document them with detailed information, including steps to
reproduce the issue, screenshots, and logs.
Defect verification: After developers fix reported defects, testers
verify the fixes by retesting the affected areas to ensure the issues are
resolved.
Regression testing: Perform regression testing to ensure that new
changes or fixes have not introduced new defects or adversely
affected existing functionality.
The benefits of manual testing are as follows:
Exploratory testing: Human testers can apply creativity and
intuition to explore various aspects of the software, uncovering
unexpected issues.
Early detection: Human testers can detect issues that might not be
captured by automated tests, contributing to improved software
quality.
Usability assessment: Manual testing helps evaluate the user-
friendliness and overall user experience of the software.
Adaptable to change: It can quickly adapt to changes in
requirements or scenarios that might not be covered by automated
tests.
Complex scenarios: Manual testing is particularly effective for
testing complex scenarios that are challenging to automate.
The limitations of manual testing are as follows:
Time-consuming: Manual testing can be time-consuming, especially
when testing extensive and repetitive scenarios.
Human error: Human testers can make mistakes during test
execution, leading to inconsistent results.
Resource-intensive: Manual testing requires skilled testers, which
can be resource-intensive in terms of training and compensation.
Limited scalability: It can be challenging to scale manual testing to
handle large-scale projects or continuous integration processes.
Record and playback tools
The first stage of test automation emerged when graphical user interfaces
(GUIs) gained prominence. Testers may record user activities on the GUI
and replay them later as test cases These tools have several shortcomings
while being easy to use and not requiring programming experience. For
example, the recorded test cases are not especially resilient because any
change to the GUI could result in a failure. They were not maintainable
because they did not follow any structure or design principles. They were
also not scalable because they could only test one application at a time.
Here are some of the benefits of using record-and-playback tools:
They can automate repetitive tasks. By doing so, testers may have
more time to devote to testing tasks that are more challenging.
They can be applied to GUI testing. In the early stages of automation
testing, this was a significant advancement.
Record and playback tools are relatively easy to use. This makes
them a good choice for beginners in automation testing.
Here are some of the limitations of using record-and-playback tools:
They can only automate tasks that can be recorded and replayed. This
can limit their usefulness for complex tests.
They can be brittle. This means that they can break easily if the GUI
changes.
It can take a long time to maintain them. As a result, whenever the
GUI changes, so must the scripts.
Record and playback-based tools are a type of test automation tool that
allows testers to record their interactions with a software application and
then play them back to test the application automatically. This can be a
great way to automate simple tests that are performed repeatedly, such as
login, checkout, or search.
Here are some of the most popular record and playback-based tools:
Selenium IDE: Selenium IDE is a free and open-source record and
playback tool for web applications. It is simple to use and works with
many different browsers to record and replay tests.
Cypress: Cypress Studio feature allows users to record and playback
the script in JavaScript or TypeScript and run tests in a browser.
Cypress also provides features such as automatic waiting, real-time
reloads, time travel debugging, and screenshots/videos.
Playwright: Playwright lets you automate browser functions,
including scrolling, clicking, and typing. You can record and replay
your interactions with the website using Playwright's function. In
addition to this, you may alter the script to include logic, arguments,
or assertions. Playwright supports recording and playback for
Chromium, Firefox, and WebKit browsers.
Katalon Studio: The Katalon Studio is a tool that can help non-
programmers create test cases and automate various tasks. The
playback and record modes of Katalon Studio allow non-
programmers to carry out automation and test cases effectively.
Besides being more powerful than a Selenium IDE, Katalon Studio
also comes with various features that allow non-programmers to
automate and test cases effectively. One of these is the Web Recorder
Utility, which can convert the actions the application takes into
executable code.
TestComplete: TestComplete is a record and playback tool that can
be used to automate a wide variety of software applications, including
web, desktop, and mobile applications. You can record user events,
including data entry, item selection, website navigation, form
submission, and mobile motions.
Script-based tools
When scripting languages like Perl, Python, Ruby, and JavaScript gained
popularity, the second stage of test automation evolved. Testers had more
freedom and control over the test logic and execution because script-based
tools let them develop test scripts in these languages. Additionally, these
tools support a variety of testing methodologies, including functional,
performance, security, and API testing.
However, they also had some shortcomings. For example, they necessitated
programming knowledge, which raised the learning curve and hindered
testers and developers from working together effectively. They lacked
uniformity and standardization as well because several testers may create
various scripts for the same functionality. Additionally, they failed to adhere
to any design principles because they had limited reusability and
maintainability.
Script-based tools have several benefits over manual testing and record-
and-playback tools.
Unlike human testers, script-based solutions can perform tests more
quickly, reliably, and accurately. Data-driven testing, parameterization,
looping, branching, and error handling are just a few of the complicated test
scenarios that script-based solutions can handle.
Software development tools like version control, issue tracking, and
continuous integration systems are just a few examples that can be
integrated with script-based technologies. Functional testing, regression
testing, load testing, stress testing, and security testing are among the many
testing kinds that script-based systems can offer.
Some of the advantages of script-based test automation tools are as follows:
They enabled testers to create customized and reusable test scripts
that could be run on different platforms and environments.
They improved test efficiency and accuracy by reducing human
errors and increasing test coverage.
They reduced test maintenance costs by allowing testers to update or
modify test scripts easily when software applications changed.
They facilitated collaboration and communication among testers and
developers by using common programming languages and
frameworks.
Some of the disadvantages of script-based test automation tools are as
follows:
They raised the learning curve and training costs by requiring testers
to possess programming abilities and an understanding of the
application being tested.
The scripts for the tests were more likely to contain mistakes and
flaws, which can have a negative impact on the accuracy and
dependability of the test results.
Sometimes, scripts were unable to evaluate some user interface
elements that required manual inspection, such as font size, color,
layout, and aesthetics.
Their performance or functionality may have been constrained by
their dependence on the accessibility and compatibility of testing
tools and frameworks.
Here are some of the most popular script-based test automation tools:
Selenium: Selenium is a popular open-source tool for automating
web applications. It supports a variety of programming languages,
including Java, Python, Ruby, C#, JavaScript, Perl, and PHP.
Selenium can automate a wide range of tasks, such as entering text,
clicking buttons, and submitting forms.
Playwright: Playwright is a new open-source tool for automating
web applications. It is written in JavaScript and is designed to be fast
and reliable. Playwright also supports a variety of features that make
it ideal for modern web applications, such as WebSockets,
WebDriver, and Node.js.
WebDriverIO: WebDriverIO is an open-source tool for automating
web applications. It is written in JavaScript and supports a variety of
features, such as WebSockets, WebDriver, and Node.js. WebDriverIO
is also fast and reliable, making it ideal for CI/CD environments.
TestCafe: TestCafe is an open-source tool for automating web
applications. It is written in JavaScript and supports a variety of
features, such as WebSockets, WebDriver, and Node.js. TestCafe is
also fast and reliable, making it ideal for CI/CD environments.
NightwatchJS: An open-source solution for automating web
applications is called NightwatchJS. It is developed in JavaScript and
supports several features, including WebDriver, Node.js, and
WebSockets. NightwatchJS is perfect for CI/CD settings since it is
quick and dependable.
Appium: An open-source tool for automating mobile apps. It works
with various platforms, including Windows Phone, iOS, and Android.
Different operations like typing text, clicking buttons, and scrolling
through lists can be easily automated with Appium.
Framework based tools
The third stage of test automation evolved as frameworks like TestNG,
Cucumber, and Robot Framework gained popularity. Framework-based
tools allowed testers to use predefined libraries and components to create
test cases using a common syntax and structure. These tools provided a
wide range of capabilities, including reporting, cross-browser testing, data-
driven testing, keyword-driven testing, and behavior-driven testing.
Additionally, they enabled integration with additional tools and platforms,
such as cloud services, Git, and Jenkins. The key advantages of these tools
were that they increased test automation's scalability, reliability,
maintainability, and reusability. They faced some difficulties, though. For
example, their use by non-technical testers was constrained by the degree to
which they still required programming knowledge. The setup and
configuration of the test environment and infrastructure also took a lot of
time and effort to set up and configure the infrastructure and test
environment.
Framework-based testing tools are software development tools that provide
a structure and set of functions for automating software tests. They offer
several advantages over ad hoc testing methods, which include the
following:
Improved effectiveness and output: Framework-based testing
solutions can assist in automating repetitive operations, leaving
testers to concentrate on more intricate and strategic testing duties.
This has the potential to increase production and efficiency
significantly.
Increased test coverage: Framework-based testing tools can help to
make sure that all components of the software, including complicated
functionality and edge cases, are tested. This can lower the likelihood
of faults and increase the quality of the software.
Increased testability: Framework-based testing tools can increase
the testability of software by offering a standardized and clear API for
dealing with it. As a result, writing and maintaining test scripts may
become simpler.
However, framework-based testing tools also have some disadvantages,
which include the following:
Initial setup and learning curve: Setting up and learning to use
framework-based testing tools can be challenging. For small teams or
organizations with limited resources, this may be a barrier to
admission.
Maintenance costs: Framework-based testing tools must be regularly
maintained to keep them current with the product being tested. For
teams working on several projects, this may require a sizable time
commitment.
Lack of flexibility: Framework-based testing solutions may be rigid
and challenging to modify. For teams who must modify their testing
strategy to meet the unique requirements of a project, this can be an
issue.
Here are some of the most popular framework-based tools:
TestNG: TestNG is a Java-based framework that was released in
2004. It is the most popular framework-based tool, which offers a
variety of features, including test configuration, data
parameterization, and test grouping.
Cucumber: Cucumber is a Behavior Driven Development (BDD)
framework that was released in 2008. It is designed to make test
automation more readable and understandable by non-technical
stakeholders.
RSpec: RSpec is a BDD framework for Ruby. It is a well-liked
option for testing Ruby applications for its simplicity, adaptability,
and flexibility. RSpec makes it simple to develop tests that are clear
to both skilled and non-technical users by leveraging a natural
language syntax.
Robot framework: Robot framework is an open-source, generic test
automation framework designed to simplify and streamline the
process of creating automated tests for software applications. It
provides an easily understandable, keyword-driven syntax that allows
technical and non-technical users to write and maintain tests
effectively.
Pytest: Pytest is a unit testing framework for Python. Pytest is fast
and easy to use, making it ideal for testing small, isolated code units.
Pytest also supports a variety of features, such as test coverage
reporting and code coverage analysis.
Jest: Jest is a JavaScript unit testing framework created by Facebook.
It is designed to be fast, easy to use, and extensible. Jest can be used
to test small, isolated units of code as well as larger applications.
AI/ML-based tools
The last stage of test automation that emerged in the 2010s is AI and ML-
based testing tools. Artificial intelligence (AI) and machine learning
(ML) are rapidly transforming the field of software testing. AI/ML can be
used to generate test cases that are more comprehensive and efficient than
those created manually. AI can learn from historical data and identify
potential problems that human testers might miss. With the help of AI, test
cases are created that cover all possible combinations of user inputs and
system states. This can help to ensure that software is tested thoroughly and
that all potential bugs are found.
AI/ML-based tools are also used to automate test cases, generate test data,
and identify defects in software. Automation of test case execution with
AI/ML can free up human testers to work on more innovative and strategic
endeavors. Additionally, it might help to increase testing efficiency and
precision. For example, automating monotonous operations like logging
into a system and entering data is possible using AI. As a result, human
testers may have more time to devote to more difficult activities, such as
creating test cases and analyzing test results.
AI/ML-based testing tools offer a number of pros and cons. Some of the
pros include the following:
Increased efficiency: AI/ML-based tools can automate many of the
manual tasks involved in testing, freeing up QA engineers to focus on
more strategic and creative tasks.
Reduced costs: AI/ML-based tools can help to reduce the cost of
testing by automating manual tasks and improving the accuracy of
testing.
Scalability: AI/ML-based tools can be scaled to test large and
complex applications.
Flexibility: AI/ML-based tools can be used to test a variety of
applications and platforms.
Some of the cons include the following:
High upfront cost: AI/ML-based tools can be expensive to purchase
and implement. This is because AI/ML-based tools require a
significant amount of data to train and operate. The data can be
difficult and expensive to collect.
Data requirements: AI/ML-based tools require a large amount of
data to train and operate. This data can be difficult and expensive to
collect. For example, AI/ML-based tools may require data from
previous releases of the software under test, as well as data from
similar applications.
Complexity: AI/ML-based tools can be complex to use and maintain.
This can require specialized skills and training. For example, AI/ML-
based tools may require the user to have knowledge of ML
algorithms and statistical analysis.
Susceptibility to bias: If AI/ML-based systems are trained on biased
data, they may be subject to bias. As an outcome, the test cases that
the tools produce may not be accurate representations of the real
world. An AI/ML-based tool, for example, may produce test cases
that are irrelevant to other user demographics if it has been trained on
data from a particular user population.
Journey into the future
AI/ML-based testing tools are revolutionizing the software testing
landscape. Software testing processes like test design, test execution, test
analysis, test reporting, and test maintenance can be automated, optimized,
and improved with the use of AI/ML-based testing solutions. The
effectiveness and accuracy of AI/ML-based testing tools can be improved
over time by using the feedback and data produced by the testing process as
a learning resource. As test automation evolves, we can expect to see more
integration of AI/ML with other technologies, such as natural language
processing (NLP), computer vision (CV), robotics, blockchain, or
quantum computing.
These technologies will enable testers to automate more aspects of testing,
such as requirements analysis, test orchestration, test data generation, defect
detection, root cause analysis (RCA), and test optimization. They will also
enable testers to shift left and right in the software development lifecycle
(SDLC) and to collaborate more effectively with developers, business
analysts, customers, and end-users.
Some of the most popular AI-powered tools for test automation and their
features are discussed further.
Testim
Testim is a cloud-based platform that uses ML to create, execute, and
maintain end-to-end tests for web and mobile applications. Testim allows
you to record user actions and generate test scripts in minutes, without
requiring any coding skills. Testim also learns from every execution and
automatically updates the test scripts to reflect changes in the application.
Some of the key features of the Testim tool are as follows:
AI-driven test creation: Testim uses AI to automatically generate
test scripts based on user interactions with the application. It analyzes
user actions and behavior to create initial test cases, reducing the
need for manual scripting.
Self-maintenance: One of Testim’s standout features is its ability to
automatically adapt test scripts when the application’s UI changes.
The AI algorithms recognize UI modifications and update the test
scripts accordingly, minimizing maintenance efforts.
Visual testing: Testim provides visual testing capabilities that allow
testers to validate the visual appearance of the application. It can
detect visual regressions, layout issues, and discrepancies in UI
elements across different browsers and devices.
Cross-browser and cross-device testing: The platform supports
testing across various browsers and devices, ensuring consistent
performance and appearance across different environments.
Parameterization and data-driven testing: Testim enables data-
driven testing by allowing testers to parameterize test steps with
different input values. This feature enhances test coverage by testing
multiple scenarios with varying data.
Smart locators: Testim uses AI to automatically identify the best
locators for UI elements, reducing the impact of changes in the
application’s structure on test scripts.
Test execution and analyzis: The platform allows users to execute
tests on demand or integrate them into continuous integration (CI)
pipelines. It provides detailed reports and insights to help teams
analyze test results and identify issues.
Applitools
Applitools is a visual testing platform that uses AI to compare screenshots
of your application across different browsers, devices and resolutions.
Applitools detects visual bugs, layout issues and UI inconsistencies that
might otherwise go unnoticed by traditional testing methods. Applitools
also provides a root cause analysis feature that helps you identify the source
of the visual defects and fix them faster.
Applitools integrates with various testing frameworks such as Selenium,
Cypress, Playwright, and TestCafe, as well as CI/CD tools such as Jenkins,
CircleCI, and Azure DevOps.
Here are some key features of the Applitools tool are as follows:
Visual testing: Applitools specializes in visual testing, which
involves comparing the visual appearance of application screens and
components across different versions, browsers, and devices. It helps
detect differences in layout, styling, and content.
Cross-browser and cross-device testing: Applitools allows you to
perform visual testing across various browsers, browser versions, and
devices. This helps ensure a consistent user experience across
different environments.
AI-powered visual validation: Applitools uses AI and machine
learning to analyze application screenshots and images intelligently.
It can identify visual changes that may not be immediately apparent
through manual inspection.
Accessibility testing: Applitools provides accessibility testing
features that help identify visual accessibility issues, ensuring your
application is usable by individuals with disabilities.
Applitools Eyes dashboard: The Applitools Eyes dashboard
provides a centralized view of your visual test results, including
comparisons, history, and analytics.
Ultrafast Grid: The Ultrafast Grid is Applitools’ cloud-based
infrastructure that enables parallel testing across various
environments, browsers, and viewports. This speeds up test execution
and provides comprehensive coverage.
Mabl
Mabl is an intelligent test automation platform that uses machine learning to
automate functional and regression testing for web applications. Mabl
enables you to create tests by simply using your application as a user would
and then automatically generates test scripts that can run on multiple
browsers and environments.
Mabl also monitors your application performance and user feedback and
alerts you of any issues or anomalies. Mabl offers a comprehensive
dashboard that displays test results, insights and recommendations, as well
as integrations with popular tools such as Slack, Jira and GitHub.
Some of the features of Mabl include the following:
AI-powered test automation: Mabl leverages AI/ML to
automatically identify and maintain UI elements, reducing the need
for manual updates to tests when the application's UI changes.
Codeless test automation: Mabl's platform is designed to enable
testing without the need for extensive coding knowledge. It offers a
visual interface for creating tests, making it accessible to non-
technical team members such as testers, product managers, and
designers.
Cross-browser testing: Mabl enables you to test your web
applications across various browsers and browser versions to ensure
consistent functionality and appearance for different users.
Visual testing: Mabl includes visual testing capabilities, allowing
you to detect visual regressions and UI inconsistencies between
different versions of your application. This can be particularly useful
for catching design and layout issues.
Self-healing tests: Mabl can automatically adjust tests based on
changes in the application's user interface, making them more
resilient to small UI changes that might otherwise break traditional
tests.
API testing: In addition to UI testing, Mabl supports API testing,
allowing you to validate the functionality and behavior of your
application's APIs.
Performance testing: Mabl offers some performance testing
capabilities, allowing you to simulate user load and gauge your
application's performance under different conditions.
Data-driven testing: Mabl supports data-driven testing, allowing you
to test your application with various inputs or datasets to ensure it
behaves correctly under different scenarios
Functionize
Functionize provides an AI-powered testing platform for software
applications. Functionize aims to improve software testing and quality
assurance by utilizing AI/ML technologies to automate testing processes
and enhance test coverage. Please note that there might have been
developments or changes since then.
Following are some of the key features associated with Functionize:
AI-powered testing: Functionize employs AI/ML algorithms to
create, execute, and maintain test cases. This AI-driven approach is
designed to make testing more efficient, adaptable, and
comprehensive by automatically generating and adjusting tests based
on application changes.
Natural language processing (NLP): One distinctive feature of
Functionize is its use of NLP to enable testers to write test scenarios
using plain language. Testers can describe scenarios in human-
readable sentences, and the platform translates these descriptions into
test scripts.
Self-healing tests: One of the challenges in automated testing is
maintaining tests as the application evolves. Functionize aims to
address this by utilizing AI to identify and adjust tests that are
impacted by changes in the application's user interface or
functionality. This results in more robust and self-healing test suites.
Cross-browser and cross-platform testing: Functionize might offer
features to test web applications on various browsers, devices, and
operating systems to ensure consistent functionality across different
environments.
Data-driven testing: Functionize supports data-driven testing,
allowing testers to use different sets of data to validate application
behavior.
testRigor
testRigor is a generative AI based test automation tool that helps QA teams
automate their testing processes. It is designed to be easy to use, even for
those with no prior experience in test automation. testRigor uses NLP to
convert plain English test specifications into executable code. This means
that you can write your tests in a way that is easy to understand, even for
non-technical users.
Following are some of the key features that testRigor was known for:
Plain English test creation: testRigor uses plain English to create
tests, so you can write tests without having to learn any programming
or automation skills. This makes it easy for anyone to get started with
test automation, even if they do not have a technical background.
Generative AI: testRigor uses generative AI to generate tests from
your plain English specifications automatically. This can save you a
lot of time and effort, and help you to increase your test coverage.
Supports a variety of testing types: testRigor supports a variety of
testing types, including web testing, mobile testing, API testing, and
visual testing. This allows you to test your application from end-to-
end.
No code/codeless: You can create and run automated tests without
having to write any code. This makes it easy for anyone to get started
with test automation, even if they do not have a technical
background.
Low-code: Low-code test automation tools allow you to write some
code, but they provide a lot of drag-and-drop functionality to make it
easier to create tests.
TestCraft
TestCraft is a codeless automated testing tool that utilizes AI/ML to assist in
creating, executing, and maintaining automated test cases for software
applications. It aims to simplify the process of automated testing by
providing a visual interface where testers can design test scenarios without
writing traditional code.
Following are some of the key features that TestCraft was known for:
Codeless automation: TestCraft is designed to enable testers, even
those without extensive programming knowledge, to create
automated tests without writing code. This is achieved through a
visual interface where users can drag and drop elements to create test
scenarios.
AI-powered element identification: TestCraft incorporates AI to aid
in identifying and maintaining the elements on the application's user
interface. This reduces the fragility of automated tests when the
application's UI changes.
Visual test building: TestCraft's visual test creation feature allows
you to create automated tests that verify the visual appearance of your
web applications. This feature uses a visual recorder to capture
screenshots of your application as you interact with it. The recorder
then generates code that can be used to automate the test.
Integrations: TestCraft may provide integrations with popular
software development and testing tools, such as continuous
integration/continuous deployment (CI/CD) pipelines, issue
tracking systems, and more.
Collaboration and sharing: TestCraft might offer features for
collaborative test case creation and sharing. Teams can work together
on test cases, share them, and collaborate on test execution and
analyzis.
Benefits of automation testing
Automation testing is a software testing approach that uses automated
scripts and tools to execute tests on software applications. It offers several
benefits as compared to manual testing, including increased efficiency,
faster feedback, repeatability, broader test coverage, and improved
accuracy.
Here is a detailed overview of these benefits with examples:
Efficiency: Automated tests can run faster than manual tests,
allowing quicker feedback on the application's functionality. This is
particularly beneficial in agile development environments where
rapid testing is essential.
For example, think of a web application that needs to be tested and
contains many forms. Automating the form submission and validation
process can save time because the automation tool can swiftly
complete the form fields, submit them, and check the results.
Repeatability: Automation is highly effective for performing
regression testing, ensuring that new changes have not introduced
regressions or unintended side effects in the existing functionality.
For example, let us say a user reports a bug in a certain software
program functionality. Once the bug has been fixed, the test script
that we have written is executed again. Thus, automation is highly
effective for performing the regression testing.
Early detection of defects: Automation testing can be integrated into
the CI/CD pipeline, allowing for early detection of defects as soon as
new code is committed. This prevents issues from propagating
through the development cycle.
For example, suppose you are developing an e-commerce website. By
automating tests for the checkout process, you can catch issues like
payment gateway integration problems early in the development
cycle, preventing the issues from reaching production.
Faster feedback: When automated tests are incorporated into the CI
pipelines, developers receive feedback right away after committing
new code. This facilitates early detection and resolution of problems
in the development process.
For example, automated unit tests can be run each time a developer
pushes code in a CI/CD pipeline. The developer can be informed if a
unit test fails because of the new code, allowing them to address the
problem before it moves further along in the development lifecycle.
Cost savings: While there is an initial investment in setting up
automation infrastructure and creating test scripts, the long-term
benefits include reduced manual testing efforts and improved
software quality. This leads to overall cost savings in the software
development lifecycle.
For example, over the course of multiple releases, the time and effort
saved by using automation can outweigh the initial investment.
Manual testing requires more resources as the application complexity
increases, while automated tests can be reused and scaled easily.
Comprehensive test coverage: Automation testing enables thorough
coverage of various test scenarios, including those that are time-
consuming or complex to execute manually. This leads to increased
confidence in the software's functionality.
For example, for a healthcare software system, automating tests to
simulate patient data entry, appointment scheduling, and prescription
processing ensures that all critical functionalities are rigorously
tested.
Data-driven testing: Automation testing supports data-driven testing,
where a single test script can be executed with different sets of input
data, uncovering defects and validating a range of scenarios.
For example, imagine testing is an e-learning platform. By
automating data-driven tests, you can verify user registration with
various combinations of valid and invalid email addresses,
passwords, and personal details.
Cross-platform and cross-browser testing: Automation testing can
be used to validate software across different platforms, devices, and
browsers, ensuring consistent functionality.
For example, a responsive web application needs to be tested across
various devices and screen sizes. Automation can facilitate this by
executing tests on emulated devices and real browsers.
Overview of Playwright
The Playwright is a Microsoft-developed open-source automation
framework which makes it easier to test and automate browsers for web
applications. Playwright was released in 2020 and gave programmers and
testers the ability to create scripts that would interact with web pages in
several browsers, including Chromium (which powers Google Chrome),
Mozilla Firefox, and WebKit (which runs Safari). It resolves many of the
issues and constraints that other automation tools, like Selenium,
experience, making it a potent choice for contemporary web development
and testing requirements.
With Playwright, developers can create scripts in various programming
languages, such as Java, JavaScript, TypeScript, Python, and C#, to perform
actions within a browser, including navigating to websites, interacting with
page elements like buttons and forms, and capturing screenshots.
Playwright supports both headless and headed modes. In headless mode, the
browser runs without a visible graphical user interface, making it suitable
for tasks like automated testing and web scraping, where visual feedback is
not required. In headed mode, developers can see the browser UI, which
can be useful for debugging and visually verifying the automation process.
Playwright was designed to eliminate flakiness in browser automation. It
addresses common challenges that developers often face in this domain,
such as timing issues and network-related inconsistencies. Playwright's
event-driven architecture allows it to wait for specific conditions before
proceeding with the automation flow. This ensures that the application
under test has reached the expected state before interactions take place. The
concept of browser contexts in Playwright ensures isolation between
different automation tasks. This prevents interference between different
tests or scenarios running within the same browser instance, contributing to
greater consistency and reliability.
Microsoft Playwright gained popularity for the following reasons:
Cross-browser and cross-platform compatibility: Playwright
allows developers to write browser automation scripts that work
consistently across multiple browsers (Chrome, Firefox, and WebKit)
as well as different operating systems (Windows, macOS, and Linux).
This cross-browser and cross-platform support was a significant
advantage over other automation frameworks.
Modern and powerful API: Playwright provides a modern and
powerful API for automating web browsers. Its API is designed to be
more intuitive and developer-friendly compared to older automation
frameworks like Selenium. It offers a wide range of capabilities for
interacting with web pages, handling network requests, and managing
browser contexts.
Fast and reliable: Playwright is known for its speed and reliability. It
leverages modern browser features to optimize automation tasks and
provides features like parallel execution and headless mode, allowing
for faster and more efficient testing and automation workflows.
Multiple language support: Playwright offers bindings for various
programming languages, including Java, JavaScript/TypeScript,
Python, and C#. This makes it accessible to developers from different
language backgrounds and enables them to use their preferred
programming language for automation tasks.
Evolving technology: Playwright is developed by Microsoft, and it is
actively maintained and updated. This means it benefits from ongoing
improvements and updates to keep up with changes in web
technologies and browser behavior.
Integration with popular testing frameworks: Playwright can be
easily integrated with popular testing frameworks like Jest, Mocha,
and more. This makes it a suitable choice for incorporating automated
testing into software development pipelines.
Community and support: Over time, Playwright has garnered a
supportive and active community of developers who contribute to its
development, share tips, and assist in forums, documentation, and
online communities.
Headless browser testing: Playwright supports headless mode,
allowing developers to run browser automation tasks without a
visible browser window. This is particularly useful for running tests
and automation in server environments or in scenarios where UI
interaction is not necessary.
Device emulation: Playwright enables you to emulate various
devices, such as smartphones, tablets, and different screen sizes,
allowing you to test responsive designs and mobile-specific
functionality.
Network intercept and mocking: You can intercept and modify
network requests using Playwright. This feature is useful for testing
scenarios involving AJAX requests, APIs, and network-related
behavior.
Page context isolation: The Playwright provides isolated execution
contexts for each browser page, enhancing security and preventing
cross-page interference.
Browser contexts: You can create multiple browser contexts within a
single browser instance. Each context maintains its state, such as
cookies and cache, enabling scenarios like separate sessions or user
accounts.
Video recording and screenshots: Playwright supports video
recording of browser sessions and capturing screenshots during
automation, which can be valuable for debugging and documentation.
Parallel execution: Playwright supports parallel execution of
browser instances, which can significantly speed up the execution of
automated tests or tasks.
Playwright vs. Selenium
In the world of software development and testing, automating web
interactions is crucial for ensuring efficient and reliable application
performance. Two popular tools, Playwright and Selenium, have emerged
as powerful options for automating web browsers. Both the tools facilitate
web testing and interaction, but they come with distinct features,
capabilities, and approaches. In this comparison, we will explore
Playwright and Selenium from the perspective of beginners and experience,
helping you understand their fundamental differences and choose the best
tool for your needs.
Feature comparison
Playwright and Selenium are both popular automation tools for browser
automation. However, there are several differences between them in terms
of features and capabilities. Following is a feature comparison between
Playwright and Selenium:
Feature Playwright Selenium
Ease of use Playwright's modern API is designed to While Selenium has improved over
be intuitive and developer friendly. Its time, its API might feel slightly more
syntax closely resembles human verbose and complex, especially for
interactions, making it easy for beginners. It might require a steeper
beginners to write and understand learning curve for those new to
automation scripts automation.
Language Playwright offers official bindings for Selenium provides bindings for a
bindings Java, JavaScript/TypeScript, Python, wider range of programming
and .NET, allowing developers to languages, including Java, Python,
choose the language they are most C#, Ruby, and more. This broad
Feature Playwright Selenium
comfortable with. This is advantageous language support makes Selenium
for teams with diverse programming suitable for projects with language-
language preferences. specific requirements.
Installation and Playwright's installation process is Selenium requires the installation of
setup more streamlined, as it bundles browser drivers for specific browsers
binaries and requires minimal setup. (Chrome, Firefox, etc.) and the
This simplifies initial setup for corresponding language bindings.
beginners. This process can be complex and
error prone.
Execution speed Playwright boasts improved speed and Selenium's performance can be
reliability. Its architecture is designed affected by various factors, including
to execute commands more efficiently, browser versions and WebDriver
leading to faster automation. implementations. It might suffer from
slower execution speeds in certain
scenarios.
Community Playwright has gained a strong Selenium has a well-established and
support community following since its mature community due to its long
introduction. Its active development history. The community has produced
and continuous improvements indicate extensive documentation, tutorials,
a growing community that contributes and resources.
to its features, bug fixes, and
documentation.
Cross-browser Playwright's ability to work with Selenium has historically been the
testing multiple browser engines out of the box go-to for cross-browser testing due to
simplifies cross-browser testing. It its widespread adoption. However,
provides consistent APIs for different managing different browser drivers
browsers, streamlining the process of and the associated complexities can
writing tests that run across various sometimes make cross-browser
browser environments. testing with Selenium more
challenging.
Features support Playwright matches Selenium's feature Selenium provides a comprehensive
set and introduces additional set of features, including support for
functionalities. It offers built-in support browser automation, user
for intercepting network requests, interactions, headless mode, and
emulating mobile devices, and taking integration with various testing
screenshots or videos of test runs. frameworks like JUnit and TestNG.
These features make Playwright an Its extensive ecosystem has led to the
attractive choice for scenarios requiring development of numerous third-party
advanced testing capabilities. tools and plugins.
Headless mode Playwright excels in headless mode Selenium supports headless mode,
support, offering consistent behavior allowing browser interactions
across different browser engines and without a visible UI. However,
facilitating integration into automated configuring headless mode can
Feature Playwright Selenium
workflows. sometimes be tricky.
Learning curve Playwright's unified API and modern Selenium's learning curve can vary
approach to automation can contribute depending on the programming
to a faster learning curve, especially for language and specific
developers familiar with modern web implementation you choose. While its
technologies. Its intuitive widespread use has led to a plethora
documentation and code samples make of resources, the complexity of some
it easier for newcomers to start. scenarios and the need for
workaround solutions can make the
initial learning process more
challenging.
Reliability and Playwright was designed with Selenium has evolved over the years
stability reliability in mind. It offers features to become more reliable, but it can
such as automatic waiting for page still encounter issues related to
elements, better handling of race timing and synchronization,
conditions, and improved error especially in dynamically changing
messages, which contribute to more web applications. Playwright's design
stable and predictable test results. incorporates lessons learned from
Selenium's shortcomings.
Table 1.1 : Playwright vs. Selenium
Playwright vs. Cypress
In the realm of modern web development, end-to-end testing frameworks
have become crucial for delivering high-quality web applications.
Playwright and Cypress are two prominent players in this space, both
catering to the needs of experienced developers looking for efficient and
reliable testing solutions.
In this comparison, we will explore Playwright and Cypress from the
perspective of beginners and experience, helping them make informed
decisions based on their specific project requirements.
Feature comparison
Playwright and Cypress are both popular tools used for browser automation
and end-to-end testing, but they have different features and approaches.
Here is a comparison of some key features between Playwright and
Cypress:
Feature Playwright Cypress
Installation and setup Setting up Playwright involves Cypress boasts a remarkably easy
installing the Playwright package setup process. Once installed, it
and its browser dependencies. provides a graphical interface for
While Playwright's setup is test management and execution. Its
relatively straightforward, it does unique architecture eliminates the
require additional configurations need for configuring browser
for different browsers. This can be drivers, making the setup process
a minor drawback when compared hassle-free.
to tools that offer more streamlined
setups.
Performance Playwright is renowned for its fast Cypress's execution speed is
execution speed. This can be a generally good, but it might not be
significant advantage when running as fast as Playwright in all scenarios.
tests on large applications or However, Cypress's emphasis on
complex scenarios. The efficient interactive testing and real-time
execution of test scripts contributes debugging can compensate for
to faster feedback during slightly slower execution times by
development and testing cycles. providing immediate insights into
test results and application behavior.
Browser support Playwright supports Chromium, Cypress supports most of the
Firefox, and WebKit browsers, browser including Chrome, Edge,
enabling cross-browser testing on Firefox, WebKit browsers.
different rendering engines.
Language support Supports multiple programming Support programming languages,
languages, including JavaScript, including JavaScript, TypeScript.
TypeScript, Java, Python, and C#.
This makes it versatile and
accessible for developers with
different language preferences.
Parallel execution Supports parallel execution out of Requires additional setup and tools
the box, which can significantly to achieve parallel execution, which
reduce test execution time. might involve more complexity.
Architecture Playwright offers a multi-browser, Cypress employs a different
multi-context, and multi-page architecture where it runs directly
automation approach. It allows within the browser. This architecture
users to work with multiple provides tight integration with the
browser instances simultaneously application being tested, allowing
and provides support for different for real-time control and immediate
browser contexts and pages. feedback.
Community and Being backed by Microsoft, Cypress has been around longer and
ecosystem Playwright is gaining popularity has a well-established community
and has a growing community. It and ecosystem. There is a large
has strong cross-browser collection of plugins and extensions
Feature Playwright Cypress
capabilities and a wider range of available.
use cases.
Debugging Playwright offers strong debugging Cypress provides an interactive Test
capabilities, allowing you to see Runner interface that allows you to
what is happening in the browser visualize and control the execution
during test execution. It has built-in of your tests. You can debug, see
tools for debugging and inspecting each step of your test as it is
elements. executed, along with the command
log, application preview, and any
errors or failures.
Documentation and Playwright's documentation is Cypress prides itself on having an
learning curve comprehensive and well- easy-to-use and beginner-friendly
maintained, but due to its broader API, making it relatively simple for
scope, it might have a steeper newcomers to get started quickly.
learning curve for beginners.
Table 1.2 : Playwright vs. Cypress
Conclusion
In this chapter, we discussed about automation testing and why it has
become an integral part of modern software development practices. We saw
the benefits of automation testing and various types of frameworks
available in the market. Finally, we saw the comparison between Playwright
vs. Selenium and Playwright vs. Cypress.
In the next chapter, we will start our journey with Playwright where you
will see how we do Playwright setup in various platform. You will also see
how to write the script in Playwright.
Multiple choice questions
1. What is Playwright?
a. A type of theater script
b. A web automation framework
c. A musical instrument
d. A painting technique
2. Which programming languages are officially supported by
Playwright?
a. Python, Java, and C#
b. Ruby, JavaScript, and Swift
c. PHP, Kotlin, and TypeScript
d. C++, Go, and Rust
3. What is the primary purpose of Playwright?
a. Game development
b. Mobile app development
c. Web scraping
d. Automated testing and browser automation
4. What is the purpose of Playwright's headless mode?
a. To simulate user interactions visually
b. To run tests without displaying the browser window
c. To speed up browser rendering
d. To enhance browser security
5. Playwright provides cross-browser support. What does this
mean?
a. Playwright only supports one specific browser
b. Playwright supports all browsers except Chrome
c. Playwright can automate multiple different browsers using the
same codebase
d. Playwright only works on mobile browsers
Answers
1. b.
2. a.
3. d.
4. b.
5. c.
CHAPTER 2
Getting Started with Playwright
Introduction
This chapter begins by installing Playwright and setting up our development
environment. Once our environment is set up, we will understand the basic
architecture of Playwright. We will take a comprehensive overview of the
Playwright’s impressive array of features. We will go step-by-step through setting
up a project structure, initializing Playwright, and configuring it to match our
needs. Finally, we will write our inaugural Playwright script across the different
browsers and introduce you to the concept of cross-browser testing by providing a
practical example.
Structure
This chapter covers the following topics:
Installing Playwright in different environments
Understanding the basic architecture of Playwright
Overview of Playwright's features
Creating Playwright project
Writing your first Playwright script
Cross browsers testing with example
Objectives
By the end of this chapter, you will understand the different ways of installing the
Playwright. You will know the basic architecture of Playwright and its features.
Next, you will see how to create a project in playwright and write the script.
Finally, you will learn cross-browser testing by taking a few examples.
Installing Playwright in a different environment
Installing Playwright on your system is a straightforward process that enables you
to start using its browser automation capabilities for various tasks. You may select
the programming language that best meets your needs because Playwright
supports a wide range of languages. All contemporary rendering engines,
including Chromium, WebKit, and Firefox, are supported by Playwright.
So, let us start with the installation part of Playwright. We have divided the
installation into two parts: one to install Playwright on Mac, window machine
using NPM, and in the other part, we will cover how to install Playwright using
the visual code extension.
Install Playwright on Mac
There are two ways to install Playwright on Mac:
Install using npm or yarn
Prerequisites: Before we dive into the installation process, make sure you
have the following prerequisites:
Node.js: Playwright is built on top of Node.js, so you need to have it
installed on your Mac. You can download it from the official Node.js
website with version 16+.
NPM: Node.js package manager (NPM) is usually included with
Node.js installation. You can verify if it is installed by running npm -v in
your terminal
VS Code is installed.
Here are the steps for initiating the installation process:
1. Open Terminal: To begin, open the Terminal application on your Mac.
Open the VS Code and under Terminal | New Terminal. Refer to Figure
2.1:
Figure 2.1: Open Terminal
2. Check the Node version: Make sure the Node version should be above 14.
Use the following command to check the Node version:
Command: node -v
3. Create a project directory: You can create a new directory for your
Playwright project or navigate to an existing project directory. To create a
new directory, use the following command:
Command: mkdir playwright_demo
4. In the VS Code IDE, open the newly created folder; one way of opening it
is, go to File | Open Folder. Refer to Figure 2.2:
Figure 2.2: Open Folder
5. In Figure 2.3, we can see that initially, we did not have any files inside the
created folder:
Figure 2.3: Open terminal
6. Type the following command and press Enter to install playwright:
Command:
npm init playwright@latest
Or,
yarn add playwright
7. Press the down arrow to select JavaScript. By default, TypeScript will be
selected. Press Enter after selecting JavaScript. Refer to Figure 2.4:
Figure 2.4: Select option JavaScript
8. Press Enter to select tests folder to put your end-to end tests. Refer to
Figure 2.5:
Figure 2.5: Select tests folder
9. Press y for GitHub basic setup. Refer to Figure 2.6:
Figure 2.6: Press y
10. Press Enter to start downloading browser binaries to your system. In Figure
2.7, you can see browser binaries for Chromium, Firefox, WebKit are
downloaded:
Figure 2.7: Download browsers
11. As soon as the installation is complete, the following success message will
be displayed. Provide a few instructions and generate a few files on your
system. Refer to Figure 2.8:
Figure 2.8: Playwright install successfully
Install Playwright in Window
Follow the steps to install Playwright on Windows:
Install using npm
Prerequisites: Before we dive into the installation process, make sure you
have the following prerequisites:
Node.js: Playwright is built on top of Node.js, so you need to have it
installed on your Window. You can download it from the official Node.js
website with version 16+.
NPM: NPM is usually included with Node.js installation. You can verify
if it is installed by running npm -v in your terminal.
VS Code is installed.
Let us start the installation process:
1. Open the command prompt and type cmd. Refer to Figure 2.9:
Figure 2.9: Command Prompt
Check npm and node should be installed. Check npm version using command
‘npm -v’.
The following command is used to check the node version in the system:
Check the node version using the command: node-v
2. Create a project folder and locate the folder. The following commands are
used to create a new director.
Command:
mkdir bpbNewPlaywright
cd bpbNewPlaywright
3. Type the following command in Figure 2.10 and press Enter to install
Playwright in the directory bpbPlaywright:
Command: npm init playwright@latest
4. Press the down arrow to select JavaScript. By default, TypeScript will be
selected. Press Enter after selecting JavaScript. Refer to Figure 2.10:
Figure 2.10: Select language
5. Press Enter to select tests folder to put your end-to end tests. Refer to
Figure 2.11:
Figure 2.11: Select tests folder
6. Press y for GitHub basic setup. Refer to Figure 2.12:
Figure 2.12: Install Playwright
7. Press Enter to start downloading browser binaries to your system. In Figure
2.13, we can see browser binaries for Chromium, Firefox, Webkit are
downloaded:
Figure 2.13: Install browsers
8. As soon as the installation is complete, the following success message will
be displayed. Provide a few instructions, and generate a few files on your
system. Refer to Figure 2.14:
Figure 2.14: Playwright install successfully
Install Playwright using VS Code extension
The second way of installing the Playwright is by installing the extension in VS
Code:
1. Create a project directory: Create a new directory for your Playwright
project or navigate to an existing project directory. The following
commands are used to create a new directory:
Command:
mkdir bpbNewPlaywright
cd bpbNewPlaywright
2. Navigate to the Extensions section and install it.
a. Navigate to the Extensions section and type Playwright. There are
multiple options available. Select the Playwright Test for VS Code by
Microsoft option.
b. Click on Install. Refer to Figure 2.15:
Figure 2.15: Install Extension
3. Type (Ctrl +Command +P) and type Install Playwright. Refer to Figure
2.16:
Figure 2.16: Install Playwright
4. Please ensure that you have checked the checkboxes for Chromium,
Firefox, and WebKit in your browser settings, as Playwright is compatible
with all of these browser engines. Additionally, make sure to select the
JavaScript programming language, and then proceed to click the OK
button. Refer to Figure 2.17:
Figure 2.17: Select browsers
5. We have successfully installed Playwright using the extension. Refer to
Figure 2.18:
Figure 2.18: Playwright successfully
After learning how to download Playwright on both Mac and Windows, the
following section will delve into Playwright's architectural framework.
Understanding the architecture of Playwright
Playwright is designed to automate interactions with web browsers, enabling
developers to write tests, scripts, and other automation tasks for web applications.
To understand how Playwright's architecture works, we will compare its work
with Selenium:
Ease of use:
Playwright: Playwright's modern API is designed to be intuitive and
developer-friendly. Its syntax closely resembles human interactions,
making it easy for beginners to write and understand automation scripts.
Selenium: While Selenium has improved over time, its API might feel
slightly more verbose and complex, especially for beginners. It might
require a steeper learning curve for those new to automation.
Language bindings:
Playwright: Playwright offers official bindings for Java,
JavaScript/TypeScript, Python, and .NET, allowing developers to choose
the language they are most comfortable with. This is advantageous for
teams with diverse programming language preferences.
Selenium: Selenium provides bindings for a wider range of
programming languages, including Java, Python, C#, Ruby, and more.
This broad language support makes Selenium suitable for projects with
language-specific requirements.
Installation and setup:
Playwright: Playwright's installation process is more streamlined, as it
bundles browser binaries and requires minimal setup. This simplifies the
initial setup for beginners.
Selenium: Selenium requires the installation of drivers for specific
browsers (Chrome, Firefox, etc.) and the corresponding language
bindings. This process can be complex and error-prone.
Execution speed:
Playwright: Playwright boasts improved speed and reliability. Its
architecture is designed to execute commands more efficiently, leading
to faster automation.
Selenium: Selenium's performance can be affected by various factors,
including browser versions and WebDriver implementations. It might
suffer from slower execution speeds in certain scenarios.
Features support:
Playwright: Playwright matches Selenium's feature set and introduces
additional functionalities. It offers built-in support for intercepting
network requests, emulating mobile devices, and taking screenshots or
videos of test runs. These features make Playwright an attractive choice
for scenarios requiring advanced testing capabilities.
Selenium: Selenium provides a comprehensive set of features, including
support for browser automation, user interactions, headless mode, and
integration with various testing frameworks like JUnit and TestNG. Its
extensive ecosystem has led to the development of numerous third-party
tools and plugins.
Playwright's architecture is designed to be more efficient and streamlined than
Selenium. Playwright bundles the browser binaries directly with its library, giving
it more direct control over browser behavior. Instead of relying on an external
WebDriver server, Playwright communicates directly with the browser instances.
Playwright uses a single connection to the browser and sends commands over that
connection as needed. This allows for faster and more efficient communication
between your test script and the browser. Rather than sending each interaction as a
separate HTTP request, Playwright can batch multiple actions together, reducing
overhead.
Playwrights use WebSocket-based protocol for communication between the client
(your test script) and the server (Playwright server). This architecture is one of the
key factors contributing to Playwright's efficiency and speed. When you start a
test using Playwright, your test script uses a WebSocket connection to transmit
commands to the Playwright server. These commands, which direct the server to
perform different tasks like visiting a URL, interacting with objects, or capturing
screenshots, are often transmitted in JSON format.
In Figure 2.19, we can see how Playwrights architecture works. We have broken
down the architecture into various key components, which are explained as
follows:
Figure 2.19: Playwright architecture
Here is a breakdown of the key components of Playwright architecture and the
communication process:
Client code: This is where you write your test scripts using Playwright's
API. The client-side code contains commands that define interactions with
the browser, such as navigating to pages, clicking elements, filling out
forms, etc.
WebSocket connection: Playwright operates on a client-server model
where the client-side code communicates with the server-side component
over a WebSocket connection. This connection is established when you start
your test and remains open throughout the test's execution.
Playwright server: The Playwright server is responsible for receiving
commands from the client, executing them in a browser instance
(Chromium, Firefox, or WebKit), and sending back responses. The server
manages the browser instances and orchestrates their behavior based on the
commands it receives.
JSON format: The commands you provide into your test code are
translated into JavaScript Object Notation (JSON) format before being
transmitted to the Playwright server. The lightweight JSON data exchange
format is simple to read and write by both people and machines.
Execution process: When you trigger a test, the client-side code is
executed. The commands you have written are serialized into JSON and
transmitted over the WebSocket connection to the Playwright server. The
server interprets these commands and performs corresponding actions
within the browser instance.
Response transmission: After executing a command, the Playwright server
sends back a response in JSON format through the WebSocket connection.
This response includes information about the success or failure of the
command, as well as any relevant data or outcomes.
Iterative process: The test execution continues iteratively, with the client
sending commands, the server executing them, and responses being sent
back to the client until all the commands in the test script have been
executed.
Understanding why Playwright is faster than Selenium
Understanding the kind of protocol utilized by Playwright and tools like Selenium
can help us understand why Playwright is faster. Selenium uses the HTTP
protocol to connect with the browser driver. Every action you perform when using
Selenium, including clicking an element, launching a browser, putting text into a
text box, and accessing a URL, is transformed into a specific HTTP request. The
WebDriver instance's running server processes these HTTP requests, which result
in JSON responses. These responses contain information about the success or
failure of the action, as well as any relevant data (like element properties, page
source, etc.).
After each command/request is executed, the connection between the client
(where your automation script is running) and the server (where the WebDriver
instance is running) is terminated. This is because WebDriver maintains a
stateless connection model. For the next command, a new connection is
established between the client and the server. This means that the client and server
are reconnected for each command, and the necessary information to recreate the
browser state.
You can see how HTTP requests work in the following figure:
Figure 2.20: HTTP request
In HTTP, each command is sent as a separate request, and the connection is re-
established for each command. Selenium commands are executed sequentially,
one after the other. This process might seem inefficient, but it ensures a clean slate
for each command and helps avoid potential interference between different
commands. However, it also means that the execution speed of Selenium scripts
can be affected, especially when dealing with many actions.
Playwright, on the other hand, uses a persistent WebSocket connection for
communication. This means that the client and the browser maintain a long-lived
connection, allowing commands and responses to be transmitted more quickly and
with less overhead. This is one of the reasons Playwright is often noted for its
improved speed and efficiency compared to other automation tools. In the
following figure, you can see how Web Socket Connection works:
Figure 2.21: Web Socket Connection
The persistent nature of WebSocket connections eliminates the frequent need to
establish new connections for individual actions or commands. This minimizes
latency and accelerates the execution of automation scripts, enhancing efficiency.
Furthermore, the connection remains intact until either the client or the browser
decides to close it, which can contribute to the efficiency of the overall process.
This architecture allows Playwright to offer faster and more responsive
automation, making it a popular choice for web testing and other browser
automation tasks.
Overview of Playwright's features
Playwright is an open-source automation testing framework that automates a wide
range of tasks.
Here are some of the key features of Playwright:
Rich interaction APIs: Playwright provides a wide range of APIs for
simulating user interactions, including clicking elements, typing text,
scrolling, hovering, and more. These APIs cover a variety of use cases for
testing and automating web applications.
Multi-browser support: Playwright supports Chromium (Google Chrome
and Microsoft Edge), Firefox, and WebKit (Safari) browsers. This allows
you to write scripts that work consistently across different browsers.
Headless and headful modes: Playwright supports both headless mode
(running the browser without a visible UI) and headful mode (with a visible
UI). This is useful for testing and debugging automation scripts.
Multiple language support: Playwright's API is available in TypeScript,
JavaScript, Python, Java, and .NET. This makes it a versatile tool that can
be used by developers of all programming languages.
Powerful selector engine: Playwright's selector engine allows you to locate
elements using various methods, including CSS selectors, XPath, and text
content. It also supports complex selectors for more precise targeting of
elements.
Parallel execution: Playwright supports parallel test execution, allowing
you to run multiple browser instances or tests simultaneously, improving
test suite efficiency.
Network interception: Playwright allows you to intercept and modify
network requests and responses, which is useful for mocking APIs, testing
network-related functionality, or handling specific scenarios.
Browser contexts: Playwright lets you create multiple browser contexts,
each with an isolated set of cookies, storage, and cache. This is useful for
scenarios like testing user authentication or handling different sessions.
Emulation: Playwright allows you to emulate various devices, viewports,
and network conditions. This is crucial for testing responsive design and
ensuring optimal performance on different devices.
File downloads and uploads: You can automate the handling of file
downloads and uploads through Playwright, which is essential for testing
file-related functionality.
Screenshots and videos: Playwright enables you to capture screenshots and
record videos of your automation sessions, which is helpful for
documentation, debugging, and sharing results.
Test framework integrations: Playwright can be integrated with popular
test frameworks like Jest, Mocha, and Jasmine, making it easy to
incorporate browser automation into your testing workflows.
Automatic waiting: Playwright automatically waits for navigation and
element visibility conditions by default, reducing the need for manual waits
and timeouts.
Trace recording and playback: Playwright's trace feature records browser
interactions, network requests, and more. You can later play back the trace
for analysis and debugging, aiding in identifying performance bottlenecks
and issues.
Integration with CI/CD tools: Playwright can be integrated with CI/CD
tools, such as Jenkins and Travis CI, to automate the execution of tests.
Built-in reporters: Playwright comes with built-in reporters that can be
used to generate test reports.
Creating Playwright project
In the previous section, we learned how to install Playwright on Mac and
Windows. Now, let us see how the folder structure looks after installing the
Playwright. Refer to Figure 2.22:
Figure 2.22: Folder structure
Understanding the folder structure is essential when organizing your Playwright
project. Here is a detailed breakdown of the typical Playwright folder structure:
Let us summarize the purpose of each folder and file we have mentioned:
e2e: The e2e (end-to-end) folder is dedicated to housing the end-to-end test
files that you create using Playwright's testing framework. These tests
simulate real user interactions with your application, covering scenarios that
span multiple components and workflows. By testing the application end-
to-end, you can verify that different parts of your application work together
seamlessly.
node_modules: The node_modules folder is automatically generated when
you install external packages and dependencies using package managers
like npm (Node Package Manager) or yarn. It holds all the required libraries
and code needed for your project to function correctly. You typically do not
need to manually create this folder; it's managed by the package manager.
tests: The tests folder is where you store your test files. These files might
be named with a .spec.js extension to indicate that they are test
specifications. This is where you write your Playwright tests, defining
different scenarios and expected behaviours of your application.
test-examples: This folder might contain additional test files as needed. It is
not a standard Playwright folder, but it seems like you're suggesting that
you can create this folder to store extra test files
.gitignore: The .gitignore file specifies which files and folders should be
excluded from version control when using Git. It helps prevent unnecessary
files from being added to your repository, such as build artifacts, temporary
files, and sensitive information. This ensures that only essential project files
are tracked in the repository.
package-lock.json: This file is automatically generated and updated by npm
when installing packages. It locks the versions of the installed dependencies
to ensure consistency across different installations of your project.
package.json: This file holds your project's metadata, configuration
settings, and a list of its dependencies. You define scripts, project name,
version, author, and other essential information here. You also manage your
project's dependencies by listing them here, so others can install them easily
using npm or yarn.
playwright.config.js: The playwright.config.js file is a configuration file
specific to Playwright. It allows you to set up various options and settings
for your Playwright tests. This includes configuring different browser
environments, specifying test environment variables, setting up custom
browser contexts, and more. This file helps tailor Playwright's behavior to
your project's requirements.
Writing your first Playwright script
In this section, you will see how to create a script in Playwright. Before writing
the script, let us first see why Playwright and JavaScript is the best combination
for writing the script.
There are several reasons why JavaScript with Playwright is an excellent choice
for browser automation:
Popularity and industry adoption: JavaScript is one of the most widely
used programming languages, both for web development and other tasks. Its
popularity means that many developers are already familiar with it, making
it easier to onboard new team members to your automation efforts.
Cross-platform compatibility: JavaScript is a cross-platform language,
and Playwright extends this advantage by supporting browser automation
on various operating systems. This allows you to run your scripts on
different platforms without modification.
Asynchronous nature: JavaScript's asynchronous nature aligns well with
browser automation tasks, which often involve waiting for elements to load,
making requests, and handling callbacks. Playwright's API heavily utilizes
async/await syntax, making it easier to write clean and readable code for
handling asynchronous operations.
Automation: JavaScript is indeed a flexible language that can be used to
write various types of automated tests, including unit tests, integration tests,
and end-to-end tests. Playwright complements this flexibility by providing a
high-level API that can be effectively used to write tests for different types
of web applications, including single-page applications (SPAs) and
progressive web applications (PWAs).
DOM manipulation: JavaScript's ability to manipulate the Document
Object Model (DOM) of a webpage allows you to interact with and modify
elements on a webpage. This is essential for automating tasks like clicking
buttons, filling out forms, and extracting data from web pages.
We have seen the benefit of using JavaScript with Playwright. Now, you will see
how we can create the script.
UI testing with Playwright and JavaScript
To perform UI testing with Playwright, you need to create a test script. This script
will tell Playwright what to do, such as navigate to a website, click on a button, or
enter text into a field. You can then run the test script to automate the testing
process.
Let us take an example of UI automation for the site: https://ecommerce-
playground.lambdatest.io/.
Let us consider the following use case to delve deeper into UI testing with
Playwright:
1. Open the site https://ecommerce-playground.lambdatest.io/ in your
browser.
2. Select Desktop from the Mega Menu.
3. Click on the Palm Treo Pro product.
4. Add item Palm Treo Pro into Cart.
5. Verify Palm Treo Pro product should be listed in the Cart.
6. Verify quantity of the added product should be “1”.
Here is the code snippet for the previously mentioned use case:
1. // @ts-check
2. const { test, expect } = require("@playwright/test");
3. test("Search the text 'Palm Treo Pro ", async ({ page
}) => {
4. await page.goto('https://ecommerce-
playground.lambdatest.io/');
5. await page.locator("span.title", {hasText: 'Mega Menu'
}).hover()
6. await page.locator("a[title=Desktop]").click();
7. await page.locator("div.carousel-item.active >
img[title='Palm Treo Pro']").click()
8. await page.locator("#container button[title='Add to
Cart']").click();
9. await page.locator("a.btn.btn-primary.btn-block",
{hasText: 'View Cart'}).click()
10. await expect(page.locator("td.text-left", {hasText:
'Palm Treo Pro'})).toBeVisible()
11. await expect(page.locator("div[class$='flex-nowrap'] >
input")).toHaveValue("1")
12. });
Code walkthrough:
Here is an explanation of each line of code:
Line #02 const {test, expect} = require("@playwright/test");
This line imports the test and expect functions from the @playwright/test library.
These functions are used to write and run Playwright tests.
Line #03 test("Search the text 'Palm Treo Pro", async ({page
}) => {
In this line, the code begins with defining a test function using the test keyword.
The test function takes two arguments: a description string and an asynchronous
callback function that receives an object with a page property.
Line #4 await page.goto('https://ecommerce-
playground.lambdatest.io/');
The page.goto method navigates the browser to the provided URL, in this case,
'https://ecommerce-playground.lambdatest.io/'.
Line #5 await page.locator("span.title", {hasText: 'Mega
Menu'}).hover();
Here, the code uses page locator to find an element with the <span> tag that has
the class “title” and contains the text 'Mega Menu'. The hover() method is then
called on the located element, simulating a hover action.
Line #6 await page.locator("a[title=Desktop]").click();
This line finds an <a> element with the attribute title equal to "Desktop" and
simulates a click action on it.
Line #7 await page.locator("div.carousel-item.active >
img[title='Palm Treo Pro']").click();
Here, an image element with the title attribute set to ‘Palm Treo Pro’ is located
within a <div> element with the class "carousel-item active". The code
simulates a click action on this image.
Line#8 await page.locator("#container button[title='Add to
Cart']").click();
This line locates a <button> element with the title attribute set to ‘Add to Cart’
within an element with the ID "container" and simulates a click action on it.
Line#9 await page.locator("a.btn.btn-primary.btn-block",
{hasText: 'View Cart'}).click();
The code finds an <a> element with the classes "btn btn-primary btn-block" and
containing the text 'View Cart'. It then simulates a click action on this element.
Line#10 await expect (page.locator("td.text-left", {hasText:
'Palm Treo Pro'})).toBeVisible();
This line uses the expect assertion function to validate that a <td> element with the
class "text-left" and containing the text 'Palm Treo Pro' is visible on the page.
Line#11 await expect(page.locator("div[class$='flex-nowrap']
> input")).toHaveValue("1");
Here, the code uses expect again to verify that an <input> element within a <div>
element with a class ending in "flex-nowrap" has its value set to "1".
Different ways of running the script
There are several ways to run test scripts in Playwright, and the choice of method
depends on your specific requirements and development environment.
Here are some different ways to run test scripts in Playwright:
Using the Playwright CLI in a headless browser:
Running all tests: npx playwright test
Running a single test file: npx playwright test homePage.spec.ts
Running tests in a headed browser:
npx playwright test landing-page.spec.ts --headed
Run tests in UI Mode:
npx playwright test --ui
Let us run the above code in different ways:
Run the command npx playwright test
Output: In Figure 2.23, you can see we have only one test case, and it
runs for all three configure projects for major browsers in
playwright.config.js:
Figure 2.23: Run test case
Run command npx playwright show-report
Output: HTML report is generated. In the following report shown in
Figure 2.24, you can see the test case pass in all tree browsers:
Figure 2.24: HTML report
Run the command npx playwright test searchProduct.spec.js --headed
Output: This command will open three browsers and run the test case in
parallel. Refer to Figure 2.25:
Figure 2.25: Test case run in parallel
Run the command npx playwright test --ui command: Playwright UI
mode is a feature that provides a graphical user interface (UI) for
managing and running your Playwright test scripts.
Output: You will get a list of all your test files when you first start UI
Mode. By clicking the triangle symbol on the sidebar, you may execute
each test. By holding your cursor over the name and selecting the
triangle next to it, you may also execute a single test file, a block of tests,
or a single test. Refer to Figure 2.26:
Figure 2.26: List of test case
In Figure 2.27, you can see the executed test case. The figure shows that all
commands are being executed line by line on the left side. At the bottom of the
Source tab, you can see the complete code, executed line by line. To view the
trace, click on one of the test names. Traces are displayed for each test that has
been executed.
Figure 2.27: Test case execution
We have seen how we can execute the test cases in different ways. In the next
section, you will see how test cases are executed in different browsers.
Cross-browser testing with example
Cross-browser testing is a crucial process in web development that involves
testing a website or web application to ensure it functions correctly and appears
consistently across different web browsers and browser versions. Since there are
numerous web browsers available, for example, Google Chrome, Mozilla Firefox,
Microsoft Edge, Safari, and others, and each browser may have multiple versions
in use, it is important to verify that your website works as intended for all users,
regardless of their choice of browser.
Although cross-browser testing may seem complicated, it is a fundamental idea
that every aspiring web developer should know. No matter which web browser
your consumers like, the most important thing is to ensure your website or online
application functions as intended.
Imagine web browsers as different cars and your website as a road. Like some
roads are smoother for certain cars, web pages can appear differently or even
break in some browsers. That is why we do cross-browser testing, to make sure
our website works smoothly on all these "cars" (browsers) and their different
"models" (versions).
Importance of cross-browser testing
Cross-browser testing is important for several reasons, primarily to ensure a
positive user experience, maintain website or web application functionality, and
reach a wider audience.
Here are some reasons why cross-browser testing is beneficial:
User experience consistency: Browsers like Internet Explorer, Edge,
Firefox, Chrome, and Safari render web pages differently. Cross-browser
testing guarantees that your website or application functions and presents
itself consistently across all browsers. This consistency is vital for providing
a seamless user experience and avoiding user frustration.
Wider audience reach: Users have their browser preferences, and they can
access your website or application using any of these. By conducting cross-
browser testing, you ensure that your product is accessible to a broader
audience, regardless of their browser choice or platform (Windows, macOS,
Linux).
Compatibility: Different browsers support different web standards and
technologies. Testing across multiple browsers ensures your application is
compatible with various HTML, CSS, and JavaScript specifications. This
can prevent issues related to non-standard or deprecated features.
Browser market share: It is crucial to consider the market share of various
browsers. By performing cross-browser testing, you can prioritize the
browsers your target market uses the most, ensuring that most of your
consumers have a favorable experience.
Bug detection: Cross-browser testing reveals browser-specific problems,
including poor layout, broken functionality, or slow performance. When
only one browser is used for testing, these problems might not be
noticeable, but they can cause significant problems for users on other
browsers.
Accessibility compliance: Cross-browser testing helps ensure your website
is accessible to all users, including those with disabilities. It helps identify
and rectify issues related to keyboard navigation, screen readers, and other
accessibility features.
In the upcoming section, we will explore the versatility of cross-browser testing
across desktop and mobile platforms using Playwright. Playwright empowers
developers to seamlessly conduct cross-browser testing on both desktop and
mobile environments.
Cross-browser testing with desktop
In Playwright, we can do cross-browser testing by providing browser detail in
playwright.config.js file. This file allows you to specify various settings and
configurations for your tests, including which browsers to use.
In playwright.config.js you can mention the name of the browsers on which we
want to execute the test case. In Figure 2.28, we have configured browsers
chromium, firefox, webkit (Line #20-Line#33). When we execute the test case, it
will run in three browsers.
Figure 2.28: Cross browser testing with Desktop
Execute the test case:
We have one test case, so let us execute it using the command npx playwright
test. Test case run in all three browsers with 3 workers. Refer to Figure 2.29:
Figure 2.29: Execute test cases
Run the command npx playwright show-report. It will open the HTML report, as
shown in Figure 2.30:
Figure 2.30: HTML report
Cross-browser testing with mobile viewports
In Playwright, you can do cross-browser testing in different mobile viewports. To
execute the test case on mobile, you must provide browser details in the
playwright.config.js file. This file allows you to specify various settings and
configurations for your tests, including which browsers to use.
In playwright.config.js, we can mention the name of the browsers on which we
want to execute the test case. In Figure 2.31, we have configured device Pixel 5,
iPhone 12 (Line #25-Line#34). When we execute the test case, it will run in three
browsers.
Figure 2.31: Cross browser testing with Mobile
To execute the same test case on mobile, we have to tweak the script. Following is
the script to run the test cases in mobile:
// @ts-check
const { test, expect } = require('@playwright/test');
test("Search the text 'Palm Treo Pro ", async ({ page }) =>
{
await page.goto('https://ecommerce-
playground.lambdatest.io/');
await page.locator("[title='Shop by
Category']").first().click();
await page.locator("span.title", {hasText: 'Desktops and
Monitors' }).click()
await page.locator("div.carousel-item.active >
img[title='Palm Treo Pro']").click()
await page.locator("[title='Add to Cart']").first().cl
ick();
await page.locator("a.btn.btn-primary.btn-block",{hasText:
'View Cart'}).click()
await expect(page.locator("td.text-left", {hasText: 'Palm
Treo Pro'})).toBeVisible()
await expect(page.locator("div[class$='flex-nowrap'] >
input")).toHaveValue("1")
});
Execute the test case:
We have one test case. Let us execute the test case using the command npx
playwright test. Test case run in both devices, Pixel 5, iPhone 12. Refer to
Figure 2.32:
Figure 2.32: Test case run in Mobile
In the above figure, you can see that both the test cases are passed in Pixel 5, and
iPhone 12.
Conclusion
In this chapter, we have covered various essential aspects of Playwright. We
started by exploring the different methods of installing Playwright and gained
insights into its basic architecture and features. We then delved into the process of
creating a project in Playwright and learned how to write scripts. To wrap up, we
discussed the practical application of Playwright by demonstrating cross-browser
testing through a few examples. By the end of this chapter, you should have a
solid foundation in Playwright and be well-equipped to use it for web automation
and testing purposes.
In the next chapter, we will discuss about locator strategies and how we can
handle different type of actions. We will discuss about built-in locators from
Playwright and how to handle element input box, buttons, checkboxes,
dropdowns, multi select dropdown, and radio buttons, and mouse drag and drop.
Multiple choice questions
1. What is the default browser when using Playwright with JavaScript?
a. Firefox
b. Safari
c. Edge
d. Chrome
2. How can you install Playwright for your project?
a. Through npm or yarn.
b. By downloading it from the official website.
c. Only through a manual build process.
d. By installing a browser extension.
3. What is the purpose of headless mode in Playwright?
a. To run tests faster.
b. To simulate user interaction in a visible browser window.
c. To hide the browser's graphical user interface.
d. To enable JavaScript debugging.
4. What command is used to install Playwright in your project?
a. npm install playwright
b. npm install browser-automation
c. npm install puppeteer
d. npm install selenium
5. What does the following Playwright code do?
await page.goto('https://example.com');
a. Takes a screenshot of the current webpage
b. Navigates to the https://example.com/ website
c. Clicks a button on the page
d. Closes the browser
Answers
1. d.
2. a.
3. d.
4. a.
5. b.
CHAPTER 3
Locator Strategies and Handling
Various Actions
Introduction
In this chapter, we will look into the essential aspects of working with web
elements using Playwright. We will begin by understanding the various locator
strategies available to target specific elements on a web page. We will explore the
power of locators using Cascading Style Sheets (CSS), XML Path Language
(XPath), and element IDs. We will understand the built-in locators from
Playwright. Explore how to interact with input boxes, buttons, and checkboxes.
Next, we will tackle more complex web elements like dropdowns, menus, and
radio buttons. We will cover handling multi-select dropdowns, where multiple
options can be chosen simultaneously. Additionally, we will explore mouse drag-
and-drop operations, which are vital for tasks like reordering elements or arranging
data on web pages.
Structure
This chapter covers the following topics:
Locator's strategies
Built-in locators from Playwright
Handling element input box, buttons, and checkboxes
Handling element dropdowns, and multi-select dropdown
Handling radio buttons, and mouse drag and drop
Objectives
By the end of this chapter, you will understand about various type locators. You
will know what are built-in locators in Playwright and how to handle various types
of elements.
Locator's strategies
Locator strategies are an integral part of quality assurance (QA) automation
because they enable automation scripts to locate and interact with specific
elements on a web page or within a software application. These elements can
include buttons, text fields, checkboxes, dropdowns, and other user interface
components. Accurately identifying and interacting with these elements is essential
for performing actions like clicking buttons, entering text, and validating content
during automated testing.
Before moving into depth of what should be our locator strategies, first you need to
understand what locators are.
Understanding what is locator
Locators are often used to identify and find objects inside a user interface (UI) or
a web page. These elements could include buttons, text fields, checkboxes, links,
and other UI components. Locators are essential for automated testing because
they allow testing tools and scripts to interact with and manipulate these elements
during test execution. To ensure the quality, reliability, and functionality of
software applications, automated testing has become an indispensable practice.
This is where locators step in, facilitating the efficient execution of automated tests
by allowing testing tools and scripts to seamlessly pinpoint and manipulate these
UI elements.
From e-commerce platforms to social media networks and mobile applications,
users navigate a multitude of interfaces daily. Ensuring these interfaces function as
intended is a complex task. However, locators provide a solution by enabling
automated testing. Imagine a scenario where an e-commerce website is enhanced
and updated. The development team also includes other features like a Buy Now
button and enhanced search functionality. Thorough testing is necessary to find any
potential bug, or consistency issues in the user interface prior to these
modifications being live. This is where locators prove their worth.
Locators act as virtual hands within the automated testing process, reaching into
the UI and manipulating elements like buttons. When an automated testing script
executes, it relies on locators to find these elements, ensuring that they are where
they should be and that they respond correctly to user interactions. For instance,
the Buy Now button must function seamlessly, directing users to the checkout
process when clicked. Locators guarantee that this button can be identified and
tested. Beyond buttons, locators extend their reach to a diverse array of UI
elements. Text fields, for example, are essential components for users to input data.
Locators enable automated tests to fill these fields with various inputs, ensuring
that the application processes and displays the data accurately.
Checkboxes are another crucial element often found in UIs. They allow users to
select or deselect options, such as agreeing to terms and conditions. Automated
testing scripts rely on locators to interact with checkboxes, confirming that they
can be selected and that they accurately reflect the user's choice. Moreover, links,
which connect different parts of a web page or application, play a pivotal role in
user navigation. Locators empower automated tests to validate that these links
work seamlessly, allowing users to move between pages or access additional
information without encountering errors.
Here are some common types of locators used in test automation:
XPath: XPath is a language used to navigate Extensible Markup
Language (XML) documents and is commonly used to locate elements in
web pages. It provides a powerful way to traverse the document structure
and find specific elements based on their attributes and positions.
CSS: CSS selector is another approach frequently used to find components
on a website. They are particularly beneficial when choosing items based on
their styles and characteristics.
ID locators: ID locators are used to locate elements by their unique HTML
ID attribute. This method is efficient because IDs should be unique within a
web page.
Name locators: Name locators locate elements by their name attribute,
which is not necessarily unique but can be useful for finding elements within
forms.
Class name locators: Class name locators locate elements based on their
HTML class attribute. This is useful for selecting multiple elements with the
same class.
Link text and partial link text: These locators are specific to locating
anchor (a) elements in web pages. You can locate links based on their visible
text.
Tag name locators: Tag name locators locate elements based on their
HTML tag name, such as div, input, button, etc.
Playwright offers various types of locators, each designed to address specific use
cases. One of the most common locator types is the CSS selector. CSS selectors
allow you to target elements based on their HTML attributes, such as class names,
IDs, or attributes like data-testid. This flexibility makes CSS selectors a popular
choice for locating elements. Another widely used locator type in Playwright is
XPath. XPath locators provide a powerful way to navigate the HTML document
structure, allowing you to select elements based on their position, attributes, or text
content. While XPath can be more complex than CSS selectors, it is invaluable
when dealing with intricate page structures. Playwright also has built-in locators
essential in automation testing.
Playwright also offers built-in locators essential for efficiently identifying and
interacting with elements on web pages during automated testing. These built-in
locators are instrumental in simplifying the process of test script creation and
enhancing the reliability of automated tests. Playwright offers essential built-in
locators for efficiently identifying and interacting with elements on web pages
during automated testing, which greatly simplifies test script creation and enhances
the reliability of automated tests. These recommended built-in locators include:
page.getByRole(): Used to locate elements by explicit and implicit
accessibility attributes.
page.getByText(): Used to locate elements by text content.
page.getByLabel(): Used to locate form controls by associated label text.
page.getByPlaceholder(): Used to locate inputs by their placeholder
attribute.
page.getByAltText(): Used to locate elements, typically images, by their
text alternative.
page.getByTitle(): Used to locate elements by their title attribute.
page.getByTestId(): Used to locate elements based on their data-testid
attribute (other attributes can be configured).
You will see a detailed explanation of the above built-in locators in the Built-in
locators from Playwright section.
Locate element in Playwright using CSS, XPath selector
Playwright is a versatile and robust web automation framework that empowers
developers with a variety of options to locate and interact with web elements. Two
prominent methods within Playwright for achieving this are CSS and XPath
selectors.
CSS selectors in Playwright
CSS selectors are patterns or rules used in CSS to define which HTML elements
should be styled in a particular way. In automation testing, CSS Selectors refer to
techniques or methods to locate and interact with elements on a web page. CSS
Selectors provide a robust way to select elements, allowing testers to target
elements based on various attributes, such as class, ID, tag name, and attributes.
CSS selectors in Playwright offer a straightforward and widely adopted approach
to target web elements. They enable developers to locate elements based on their
HTML tag, class, or ID attributes. With Playwright, developers can leverage CSS
selectors to locate and interact with web elements:
Example of CSS selectors
Let us take an example using the https://www.lambdatest.com/selenium-
playground/simple-form-demo for some of the CSS selectors:
ID Selector: The id attribute of an HTML element can be used to write a
CSS selector expression to locate and style that specific element. Following
is an example of ID selector:
await page.locator('#sum1’);
Let us break the example given above:
await: This keyword is used in JavaScript for handling asynchronous
operations. It indicates that the code should wait for the operation to
complete before proceeding.
page: This likely refers to a web page or browser page object.
.locator(): This is a method used to locate elements on a web page using
CSS selectors.
'#sum1': This is the CSS selector being passed to the locator() method.
It specifies that the automation tool should locate an HTML element with
the id attribute set to "sum1" on the web page.
Refer to the following figure:
Figure 3.1: CSS selector
Class selector: A class selector allows you to select and target elements with
a specific class attribute. In this case, .mr-10 is a class selector, which targets
elements with the CSS class "mr-10". See the following example for class
selector:
await page.locator('.mr-10’);
Refer to the following figure:
Figure 3.2: Class selector
Attribute selector: It enables the selection of elements by specifying an
attribute and its associated value. For instance, the command illustrates how
to select all text input elements: See the following example for attribute
selector:
await page.locator('input[type="text"]');
Refer to the following figure:
Figure 3.3: Attribute selector
Combining selectors: This technique merges various selectors to enhance
precision. For instance, the following command selects a button that
possesses both the class ".btn.btn-primary" and the text "Login". See the
example for combining selector:
await page.locator('.btn.btn-primary:has-
text("Login")');
Refer to the following figure:
Figure 3.4: Combining selector
Next, you will see an example of XPath selectors in Playwright.
XPath selectors in Playwright
XPath is a language used to navigate and query XML documents. It allows you to
locate elements within an XML document, making it particularly useful for
extracting data or performing various operations on XML-based data sources. In
XPath, everything in an XML document is considered a node. This includes
elements, attributes, text, and more. Nodes can be of different types, such as
element nodes, attribute nodes, text nodes, and more.
Playwright also supports XPath selectors, which offer a more flexible and powerful
way to locate elements on webpages. XPath selectors enable developers to traverse
the entire webpage structure and select elements based on various conditions.
Unlike CSS selectors, which primarily rely on element attributes, XPath selectors
can navigate hierarchies, filter elements by text content, and combine multiple
conditions for precise targeting.
Example of XPath selectors
Let us take an example using the https://www.lambdatest.com/selenium-
playground/simple-form-demo for some XPTH selectors.
Here, you will see we have used SelectorsHub Plugin, it can be used to write and
verify XPath, CSS selector, Playwright selectors, jQuery and JS Path.
SelectorsHub can also auto generate the unique #xpath, css Selector and all
possible selectors. SelectorsHub Plugin Can be downloaded from
https://selectorshub.com/selectorshub/.
In the following section, you will see various types of XPath selectors with
example:
XPath contains() function: XPath provides the contains() function,
which checks whether a given string is contained within another string. It is
particularly useful when selecting elements or nodes in an XML or HTML
document based on partial or full-text content. In the following example, you
can see the example of contains function where we are extracting the data
where id contain showInput:
Figure 3.5: Xpath contain selector
await
page.locator("//button[contains(@id,'showInput')]")
XPath text() function: The text() function in XPath is used to extract the
text content of elements in an XML document. It allows you to target the
text within an element rather than the element itself. Refer to the following
example of text function where we are extracting the text of button:
Figure 3.6: XPath text selector
The following command is used to locate a button element on a web page
using an XPath expression:
await page.locator("//button[contains(text(),'Get
Sum')]")
Starts-with function: The starts-with() function in XPath is used to check
if a string starts with a specific substring. It is often used in XPath
expressions to filter elements based on the starting characters of their text or
attribute value. In the following example, you can see the starts-with()
function where we are extracting the text of button which start with ‘Get’
and getting two values because we have two buttons with same start-with
value:
await page.locator("//button[starts-
with(text(),'Get')]")
Refer to the following figure:
Figure 3.7: Starts-with selector
Built-in locators from Playwright
Playwright offers the flexibility to employ the page.locator() method for element
location through CSS and XPath, although this approach is not advisable. We will
delve into the reasons behind this recommendation in the following section.
Using the page.locator() method in Playwright to locate elements through CSS
and XPath can indeed provide flexibility, as it allows you to choose between these
two popular methods for locating elements on a webpage. However, relying solely
on this approach may not be advisable for several reasons, which are as follows:
Brittleness: CSS and XPath locators can be brittle, meaning they can easily
break if the structure or layout of the webpage changes. Even small changes
in the HTML structure or attributes can render your locators ineffective.
Performance: CSS selectors and XPath expressions can vary in
performance depending on their complexity. Writing efficient selectors and
expressions is crucial for maintaining optimal test execution speed.
Readability and maintainability: While XPath and CSS selectors can be
powerful, they can also be verbose and difficult to read, especially for
complex selectors. This can make your code harder to understand and
maintain for both yourself and others.
Instead, Playwright furnishes seven built-in locators that are recommended and
entirely capable of locating elements on any web page or website. Let us now
proceed to examine these recommended built-in Playwright locators individually,
exploring their details one by one.
Locating elements by role
This locator is used to locate elements based on their explicit or implicit
accessibility attributes. It is particularly useful for finding elements like buttons,
links, form fields, and other interactive components based on their intended roles
in the user interface.
In the following example of getByRole(), we are verifying that the button ‘Get
Sum’ is visible and clickable:
await expect(page.getByRole('button', { name: 'Get Sum'
})).toBeVisible();
await page.getByRole('button', { name: 'Get Sum' }).click();
Refer to the following figure:
Figure 3.8: getByRole selector
Locating elements by text
This locator is used to find elements by their text content. You can use it to locate
elements that contain specific text, such as buttons with specific labels or
paragraphs with content.
In the following example of getByText(), we are verifying the text by two ways:
one by the text it contains and other verifies the exact match:
await expect(page.getByText('Single Input
Field')).toBeVisible();
await expect(page.getByText('Single Input Field', { exact:
true })).toBeVisible();
Refer to the following figure:
Figure 3.9: getByText selector
Locating elements by label
This locator is used to locate a form control (e.g., an input field) based on the
associated label's text. It is a convenient way to find input fields when you know
the label text that describes them.
In the following example of getByLabel(), we are entering the data in the email
and password field using the label of the fields:
await page.getByLabel('E-Mail
Address').fill('email@email.com');
await page.getByLabel('Password').fill('Password');
Refer to the following figure:
Figure 3.10: getByLabel selector
Locating elements by placeholder
This locator is used to find an input element based on its placeholder text.
Placeholder text is often used in input fields to provide hints or examples of the
expected input.
In the example of getByPlaceholder(), we are entering the data in two fields:
await page.getByPlaceholder('Please enter first
value').fill('1');
await page.getByPlaceholder('Please enter second
value').fill('2');
Refer to the following figure:
Figure 3.11: getByPlaceholder selector
Locating elements by AltText
This locator is typically used to locate elements and images by their text
alternative. The text alternative is an essential part of web accessibility, as it
provides a description of the image for users who cannot see it.
In the example of getByAlttext(), we are clicking on Logo of the site:
await page.getByAltText('Logo').first().click();
Refer to the following figure:
Figure 3.12: getByAlttext selector
Locating elements by title
This locator allows you to locate elements by their title attribute. The title
attribute can provide additional information about an element, making it useful for
targeting specific elements that have this attribute.
In the example of getByTitle(), we are clicking on Logo of the site:
await expect(page.getByTitle('Poco Electro')). click();
Refer to the following figure:
Figure 3.13: getByTitle selector
Locating elements by Test-ID
This locator is used to locate elements based on their data-testid attribute. This is
a custom attribute that developers often add to elements for testing purposes. It is a
flexible way to uniquely identify elements for testing, even if they do not have any
other distinctive attributes.
In the example of getByTestId (), we are entering the data in two text fields:
await page.getByTestId('sum1').fill('1');
await page.getByTestId('sum2').fill('4');
Refer to the following figure:
Figure 3.14: getByTestId selector
By default, page.getByTestId() will locate elements based on the data-testid
attribute, but you can configure it in your test config or by calling
selectors.setTestIdAttribute(). Refer to the following figure:
Figure 3.15: Config file
Chaining selectors
Chaining selectors in Playwright allows you to navigate through intricate DOM
hierarchies by combining multiple selectors. This technique is particularly useful
when dealing with nested elements, dynamic content, or when you need to filter
elements based on specific conditions.
In Playwright, you can chain selectors by combining them with the >> operator.
This operator allows you to specify a sequence of selectors, each narrowing down
the elements from the previous one.
Here is a simple example:
await page.click('div.parent >> div.child >> button');
In the above example:
div.parent selects all div elements with the class parent.
From those selected elements, div.child narrows it down to div elements
with the class child.
Finally, button selects the button elements within the previously filtered
div.child elements.
Chaining with more complex selectors
Let us consider a more complex scenario where you need to interact with a button
inside a list item that meets certain conditions.
Suppose you have the following HTML structure:
<ul class="item-list">
<li class="item" data-id="1">
<span class="name">Item 1</span>
<button class="edit-btn">Edit</button>
</li>
<li class="item" data-id="2">
<span class="name">Item 2</span>
<button class="edit-btn">Edit</button>
</li>
</ul>
To click the edit button for the list item with data-id="2", you can chain selectors
as follows:
await page.click('ul.item-list >> li.item[data-id="2"] >>
button.edit-btn');
Here is the breakdown of the above code:
ul.item-list selects the ul element with the class item-list.
li.item[data-id="2"] selects the li element with the class item and the
attribute data-id="2".
button.edit-btn selects the button element with the class edit-btn within
the previously filtered li.item[data-id="2"].
Filtering locators
Playwright offers functionalities to filter locators, which helps you target specific
elements within a broader selection. This is particularly useful when dealing with
lists or elements that share similar properties.
In locator.filter([options]) method, the options object provides various ways
to refine the selection of elements targeted by your locator.
Here is a breakdown of some common filtering options:
hasText (string): Filters elements that contain the specified text content
somewhere within the element or its descendants (case-insensitive).
Example: locator.filter({ hasText: 'Buy Now' }) selects elements
containing the text "Buy Now".
hasText (string or RegExp): Similar to text, but allows for more control
using regular expressions in the filter criteria.
Example: locator.filter({ hasText: /.*Product Details.*/ }) selects
elements containing text that matches the regular expression "Product
Details" (including any characters before or after).
Let us take some examples of the options that we have in the locator.filter().
Filter by text
Here is an example demonstrating how chain locators interact with specific
elements in a list of products. Suppose we have the following HTML structure:
<ul>
<li>
<h3>Product 1</h3>
<button>Add to cart</button>
</li>
<li>
<h3>Product 2</h3>
<button>Add to cart</button>
</li>
</ul>
To click the ‘Add to cart’ button for ‘Product 2’ using Playwright, you can chain
locators like this using a filter:
await page.getByRole('listitem').filter({ hasText: 'Product
2' }).getByRole('button', { name: 'Add to cart' }).click();
Let us take another example. In the following figure, you can see we have six 'Add
to cart' buttons. We need to click on the second button, which is associated with
the text 'Sauce Labs Bike Light'.
Figure 3.16: Add Cart
From the figure given above you can see the following code filters the data with
the text 'Sauce Labs Bike Light' and clicks on the 'Add to cart' button next to
it:
await page.locator("xpath=(//*
[@class='inventory_item'])").filter({ hasText: 'Sauce Labs
Bike Light' }).getByRole('button', { name: 'Add to cart'
}).click();
Filter by not having text
This method filters the list of 'listitem' elements to include only those that do not
contain the text 'Out of stock'.
await expect(page.getByRole('listitem').filter({ hasNotText:
'Out of stock' })).toHaveCount(5);
The preceding code verifies that there are exactly 5 'listitem' elements on the
page that do not contain the text 'Out of stock'. If the count is not 5, the test will
fail.
Filter by child/descendant
Playwright locators indeed support an option to select elements that either have or
do not have a descendant matching another locator. You can therefore filter by any
other locator such as a locator.getByRole(), locator.getByTestId(),
locator.getByText() etc.
Suppose we have the following HTML structure:
<ul>
<li>
<h3>Product 1</h3>
<button>Add to cart</button>
</li>
<li>
<h3>Product 2</h3>
<button>Add to cart</button>
</li>
</ul>
The following code filters list items to select those that have a descendant heading
with the name 'Product 2', and then it clicks the 'Add to cart' button within
those filtered list items.
await page.getByRole('listitem').filter({ has:
page.getByRole('heading', { name: 'Product 2' })
}).getByRole('button', { name: 'Add to cart' }).click();
Best practices for locator strategies
Playwright is a powerful automation tool for web applications, and effectively
using locator strategies is crucial for writing reliable and maintainable tests.
Locator strategies help identify and interact with web elements like buttons, input
fields, and links. Following best practices for locator strategies in Playwright can
improve the reliability, maintainability, and efficiency of your automation tests:
Use semantic selectors: Whenever possible, encourage developers to add
unique and semantic attributes to HTML elements. This could be adding
data-testid, data-automation-id. These attributes make it easier to locate
elements and make your tests more robust.
Avoid XPath as the primary locator: While Playwright supports XPath, it
is generally best to use other locator strategies like textContent, name, or
data-testid since XPath selectors can be complex and brittle.
Use CSS selectors when possible: CSS selectors are often the preferred
choice for locating elements due to their simplicity and performance. Use
page.locator(selector) to find elements using CSS selectors.
Prioritize unique attributes: Whenever possible, choose locators based on
an element's unique attributes, such as an ID or a unique class name. Avoid
using attributes that are not guaranteed to be unique across the page.
Data attributes: Leveraging custom data attributes like data-testid is a
good practice. It is especially useful when working with dynamic or single-
page applications.
Avoid using position-based selectors: Selectors like first-child, last-child,
or nth-child can be fragile because they rely on the element's position within
the Document Object Model (DOM). If the DOM structure changes, these
selectors may break.
Keep locators separate from test logic: Keep your locators in a separate
module or class so they can be easily maintained independently of your test
scripts.
Avoid hardcoded waits: Avoid using hardcoded waits because they can
make your tests slower and less reliable. Instead, use dynamic waits that wait
for the desired condition to be met.
Use wait strategies: Employ waiting mechanisms like
locator.waitForElementState() or locator.waitForSelector() to ensure
that the element you are trying to interact with is available and ready on the
page before you proceed.
Test locators on different browsers: Ensure that your locators work
consistently across different browsers. Playwright allows you to run tests on
multiple browser engines, so take advantage of this feature.
Use the Playwright codegen tool to generate locators: This tool can help
you to find the most specific locators for the elements on a page.
Use built-in locators: Use In-built locator provided by Playwright
page.getByID(), page.getByLabel(), page.getByText(),
page.getByPlaceholder(), page.getByAltText(), page.getByTitle(),
page.getByTestId().
In the next section, you will see how we can handle input box, button, and
checkboxes.
Handling element input box, buttons, and checkboxes
Handling input boxes and buttons in Playwright involves interacting with these
elements on a web page using the Playwright API.
Handling input boxes and buttons
To understand how we can handle element input boxes and buttons, let us take an
example of site https://www.lambdatest.com/selenium-playground/simple-
form-demo.
For example:
1. Launch any browser.
2. Navigate to the site.
3. We have two input boxes.
4. Enter valid numeric data in both input fields.
5. Click on Get Sum button.
6. Verify that the sum of numbers is correct.
In the following figure, you can see we have two input boxes and buttons to do the
sum of entered numbers:
Figure 3.17: Input box and button
So, there are different ways of handling the input boxes and buttons. In the above
figure, we can locate input boxes using id or placeholder.
Let us write the code to handle input boxes and buttons using id:
1. const { test, expect } = require("@playwright/test");
2. test("LambdaTest Playground Example ", async ({ page })
=> {
3. await page.goto(
4. "https://www.lambdatest.com/selenium-
playground/simple-form-demo");
5. await page.getByTestId("sum1").fill("1");
6. await page.getByTestId("sum2").fill("4");
7. await expect(page.getByRole("button", { name: "Get Sum"
})).toBeVisible();
8. await page.getByRole("button", { name: "Get Sum"
}).click();
9. await
expect(page.locator("#addmessage")).toHaveText("5");
10. });
In the above script, we are entering data in input field using id. We are verifying
that the button is visible and finally clicking the Get Sum button. Using the
assertion, we are also verifying that the sum of number is correct.
Let us write the code to handle input boxes and buttons using placeholder:
1. const { test, expect } = require("@playwright/test");
2. test("LambdaTest Playground Example ", async ({ page })
=> {
3. await page.goto(
4. "https://www.lambdatest.com/selenium-playground/simple-
form-demo");
5. await page.getByPlaceholder("Please enter first
value").fill("1");
6. await page.getByPlaceholder("Please enter second
value").fill("2");
7. await expect(page.getByRole("button", { name: "Get Sum"
})).toBeVisible();
8. await page.getByRole("button", { name: "Get Sum"
}).click();
9. await
expect(page.locator("#addmessage")).toHaveText("3");
10. });
In the above script, we are entering data in input field using placeholder. We are
verifying that the button is visible and finally clicking the Get Sum button. Using
the assertion, we are also verifying that the sum of number is correct.
Handling checkbox
In checkbox scenario, we are covering how to handle single and multi-checkbox:
Handling single checkbox
To understand how we can handle single checkbox, let us take an example of site
https://www.lambdatest.com/selenium-playground/checkbox-demo.
For example:
1. Launch any browser.
2. Navigate to the above site.
3. Verify checkbox is uncheck.
4. Check the checkbox.
5. Verify checkbox is checked.
6. Once the checkbox is checked, then uncheck it.
In the following figure, you can see for demo purpose we have one checkbox in the
page that we are using for selecting than un-select:
Figure 3.18: Single checkbox
In the following code, you can see how we navigate to a webpage, interact with a
checkbox element, and perform assertions to verify the checkbox's state changes
correctly:
1. const { test, expect } = require("@playwright/test");
2. test("LambdaTest Playground Example ", async ({ page })
=> {
3. await page.goto("https://www.lambdatest.com/selenium-
playground/checkbox-demo");
4. //Assert checkbox is Un-Checked
5. expect(await
page.getByTestId("isAgeSelected").isChecked()).toBeFalsy(
);
6. await page.getByTestId("isAgeSelected").click();
7. //Assert checkbox is Checked
8. expect(await
page.getByTestId("isAgeSelected").isChecked()).toBeTruthy
();
9.
10. //Assert checkbox is Un-Checked again
11. await page.getByTestId("isAgeSelected").click();
12. expect(await
page.getByTestId("isAgeSelected").isChecked()).toBeFalsy(
);
13. });
Handling multi-checkboxes
To understand how we can handle multi-checkbox and let us take an example of
site https://www.lambdatest.com/selenium-playground/checkbox-demo.
For example:
1. Launch any browser.
2. Navigate to the above given site.
3. Verify first checkbox is unselected.
4. Select the first checkbox and verify checkbox is selected.
5. Verify second checkbox is unselected.
6. Select the second checkbox and verify checkbox is selected.
7. Verify third checkbox is unselected.
8. Select the third checkbox and verify checkbox is selected.
In the following figure, you can see we have multiple check boxes that we are
using in our use case:
Figure 3.19: Multi-checkbox
In the following code, you can see how this code tests the behavior of multiple
checkboxes on a webpage by interacting with them and verifying their states:
1. test("Multiple Check Box Example ", async ({page}) => {
2. await page.goto("https://www.lambdatest.com/selenium-
playground/checkbox-demo");
3. //Assert Check First checkbox
4. expect(await page.getByTestId("ex1-
check1").isChecked()).toBeFalsy();
5. await page.getByTestId("ex1-check1").click();
6. expect(await page.getByTestId("ex1-
check1").isChecked()).toBeTruthy();
7. //Assert Check Second checkbox
8. expect(await page.getByTestId("ex1-
check2").isChecked()).toBeFalsy();
9. await page.getByTestId("ex1-check2").click();
10. expect(await page.getByTestId("ex1-
check2").isChecked()).toBeTruthy();
11. //Assert Check Third checkbox
12. expect(await page.getByTestId("ex1-
check3").first().isChecked()).toBeFalsy();
13. await page.getByTestId("ex1-check3").first().click();
14. expect(await page.getByTestId("ex1-
check3").first().isChecked()).toBeTruthy();
15. });
In the next section, let us take the example of how we can handle different type of
drop downs.
Handling element dropdowns, and multi-select dropdown
Playwright is a popular automation testing framework for web applications, in
which you can handle element dropdowns, including both single-select and multi-
select dropdowns, using the Playwright API. Playwright provides methods to
interact with and manipulate these dropdown elements.
We have mainly two types of drop down: one is single-select dropdown and other
is multi-select dropdown.
Handling single-select dropdowns
To understand how we can handle single-select dropdown, let us take an example
of site https://www.lambdatest.com/selenium-playground/select-dropdown-
demo.
For example:
1. Launch any browser.
2. Navigate to the above site.
3. Select the value from drop down.
In the following figure, you can we have drop down with various options, and we
can select different option from the drop down:
Figure 3.20: Single-select dropdown
In the following code, you can see there are different ways of selecting the value
from the drop down:
1. const { test, expect } = require("@playwright/test");
2. test("LambdaTest Playground Example ", async ({ page })
=> {
3. await page.goto("https://www.lambdatest.com/selenium-
playground/select-dropdown-demo");
4. await page.locator("#select-demo").click();
5. //By using text Select Day : Monday
6. await page.locator("#select-
demo").selectOption("Monday");
7. //By using Index Select First option
8. await page.locator("#select-demo").selectOption({
index: 1 });
9. //By using value Select Day : Tuesday
10. await page.locator("#select-demo").selectOption({
value: "Tuesday" });
11. //By text Select Day : Tuesday
12. await page.selectOption("#select-demo", "Tuesday");
13. //By using label Select Day : Tuesday
14. await page.locator("#select-demo").selectOption({
label: "Friday" });
15. });
You see how we can handle single-select dropdowns by using text, index, value,
and label.
In the next section, you will see how we handle multi-select dropdowns.
Handling multi-select dropdowns
To understand how we can handle multi-select dropdown, let us take an example of
site https://www.lambdatest.com/selenium-playground/select-dropdown-demo.
For example:
1. Launch any browser.
2. Navigate to the above site.
3. Select multiple value together from the dropdown.
In the following code, you can see how we can do multi-select value from the
dropdown:
1. const { test, expect } = require("@playwright/test");
2. test("LambdaTest Playground Example ", async ({ page })
=> {
3. await page.goto("https://www.lambdatest.com/selenium-
playground/select-dropdown-demo");
4. //Select Multi Option from the Multi Select drop down
5. await page.selectOption("#multi-select",
["Pennsylvania","Texas","Florida"]);
6. await page.locator("#multi-select").selectOption(["
Pennsylvania ", "Texas", "Florida"]);
7. });
In the following figure, you can see three options "Pennsylvania", "Texas",
"Florida" are selected.
Figure 3.21: Multi-select dropdown
In the next section, you will see how to handle radio buttons, and drag and drop
feature.
Handling radio buttons, and mouse drag and drop
In Playwright, you can easily manage radio buttons by selecting specific options
using the click method, and you can perform mouse drag and drop operations by
locating source and target elements and utilizing the drag and drop function.
Handling radio buttons
To understand how we can handle radio buttons, let us take an example of site
https://www.lambdatest.com/selenium-playground/radiobutton-demo.
For example:
1. Launch any browser.
2. Navigate to the above site.
3. Select first radio option Male.
4. Verify first radio option Male should be selected.
5. Select second radio option Female.
6. Verify second radio option Female should be selected.
7. Finally, verify first radio option Male should be un-selected.
You can see how this code tests the functionality of radio buttons on a webpage by
selecting different options and verifying their states. Refer to the following code:
1. const { test, expect } = require("@playwright/test");
2. test("LambdaTest Playground Example ", async ({ page })
=> {
3. await page.goto("https://www.lambdatest.com/selenium-
playground/radiobutton-demo");
4. // Select First Radio Option "Male" using click()
method
5. await
page.locator("//input[@name='optradio']").first().click()
6. expect(await
page.locator("//input[@name='optradio']").first()).toBeCh
ecked()
7. expect(await
page.locator("//input[@name='optradio']").first().isCheck
ed()).toBeTruthy()
8. // Select Second Radio Option "Female" using check()
method
9. await
page.locator("//input[@name='optradio']").last().check()
10. expect(await
page.locator("//input[@name='optradio']").last()).toBeChe
cked()
11. expect(await
page.locator("//input[@name='optradio']").last().isChecke
d()).toBeTruthy()
12. //Verify First Radio Option 'Male' should be un-
selected
13. expect(await
page.locator("//input[@name='optradio']").first().isCheck
ed()).toBeFalsy()
14. });
In the following figure, you can see there are two options Male and Female that
we have been selected one by one in our script:
Figure 3.22: Radio button
Handling drag and drop
To understand how we can handle radio buttons, let us take an example of site
https://www.lambdatest.com/selenium-playground/drag-and-drop-demo.
For example:
1. Launch any browser.
2. Navigate to the above site.
3. Drag first field Draggable 1.
4. Drop into the destination target.
5. Drag first field Draggable 2.
6. Drop into the destination target.
7. Verify by putting assertion fields are successfully dropped.
There are two ways of drag and drop, one is using locator.dragTo() ,method and
other way is using hover, mouse.down, and mouse.up methods:
1. const { test, expect } = require("@playwright/test");
2. test("LambdaTest Playground Example ", async ({ page })
=> {
3. await page.goto("https://www.lambdatest.com/selenium-
playground/drag-and-drop-demo");
4. // Drag and drop using Method -01
5. const drag =await page.locator("//span[normalize-
space()='Draggable 1']");
6. const drop = await page.locator('#mydropzone');
7. await drag.hover()
8. await page.mouse.down()
9. await drop.hover()
10. await page.mouse.up()
11. // Drag and drop using Method -02
12. const dragElement =await page.locator("//span[normalize-
space()='Draggable 2']");
13. const dropArea = await page.locator('#mydropzone');
14. await dragElement.dragTo(dropArea);
15. //Assertion to verify fields are drop successfully
16. await expect(page.locator("#droppedlist", {hasText:
'Draggable 1'})).toBeVisible()
17. await expect(page.locator("#droppedlist", {hasText:
'Draggable 2'})).toBeVisible()
18. });
Figure 3.23: Drag and drop
In the above script, you can see using Method–01 and Method–02 items are
dropped in the destination.
Conclusion
This chapter has been a comprehensive journey into the world of web automation,
focusing on essential locator strategies such as CSS, XPath, and ID. We have
harnessed the power of Playwright's built-in locators for efficiency. You have
learned to adeptly handle various web elements, from input boxes and buttons to
checkboxes, dropdowns, multi-select dropdowns, radio buttons, and the intricate
art of mouse drag and drop operations.
In the next chapter, we will discuss the intricacies of handling more advanced and
complex web elements. We will start by learning how to handle file uploads and
downloads. Next, we will focus on handling iframes. Another important aspect we
will cover is handling JavaScript alerts. Additionally, we will explore working with
tabs and popups, which are common user interface elements found in web
applications. Finally, we will understand the concept of Shadow DOM, learn how
to interact with elements within the Shadow DOM.
Multiple choice questions
1. Playwright provides built-in locators for identifying elements. Which of
the following is a built-in locator?
a. findElement
b. getByText
c. locateElement
d. querySelector
2. How can you interact with radio buttons using Playwright?
a. uncheck
b. toggle
c. select
d. click
3. Which of the following is not a valid locator strategy in Playwright?
a. Name
b. LinkText
c. CSS
d. JavaScript
4. What are some common locator strategies used in Playwright for
finding web elements?
a. Playwright IDs and names
b. CSS, XPath, and ID
c. JavaScript and Python
d. Playwright elements and attributes
5. What is the purpose of the Playwright function hover()?
a. It simulates hovering the mouse over an element.
b. It clicks an element.
c. It enters text into an input field.
d. It selects an option from a dropdown.
Answers
1. b.
2. d.
3. d.
4. b.
5. a.
CHAPTER 4
Handling Complex Elements
Introduction
In the preceding chapter, we explored Playwright's built-in locator
capabilities and learned how to interact with various basic elements, such as
input boxes, buttons, checkboxes, dropdowns, multi-select dropdowns, radio
buttons, and performed mouse drag-and-drop operations. In this chapter, we
will cover the intricacies of handling more advanced and complex web
elements; this expertise enhances the effectiveness of automated testing by
ensuring accurate and seamless interactions with complex web elements.
Structure
This chapter covers the following topics:
Handling element upload and download the file
Handling iframe
Handling JavaScript alerts
Handling tabs and window pop-ups
Handling shadow DOM
Objectives
By the end of this chapter, you will grasp the expertise required to handle
complex web elements proficiently. This encompasses managing tasks such
as uploading and downloading files, navigating iframes, effectively handling
JavaScript (JS) alerts, seamlessly working with tabs and pop-ups, and
mastering the intricacies of dealing with shadow Document Object Model
(DOM). This comprehensive knowledge and skillset will empower you to
tackle a wide array of advanced web interactions, ensuring your ability to
automate diverse and sophisticated web applications.
Handling upload and download the file
Automating file uploads and downloads is a common requirement in web
application testing. Automating these processes ensures that file-related
functionalities work as expected, enhancing the efficiency and accuracy of
testing procedures.
Handling file upload and download functionality is a common requirement
in web automation testing. Playwright is a powerful automation library that
allows you to interact with web elements, including handling file uploads
and downloads.
Handling upload file
To understand how we can handle upload the files feature, let us take an
example of site: https://www.lambdatest.com/selenium-
playground/upload-file-demo.
The setInputFiles() method in Playwright is used to set the files that
should be uploaded using a file input element on a web page. Using this
method, we can upload single and multiple files.
Upload single file
In this scenario, we are using Playwright's setInputFiles() method to
facilitate the selection of single file for upload on a web page.
For example:
1. Open the above URL.
2. Upload the file.
3. Verify the successfully uploaded message. Refer to the following
figure.
In the following figure, you can see we have upload option that we can use
to verify the upload functionality:
Figure 4.1: Single file upload
Code:
1. const { test, expect } =
require("@playwright/test");
2. test('Upload a Single file with asseration', async
({page}) => {
3. await
page.goto('https://www.lambdatest.com/selenium-
playground/upload-file-demo')
4. await page.setInputFiles('//input[@id="file"]',
'uploadFile/uploadFile1.png')
5. await
expect(page.locator('//div[@id="error"]')).toContain
Text('File Successfully Uploaded')
6. })
In the above code, you can see that we have uploaded one single file with the
name uploadFile1.png, and after uploading, we are making sure the file has
uploaded successfully.
Execute the script provided, and you can see in the following figure, the file
is uploaded with a successful message:
Figure 4.2: Single file upload result
Upload multiple file
In this scenario, we are using Playwright's setInputFiles() method to
facilitate the selection of multiple files. In this case, instead of passing one
file we must pass an array of files.
Let us take an example of site http://blueimp.github.io/jQuery-File-
Upload:
1. Open the above URL.
2. Use setInputFiles() method to upload the multiple file.
3. Finally, verify the successfully uploaded message for each uploaded
file. Refer to the following figure.
In the following figure, we have the option to upload the multiple file:
Figure 4.3: Multiple file upload
Code:
1. test('Upload Multiple files with asseration', async
({page}) => {
2. await page.goto('http://blueimp.github.io/jQuery-
File-Upload/')
3. await page.setInputFiles('input[type="file"]',
['uploadFile/uploadFile1.png','uploadFile/uploadFile
2.png'])
4. await
expect(page.locator('p.name').nth(0)).toHaveText('up
loadFile1.png')
5. await
expect(page.locator('p.name').nth(1)).toHaveText('up
loadFile2.png')
6. })
As you can see above, we are uploading multiple files (uploadFile1,
uploadFile2), and after uploading, we are verifying if the files have been
successfully uploaded or not using assertion. Again, we are using
setInputFiles() method to upload multiple files.
Here is a detailed explanation of the code’s breakdown:
await page.goto (http://blueimp.github.io/jQuery-
File-Upload/): This line navigates the browser to the specified
URL (http://blueimp.github.io/jQuery-File-Upload/).
await page.setInputFiles (input[type=”file”],
[uploadFile/uploadFile1.png,
uploadFile/uploadFile2.png]): This line uses the
setInputFiles method to be uploaded in the file input element with
the Cascading Style Sheets (CSS) selector
'input[type="file"]'. Two files are being uploaded:
uploadFile/uploadFile1.png and
uploadFile/uploadFile2.png.
await expect (page.locator
(p.name).nth(0)).toHaveText(uploadFile1.png): This line
uses the expect function to create an assertion. It expects the first
paragraph element with class name 'name' (found using the locator
method) to have the text content uploadFile1.png.
await expect
(page.locator('p.name').nth(1)).toHaveText('uploadF
ile2.png'): Like the previous line, this one expects the second
paragraph element with class name 'name' to have the text content
'uploadFile2.png'.
Execute the above script, and you can see that both files are uploaded:
Figure 4.4: Multiple file upload result
Handling download file
Playwright is a Node.js library for automating browsers. It provides a high-
level application programming interface (API) to interact with web pages
and automate browser tasks. If you want to download a file using
Playwright, you can use the page.waitForEvent(download) method to wait
for a download to start, and then handle the download.
We can download single file and multiple files, to understand how we can
handle download files feature. Let us look at an example of site: https://the-
internet.herokuapp.com/download.
Single file download
To download a single file using Playwright, you can use the page.click()
method to interact with the download link and then wait for the download to
complete using the page.waitForEvent('download') method.
For example:
1. Open the above provided URL.
2. Click on LambdaTest.txt link.
3. Download the file in folder with providing the path.
4. Verify file downloaded in the desire path.
Code:
1. const { test, expect } =
require("@playwright/test");
2. const fs = require('fs');
3. test('Download a Single file and verifying Using
Assertion', async ({page}) => {
4. await page.goto('https://the-
internet.herokuapp.com/download')
5. const [download] = await Promise.all([
6. page.waitForEvent('download'),
7. page.locator('text=LambdaTest.txt').click(),
8. ])
9. const suggestedFileName =
download.suggestedFilename()
10. const filePath = 'download/' + suggestedFileName
11. await download.saveAs(filePath)
12. expect(fs.existsSync(filePath)).toBeTruthy()
13. })
Here is a breakdown of test case:
Navigating to the specified webpage: await page.goto
(https://the-internet.herokuapp.com/download).
Waiting for the download event to occur and triggering the download
by clicking on an element with the text LambdaTest.txt.
page.waitForEvent (‘download’) function in Playwright is used to
pause the script execution until a download event occurs on the page.
Extracting the suggested file name for the download using this line:
const suggestedFileName = download.suggestedFilename().
Constructing the file path where the downloaded file will be saved
using: const filePath = 'download/' + suggestedFileName.
Saving the download to the specified file path using this line: await
download.saveAs(filePath).
Checking if the file exists in the specified path using the
fs.existsSync() function:
expect(fs.existsSync(filePath)).toBeTruthy().
Execute the above script, and you can see that a single file is downloaded, as
shown in the following figure:
Figure 4.5: Single file download result
Multiple file download
In the second scenario, we can download multiple files. For this, you might
need to loop through a list of download links and trigger the downloads.
Here is an example of how you can download multiple files sequentially:
1. Open the URL https://the-internet.herokuapp.com/download.
2. Click in test.xlsx link.
3. Click on sample.txt link.
4. Download the file in folder with providing the path.
5. Verify file downloaded in the desire path.
Code:
1. const { test, expect } =
require("@playwright/test");
2. const fs = require('fs');
3. test('Download Multiple files and Apply Asseration',
async ({page}) => {
4. await page.goto('https://the-
internet.herokuapp.com/download')
5. const downloadFile = ['test.xlsx', 'sample.txt']
6. for (const fileName of downloadFile) {
7. const [download] = await Promise.all([
8. page.waitForEvent('download'),
9. page.locator(`text=${fileName}`).click(),
10. ])
11. const suggestedFileName =
download.suggestedFilename()
12. const filePath = 'download/' +
suggestedFileName
13. await download.saveAs(filePath)
14. expect(fs.existsSync(filePath)).toBeTruthy()
15. }
16. })
Here is a detailed explanation of the code’s breakdown:
Import the necessary dependencies: test and expect from the
@playwright/test package, and the file system (fs) module.
Inside the test function, it uses the page object to navigate to a web
page with the URL https://the-internet.herokuapp.com/download.
downloadFile contains two file names: test.xlsx and sample.txt.
These are the files that the script will attempt to download.
Then, we iterate through each file name in the downloadFile array
using a for...of loop.
For each file name, we will perform the following steps:
1. Waits for a download event to occur on the page using
page.waitForEvent('download'). This event is triggered when a
file download is initiated.
2. Clicks on the element with the text matching the current fileName
using page.locator(text=${fileName}).click(). This simulates a
user clicking on a link to download the file.
3. Retrieves the suggested file name for the download using
download.suggestedFilename().
4. Constructs a file path for the downloaded file in a download
subdirectory using the suggested file name.
5. Saves the downloaded file to the specified file path using
download.saveAs(filePath).
6. Finally use the expect function to assert that the file exists on the
file system by checking if fs.existsSync(filePath) returns true.
Let us execute the above code. In the following two figures, you can see we
have first click on test.xls then sample.txt link:
First, we have clicked on test.xls:
Figure 4.6: Multiple file download result
In the following figure, we have clicked on the sample.txt:
Figure 4.7: Multiple file download result
Handling iframe
An iframe, or inline frame, is a HTML element that allows you to load
another HTML document within the current HTML document. The most
typical application of an iframe is to embed content from one website into
another. Sites that prohibit direct hotlinking of material may permit it since
the child site can load its own content and cookies. The preferred method for
including Google Maps or a YouTube video is using an iframe.
Here are some of the benefits of using iframes:
Convenience: Using iframes, you may quickly include material from
other websites into your site. This can help you save time and effort
also make your website more informative and engaging.
Flexibility: Using iframe, you can embed variety of content in your
website including videos, maps, social media feeds. Because of this,
you have a flexibility how you can lay out your website.
Control: iframes may be styled and managed using JavaScript and
CSS. This enables you to design an integrated and seamless
experience for your users.
To understand more about iframe, let us take an example of site:
https://www.lambdatest.com/selenium-playground/iframe-demo/.
We will explore two types of iframe: one is a normal iframe, and another one
is a nested iframe.
For example, Normal iframe Follow the given steps:
1. Open the URL https://www.lambdatest.com/selenium-
playground/iframe-demo/.
2. Enter the text qaautomationlabs.com.
3. Validate the text qaautomationlabs.com after entering.
In the following figure, you can see the text box is inside the iframe, and we
have to enter some text in this text box:
Figure 4.8: Normal iframe
Code:
1. const { test, expect } =
require("@playwright/test");
2. test('Normal iframe Enter data in field which inside
the iframe', async ({page}) => {
3. await
page.goto('https://www.lambdatest.com/selenium-
playground/iframe-demo/')
4. const textarea = await
page.frameLocator('[src="./contant"]').locator('//di
v[@class="rsw-ce"]')
5. await textarea.fill('qaautomationlabs.com')
6. await
expect(textarea).toHaveText('qaautomationlabs.com')
7. })
Execute the preceding script and you can see that data is entered in text box
which inside the iframe:
Figure 4.9: Normal iframe result
Example: Nested iframe
Nested iframe is the type of frame that is embedded with another iframe.
Each iframe functions independently, allowing for the integration of multiple
layers of external content into a single webpage.
In the example given below, you can see that at a high level, we have two
iframes named frame-top and frame-bottom. In the Top frame, we have only
the iframe with the name Top, and in the bottom frame, we have three
iframes named Left, Middle, and Right.
Figure 4.10: Nested iframe
At the Top of the iframe, we have only one iframe name: frame-top. The
bottom of the iframe, as you can see in the following figure, we have three
iframe name frame-left, frame-middle, and frame-right:
Figure 4.11: Nested iframe top
You can see in the following figure, we have four iframe name frame-
bottom, frame-left, frame-middle, and frame-right:
Figure 4.12: Nested iframe bottom-left-middle
Now, let us see how we can automate nested iframe.
Code:
1. const { test, expect } =
require("@playwright/test");
2. test('Nested iframe ', async ({page}) => {
3. await
page.goto('https://www.lambdatest.com/selenium-
playground/nested-frames/')
4. //Top iframe
5. const topframebody = await
page.frameLocator('[name="frame-
top"]').locator('body')
6. await expect(topframebody).toHaveText('Top')
7. //Bottom iframe
8. const bottomframe = await
page.frameLocator('[name="frame-bottom"]')
9. const middleframebody = await
bottomframe.frameLocator('[name="frame-
middle"]').locator('body')
10. await
expect(middleframebody).toHaveText('Middle')
11. const rightframebody = await
bottomframe.frameLocator('[name="frame-
right"]').locator('body')
12. await expect(rightframebody).toHaveText('Right')
13. const bottomframebody = await
bottomframe.frameLocator('[name="frame-
left"]').locator('body')
14. await expect(bottomframebody).toHaveText('Left')
15. })
Execute the above script, and as you can see in the following figure, we have
verified the text that we have in each iframe:
Figure 4.13: Nested iframe result
Handling JavaScript alerts
In JavaScript, an alert is a function that displays a dialog box with a
specified message and an Ok button. It is commonly used for simple pop-up
notifications or alerts to inform users about something on a webpage.
There are different types of JavaScript alerts:
Standard alert: This type of alert displays a simple dialog box with a
message and an OK button.
Confirm alert: This type of alert displays a dialog box with a
specified message and OK and Cancel buttons. It is often used to ask
the user for confirmation.
Prompt alert: This type of alert prompts the user to enter some
information. It takes an optional second parameter, which is the
default text displayed in the input field.
Standard alert
To understand more about standard alert, let us take an example of site:
https://www.lambdatest.com/selenium-playground/javascript-alert-box-
demo.
For example:
1. Open the above URL.
2. Invoke a JS alert.
3. Validate the text content.
4. Click OK.
Code:
1. test('Handling JS Alert - Standard Alert', async
({page}) => {
2. await
page.goto('https://www.lambdatest.com/selenium-
playground/javascript-alert-box-demo')
3. page.on('dialog', async (dialog) => {
4. expect(dialog.message()).toEqual('I am an alert
box!')
5. await dialog.accept()
6. })
7. })
Let us break down what this test case is doing step by step:
1. Test description: The test is named ‘Handling JS Alert - Standard
Alert’, indicating that it deals with a standard JavaScript alert dialog.
2. Navigation: await
page.goto('https://www.lambdatest.com/selenium-
playground/javascript-alert-box-demo'): This line
navigates the Puppeteer controlled browser page to the specified URL
where the JavaScript alert is present.
3. Event listener setup: page.on('dialog', async (dialog) => { ...
}): This sets up an event listener for the 'dialog' event. When an
alert, confirm, or prompt dialog is displayed on the page, this event
will be triggered.
4. Alert message validation: expect(dialog.message()).toEqual('I am
an alert box!'): Inside the event listener, this line asserts that the
message displayed in the alert dialog is 'I am an alert box!'. If the
message is different, the test will fail.
5. Accepting the alert: await dialog.accept(): If the alert message
matches the expected value, this line accepts the alert dialog,
effectively clicking OK.
Output:
Following is the output of confirm alert after clicking on OK button of the
message ‘I am an alert box!’:
Figure 4.14: Standard alert
Confirm alert
To explain more about confirm alert, let us take an example of site:
https://www.lambdatest.com/selenium-playground/javascript-alert-box-
demo.
Let us see the following steps when we click on the Cancel button of
Confirm box:
1. Open the above URLInvoke a JS alert by click on Click Me against
Confirm box.
2. Click on Cancel button.
3. Validate the text "You pressed Cancel!”.
Let us see the following steps when we click on OK button:
1. Open the URL https://www.lambdatest.com/selenium-
playground/javascript-alert-box-demo.
2. Invoke a JS alert by click on Click Me against Confirm box.
3. Click on Ok button.
4. Validate the text ‘You pressed OK!’.
Code:
Here is the code to automate both scenarios:
1. test('Handling JS Alert - Confirm Alert With
Cancel Button:', async ({page}) => {
2. await
page.goto('https://www.lambdatest.com/selenium-
playground/javascript-alert-box-demo')
3. page.on('dialog', async (dialog) => {
4. expect(dialog.message()).toEqual('Press a
button!')
5. await dialog.dismiss()
6. })
7. await
page.locator('[type="button"]').nth(1).click()
8. await expect(page.locator('//p[@id="confirm-
demo"]')).toHaveText('You pressed Cancel!')
9. })
10. test('Handling JS Alert - Confirm Alert with Ok
Button', async ({page}) => {
11. await
page.goto('https://www.lambdatest.com/selenium-
playground/javascript-alert-box-demo')
12. page.on('dialog', async (dialog) => {
13. expect(dialog.message()).toEqual('Press a
button!')
14. await dialog.accept()
15. })
16. await
page.locator('[type="button"]').nth(1).click()
17. await expect(page.locator('//p[@id="confirm-
demo"]')).toHaveText('You pressed OK!')
18. })
Let us break down what this test case is doing step by step:
1. Handling JS alert—Confirm alert with Cancel button:
a. Navigates to the URL https://www.lambdatest.com/selenium-
playground/javascript-alert-box-demo.
b. Sets up an event listener for the dialog event using page.on. This
event listener will be triggered when a dialog (in this case, a
confirm alert) appears on the page.
c. In event listener, it expects the dialog message to be 'Press a
button!' using expect(dialog.message()).toEqual('Press a
button!').
d. It then dismisses the dialog by calling await dialog.dismiss(),
which is equivalent to clicking the Cancel button in the confirm
alert.
e. After dismissing the alert, verify the text 'You pressed
Cancel!'.
2. Handling JS alert—Confirm alert with OK button:
a. Similar to the first test, it navigates to the same URL.
b. Sets up an event listener for the 'dialog' event to handle the
confirm alert.
c. In the event listener, it expects the dialog message to be 'Press
a button!'.
d. This time, it accepts the dialog by calling await
dialog.accept(), which is equivalent to clicking the OK button
in the confirm alert.
e. After accepting the alert, verify the text 'You pressed Ok!'.
Provided are two figures, which are the output of Confirm Alert with Cancel
and OK Button.
In the following figure, you can see the message ‘You pressed Cancel!’
when we click on Cancel button:
Figure 4.15: Confirm alert cancel button
Next, you can see the message “You pressed Ok!” when we click on OK
button:
Figure 4.16: Confirm alert OK button
Prompt alert
To explain more about Prompt alert, let us take an example of site
https://www.lambdatest.com/selenium-playground/javascript-alert-box-
demo.
For example:
1. Open the URL https://www.lambdatest.com/selenium-
playground/javascript-alert-box-demo.
2. Invoke a JS alert by click on Click Me against Prompt box.
3. Enter some text.
4. Click OK.
5. Verify the text You have entered 'LambdaTest'.
Code:
1. test('Handling JS Alert - Enter Data in Input text
in prompt:', async ({page}) => {
2. await
page.goto('https://www.lambdatest.com/selenium-
playground/javascript-alert-box-demo')
3. page.on('dialog', async (dialog) => {
4. expect(dialog.message()).toEqual('Please
enter your name')
5. await dialog.accept('LambdaTest')
6. })
7. await
page.locator('[type="button"]').nth(2).click()
8. await expect(page.locator('//p[@id="prompt-
demo"]')).toHaveText("You have entered 'LambdaTest'
!")
9. })
Here is a breakdown of the given code:
Navigation: The test navigates to the specified URL:
‘https://www.lambdatest.com/selenium-playground/javascript-
alert-box-demo’.
Event listener: The test sets up an event listener for the dialog event.
When a JavaScript prompt appears on the page, the event listener is
triggered.
Dialog handling: When the dialog appears, the test checks if the
message inside the prompt is ‘Please enter your name’. If it is, the
test script accepts the prompt and enters the text ‘LambdaTest’.
Button click: After setting up the event listener, the test script clicks
the third button on the page (‘[type=”button”]’).nth(2).
Assertion: Finally, through assertion verify the text You have entered
‘LambdaTest’.
Output:
Here, first we are using the dialog object to assert the message displayed on
the dialog. Then, we are using dialog.accept(‘LambdaTest’) to enter the
text ‘LambdaTest’ in the prompt.
Refer to the following figure:
Figure 4.17: Prompt alert
Handling tabs and window pop-ups
Tabs are user interface that enable users to access and move through online
sites or documents inside the same browser window. Every tab functions
independently with its own URL bar, controls, and content. Instead of having
several open windows take up space on their desktop or taskbar, users may
organise and navigate between many websites in an organised way using
tabs. This design promotes efficient multitasking and streamlines the
browsing experience.
On the other hand, window pop-ups model are small, independent browser
windows that appear on top of the current browser window. They can be
launched either automatically by websites or by human actions such as
clicking a link. Pop-ups are widely used for marketing, advertising, or
displaying exclusive content that must be viewed immediately. Although
pop-ups were historically associated with adverts, modern web apps also use
them for important notifications or targeted user feedback collecting.
Handling new tabs
To understand more about handling new tabs, let us take an example of site
https://the-internet.herokuapp.com/windows.
For example:
1. Open the URL https://the-internet.herokuapp.com/windows.
2. Click on Click Here link.
3. Validate the text content.
4. Click OK.
Code:
1. const { test, expect } =
require("@playwright/test");
2. test('Open New Tab and back to original Page',
async({context}) => {
3. const page = await context.newPage();
4. await page.goto("https://the-
internet.herokuapp.com/windows");
5.
6. // Wait for a new page to be created
7. const [newPage] = await Promise.all([
8. context.waitForEvent('page'),
9. await page.locator('//a[normalize-
space()="Click Here"]').click()
10. ]);
11. // Wait for the new page to load
12. await newPage.waitForLoadState();
13. // Get the Title of the new tab page
14. console.log('New Tab --- >>>', await
newPage.title());
15. // Close the new tab and navigate back to the
original page
16. await newPage.close();
17. // Title of the existing page
18. console.log('Existing page --- >>> ', await
page.title());
19. });
Here is a breakdown of what the code is doing:
Inside the test function, it creates a new page using context.newPage()
and navigates to the URL "https://the-
internet.herokuapp.com/windows"
It waits for a newpage to be created by using
context.waitForEvent('page') in combination with a click action on
an element identified by an XPath locator. This simulates opening a
new tab/window by clicking a link with the text "Click Here."
Once the new page is created, it waits for the new page to finish
loading using newPage.waitForLoadState().
It retrieves and prints the title of the new tab page using
newPage.title().
It closes the new tab/page using newPage.close().
Finally, it retrieves and prints the title of the original page using
page.title().
In the following figure given, we have clicked on Click Here link to open
the new tab. After clicking on the link, we are printing the title of new page
and finally print the title of the original page. You can see the printed title in
console window in the following figure:
Figure 4.18: New tab
Handling multi page using browser context
A browser context in Playwright represents an isolated environment in a
browser instance, similar to an incognito or private browsing session. Each
browser context operates independently, with its own cookies, local storage,
session storage, cache, and authentication state. This allows you to simulate
multiple users or test different scenarios in the same browser without
interference between contexts.
Let us take the following example:
import { test } from "@playwright/test";
test("Browser Context example", async ({ browser }) => {
//First Context - Open Page in New Browser
const firstContext = await browser.newContext();
const firstPage = await firstContext.newPage();
await
firstPage.goto("https://automationexercise.com/login");
await firstPage.locator('input[data-qa="login-
email"]').click();
await firstPage.locator('input[data-qa="login-
email"]').fill("ncrmeet1@yopmail.com");
await firstPage.locator('input[data-qa="login-
email"]').press("Tab");
await
firstPage.locator('input[placeholder="Password"]').fill(
"Test@1234");
await firstPage.locator('button[data-qa="login-
button"]').click();
//Second Context -- Open Page in New Browser
const secondContext = await browser.newContext();
const secondPage = await secondContext.newPage();
await secondPage.goto("https://ecommerce-
playground.lambdatest.io/index.php?
route=account/login");
await secondPage.locator('//input[@id="input-
email"]').fill('lambdatestnew@yopmail.com')
await secondPage.locator('//input[@id="input-
email"]').press("Tab");
await secondPage.locator('//input[@id="input-
password"]').fill("Lambda123");
await
secondPage.locator('//input[@value="Login"]').click();
firstPage.bringToFront();
await
firstPage.locator('//a[@href="/products"]').click();
await firstPage.pause();
await secondPage.pause();
await firstPage.close();
await secondPage.close();
await firstContext.close();
await secondContext.close();
});
The above script demonstrates how to create and manage multiple browser
contexts to simulate different user sessions in a single test.
First browser context (firstContext):
A new browser context is created (firstContext), which acts like a
separate browser session.
A page is opened (firstPage) that navigates to a login page
(https://automationexercise.com/login).
The script simulates filling out the login form with email and
password and logs in to the site.
Second browser context (secondContext):
Another browser context (secondContext) is created, opening a
different page (secondPage).
The script navigates to a different login page (https://ecommerce-
playground.lambdatest.io) and logs in with different credentials.
Switching between pages:
After logging in on both pages, the script switches back to the first
page (firstPage) to navigate to the products section.
Pausing and closing:
Both pages are paused (page.pause()) to allow for manual inspection
during the test.
Finally, both pages and contexts are closed to clean up the browser
resources.
Handling window pop up model
Window pop-up model are user interface (UI) elements that are utilized in
various web and software applications to enhance user interaction and gather
crucial input. Model pop-ups require the user to interact with them before
they can return to the main window. This makes them ideal for displaying
important information or collecting essential input from the user.
To understand more about handling window pop ups, let us take an example
of site https://www.lambdatest.com/selenium-playground/window-
popup-modal-demo. In the following example, we are opening two window
pop up model one by clicking on Twitter button and another by clicking on
Facebook button:
Figure 4.19: Window popup
For example:
1. Open twitter pop up model and validate the title.
2. Open the URL mentioned above.
3. Open Twitter Window popup by clicking on Follow On Twitter.
4. Validate title of window popup.
5. Finally, validate title of existing page.
Open Facebook pop up model and validate the title:
1. Open the URL mentioned above.
2. Open Facebook Window popup by clicking on Open Facebook
Popup.
3. Validate title of window popup.
4. Finally validate title of existing page.
Code:
1. test('Click on Twitter Button to open Window Based
Popup ', async({page})=>{
2. await
page.goto("https://www.lambdatest.com/selenium-
playground/window-popup-modal-demo")
3. const [windowPopup] = await Promise.all([
4. page.waitForEvent('popup'),
5. // Open the Popup
6. page.locator("[title='Follow @Lambdatesting
on Twitter']").click()
7. ])
8. await windowPopup.waitForLoadState();
9. // Print title of window popup
10. console.log('Title of the Pop Up is ', await
windowPopup.title());
11. windowPopup.close()
12. // Print title of existing page
13. console.log('Title of existing page ',await
page.title());
14. })
15. test('Click on Facebook Button to open Window Based
Popup ', async({page})=>{
16. await
page.goto("https://www.lambdatest.com/selenium-
playground/window-popup-modal-demo")
17. const [windowPopup] = await Promise.all([
18. page.waitForEvent('popup'),
19. // Open the Popup
20. page.locator("[title='Follow @Lambdatesting on
Facebook']").click()
21. ])
22. await windowPopup.waitForLoadState();
23. // Print title of window popup
24. console.log('Title of the Pop Up is ', await
windowPopup.title());
25. windowPopup.close()
26. // Print title of existing page
27. console.log('Title of existing page ',await
page.title());
28. })
Handling shadow DOM
The shadow Document Object Model (shadow DOM), is a standard
utilized on the web that enables the encapsulation of a subtree, within a web
component. It serves to construct self-contained components, in web
applications, where the internal structure and styling of a component are
confined and separated from the rest of the document.
Shadow DOM allows for the creation of shadow trees, in addition to the
document tree. These shadow trees represent the DOM structure of the
project. Each have their own root node called the shadow root. The shadow
root contains elements and styling to that tree. These trees are connected to
elements, within the document tree or other shadow trees through connecting
nodes known as shadow hosts. The boundary that separates the DOM from
the shadow DOM is referred to as the shadow boundary.
Figure 4.20: Shadow DOM
Source: https://www.lambdatest.com/blog/shadow-dom-in-selenium/
Key elements of shadow DOM include:
1. Shadow host: The shadow host refers to the DOM element that has a
shadow DOM attached to it. It acts as the container, for the
encapsulated elements within it.
2. Shadow tree: Within the Shadow DOM there exists a shadow tree,
which comprises the encapsulated DOM elements along with their
associated styles and behavior.
3. Shadow root: The shadow root represents the root node of the
shadow tree. It serves as a bridge, between the shadow DOM and the
documents DOM.
4. Shadow boundary: The shadow boundary denotes the point at which
the shadow DOM concludes, and the regular DOM begins. Elements
residing within the shadow DOM cannot be Styled from outside
unless specific mechanisms are provided to enable actions.
To understand more on how to handle elements which are inside the shadow
DOM, let us take an example of site:
https://www.lambdatest.com/selenium-playground/shadow-dom.
Example:
1. Open the above URL.
2. Traverse through the shadow DOM and reach the input box.
3. Enter data in Username, Email Address, Password, Confirm
Password.
Code:
1. const { test, expect } =
require("@playwright/test");
2. test('Handling Shadow DOM', async ({page, })
=> {
3. await
page.goto('https://www.lambdatest.com/selenium-
playground/shadow-dom')
4. await
page.locator('input[name="username"]').fill('Kailash
')
5. await
page.locator('input[type="email"]').nth(2).fill('tes
t@qaautomationlabs.com')
6. await
page.locator('input[name="password"]').fill('qa@!tes
t')
7. await
page.locator('input[name="confirm_password"]').fill(
'qa@!test')
8. })
Output:
In the following figure given, we are able to enter data in the field's
username, email, password, confirm_password which are inside the
shadow DOM:
Figure 4.21: Shadow DOM result
Conclusion
This chapter has been a comprehensive journey into the world of web
automation, focusing on how we handle complex elements like upload and
download, handling iframe, handling JS alerts, handling tabs, window pop-
ups model, and handling shadow DOM.
In the next chapter, we will explore playwright tools various features in
depth. You will see playwright feature like Codegen to record and playback
the script, how to debug the Playwright script using Playwright inspector,
Trace viewer, and how we can run the test cases in UI mode.
Multiple choice questions
1. How can you handle file upload in Playwright?
a. Use the page.setInputFiles(selector, file) method to set input files
for file upload.
b. Use the input.uploadFiles(file) method to handle file uploads.
c. Use the page.fill(selector, filePath) method to fill the file input
field with the file path.
d. Use the page.attachFile(selector, filePath) method to attach a file
to the file input field.
2. How can you interact with elements inside an iframe in
Playwright?
a. Use the page.frame(selector) method to switch to the iframe
context.
b. Use the page.frameLocator(selector) method to interact with
elements inside the iframe.
c. Use the frame.focus(selector) method to focus on the iframe.
d. Use the page.waitForSelector(selector) method inside the iframe
context.
3. Which event should be used to handle JavaScript alerts, confirms,
and prompts in Playwright?
a. page.on('dialog', dialog => {})
b. page.handleDialogs(dialogHandler)
c. page.dialog('accept')
d. page.waitForEvent('alert')
4. What method is used to handle window pop-ups in Playwright?
a. page.waitForEvent('popup')
b. page.handlePopups()
c. page.popups()
d. page.on('popup', popup => {})
5. What method should be used to handle file downloads in
Playwright?
a. page.handleDownloads(downloadHandler)
b. page.waitForEvent('download')
c. page.downloadFile(selector, filePath)
d. page.on('download', download => {})
Answers
1. a.
2. b.
3. a.
4. a.
5. b.
Join our book’s Discord space
Join the book's Discord Workspace for Latest updates, Offers, Tech
happenings around the world, New Release and Sessions with the Authors:
https://discord.bpbonline.com
CHAPTER 5
Exploring Playwright Tools in Depth
Introduction
In the preceding chapter, we explored Playwright's built-in locator capabilities. We
learned how to interact with various basic elements, such as handling iframe, how
to upload and download the file, handling JS alerts, working with tabs, shadow
DOM, and various JS popups.
In this chapter, we will understand how to generate code using a test generator,
record and playback the script using Codegen, learn about Playwright Inspector
and how to debug using Playwright Inspector. You will also learn how to run the
test cases in UI mode and learn about trace viewer.
Structure
This chapter covers the following topics:
Understanding of record and playback script
Learn how to generate code in VS Code
Learn about test generator with Codegen
Learn about Playwright Inspector and debug script
Running tests in UI Mode
Learn about Trace Viewer
Objectives
By the end of this chapter, you will have a comprehensive understanding of
leveraging the Playwright extension in VS Code to generate the code snippet,
understanding how to generate test scripts using test generator with Codegen, and
exploring how Playwright Inspector is helpful in debugging the script.
Additionally, we will explore how to run tests in UI mode, a convenient option for
executing Playwright scripts with a graphical user interface. Finally, we will
explore the Trace Viewer, a valuable tool that provides a comprehensive view of
test traces.
Understanding of record and playback script
Before learning how to generate the test script in Playwright, let us discuss about
record and playback script. We will cover what is record and playback, and their
pros and cons.
About record and playback tools
In software testing, the term record and playback mean that code is generated
automatically when the user performs some action in UI, and code is executed
when user playback the recorded script. Using this approach, a series of activities
on an application are recorded, and test scripts are created to replicate these steps.
These scripts are then automatically run, verifying the functioning and efficiency
of the applications. Automating repetitive tests has twin advantages: one is saving
time, and other is optimizing resources. By recording the repetitive tests, testers
may complete typical test scenarios quickly, which cuts down on the amount of
time needed for manual testing.
The time-saving aspect allows us to focus more on complex, high-priority
scenarios. Testers can investigate complicated test cases requiring analytical
thinking and problem-solving abilities limited by tedious, repetitive tasks. While
there are many advantages to record and replay testing, there are also some
drawbacks. We cannot cover all scenarios by using record and playback tool,
especially the complicated scenarios which depend on dynamic data and have
conditional logic.
There are several pros and cons of the record and playback tool. In the following
section, let us explore some of the advantages and disadvantages of record and
playback tool.
Advantages of using record and playback tool
There are various advantages of using the record and playback tool. Some of them
are explained as follows:
Good start for beginners: The record and playback tool is good for
beginners. They provide an easy path of entry for test automation without
requiring an extensive amount of programming knowledge. Beginners can
quickly gain knowledge about the tool by recording the interaction with UI
in the application and generate the script. With recorded script, beginners
can learn the structure and logic behind the automated script. Recorded
script enables newcomers to get hands-on experience with automation
testing without diving deep into coding complexities.
Easy to use: Beginners do not require in-depth knowledge of programming.
With basic programming knowledge, they can record the script and easily
understand the flow of the automation script.
Quick Script creation: Record and playback tools allow creation of script
fast by recording the user interaction. This can significantly increase the
speed of the automation process by creating the script fast, especially for
repetitive tasks or regression testing.
Reduce time and effort: Record and playback tools significantly decrease
the amount of time and effort needed for testing by automating the repeating
steps. This increases overall testing efficiency and frees up testers to
concentrate on more complex testing scenarios.
Cross language support: Record and playback tools support cross-
language; using record and playback tools, we can record the script in
different languages. This flexibility allows for the creation and execution of
scripts in various languages, enabling testing and automation across different
language versions of the applications or systems.
Disadvantages of using record and playback tool
So far, we have seen the advantages of record and playback tools. However, at the
same time, there are disadvantages to using record and playback tools, which are
as follows:
Maintenance challenges: As applications change over time, script
maintenance may be difficult when using record and playback tools,
particularly when there are frequent updates or modifications to the
application we are testing. This can increase the maintenance cost and effort.
Brittle and rigid: In record and playback, scripts are generated by record
and replay tools based on how the user interacts with the application. The
recorded script can break if there is any change in the UI of the application
or underlined code.
Reusability of code: Scripts generated by record and playback tools are less
reusable across different environment. Since these tools often capture
specific user interactions, the generated scripts may not be easily adaptable
for testing different functionalities or applications across different
environments.
Limited coverage: In record and playback, scripts are recorded based on UI
interaction. The scope of DB, security, and performance testing is extremely
limited. This can result in limited test coverage, leaving important aspects of
the application untested.
Error handling: Using record and playback tools, it is not easy to handle
errors/debugging if script execution fails at some point. Robust error-
handling mechanisms are often lacking.
So far, we have seen the advantages and disadvantages of record and playback
tool. In the next section, you will see the different ways of recording the script in
Playwright.
In Playwright, there are two ways of recording the script: using VS Code by
installing the Playwright plugin, and another way is by using Codegen.
Learn how to generate code in VS Code
Before explaining how we can generate tests directly from VS Code, let us
understand how to install Playwright plugin in VS Code.
Navigate to the Extensions section and type Playwright. There are multiple
options available. Select the Playwright Test for VSCode by Microsoft option.
Click on Install. Refer to the following Figure 5.1:
Figure 5.1: Install extension
Type Ctrl +Command +P in mac and type Install Playwright. Refer to the
following Figure 5.2:
Figure 5.2: Install Playwright
Please ensure that you have checked the checkboxes for Chromium, Firefox, and
WebKit in your browser settings, as Playwright is compatible with all of these
browser engines. Additionally, make sure to select the JavaScript programming
language and then proceed to click the OK button. Refer to the following Figure
5.3:
Figure 5.3: Select browsers
We have successfully installed Playwright using the extension. Refer to the
following Figure 5.4:
Figure 5.4: Playwright successfully
Now, let us see how you can record the test in VS Code itself.
Record the test
To record a test, click on the Record new icon from the Testing sidebar. Refer to
the following Figure 5.5:
Figure 5.5: Record test
As you click on Record new button, recording will start and create a spec file with
name test-1.spec.ts. Also, a new browser window is open, as shown in the
following Figure 5.6:
Figure 5.6: Record test
In the open browser, navigate to the URL you want to test in the browser, then
begin clicking to capture your user activity.
For example, purpose let us take the example of the site
https://qaautomationlabs.com/ as we type the url and press Enter, Playwright
will capture your input and provide the test code straight within Visual Studio
Code. Refer to the following Figure 5.7:
Figure 5.7: Record steps
Record at cursor
Record at cursor is a way to capture the UI interaction at a particular step. Drag
your mouse to the desired location in the previously recorded script and click the
Record at cursor button from the Testing sidebar.
For example, in the following script, you can see that we have already recorded
some steps of the site https://qaautomationlabs.com/.
1. import { test, expect } from '@playwright/test';
2. test('test', async ({ page }) => {
3. await page.goto('https://qaautomationlabs.com/');
4. await page.locator('.hamburger').click();
5. await page.getByRole('link', { name: '/ Courses'
}).click();
6. await page.getByRole('heading', { name: 'Starting with
Cypress: A' }).click();
7. });
Now, suppose after step#3, we have to record some step; we will press enter after
line#3 and click on Record at cursor button. This is illustrated in the following
figure:
Figure 5.8: Record at cursor
New updated script after clicking on Record at cursor button. In the following
script, you can see that new lines are added after line#3:
1. import { test, expect } from '@playwright/test';
2. test('test', async ({ page }) => {
3. await page.goto('https://qaautomationlabs.com/');
4. await page.locator('.hamburger').click();
5. await page.getByRole('link', { name: '/ About'
}).click();
6. await page.locator('.hamburger').click();
7. await page.getByRole('link', { name: '/ Courses'
}).click();
8. await page.getByRole('heading', { name: 'Starting with
Cypress: A' }).click();
9. });
Pick locator
Pick locators help us to generate the locator of the element. Click on the Pick
locator button form the Testing sidebar to locate the element and capture the
locator (shown in Figure 5.9).
Let us take the example of the site https://qaautomationlabs.com/. Click on Pick
locator button and type the url. After selecting the Pick locator button from the
testing sidebar, you may highlight the locator beneath each element in the browser
window by hovering over it.
Once you click on the element you need, pick locator box will display in Pick
locator window. Refer to the following Figure 5.9:
Figure 5.9: Pick locator
Learn about test generator with Codegen
Before discussing what is Codegen and how we can record the script using
Codegen, let us understand the concept of test generator.
Test generator is the process of generating the script and maintaining end-to-end
tests for web applications. Playwright has the capacity to create tests for you while
you perform some actions in the UI. Playwright test generator save the time and
effort by generating test scripts that guarantee your online application
dependability and performance, regardless of whether you are a developer or QA
engineer.
Playwright test generator is designed to fulfil the needs of both QA and developer,
acknowledging the importance of thorough testing in the software development life
cycle. This tool is helpful in reducing the workload associated with writing and
maintaining manual scripts. By doing so, it promotes a more effective testing
workflow and improves the reliability and performance of the applications.
Here are some of the benefits of a test generator with Playwright:
Reduced code writing: By generating the code on the basis of UI
interaction, Playwright significantly reduces the effort and time required to
write the script. This helps QA and developers to focus on another important
task.
Cross language compatibility: In Playwright test generator, you can record
your script in different languages, e.g. (C#, Java, Java script, Python, and
TypeScript). It is easy for developers and QA to record the script and
understand the recorded script as per selected languages.
Fast test case creation: Using test generator, you can generate the script by
providing the inputs, actions, and expected outcomes. This speeds up the
process of developing tests, particularly in environments that use agile
development methods.
Resilient locators: Test generator lowers the risk of test flakiness by
identifying and recommending appropriate locators for user interface
elements. By suggesting reliable locators, the test generator ensures that the
automated tests run successfully without any issues.
Reusing code: We can re-use some parts of the recorded code in creating the
script, which helps in creating the script fast. This not only expedites the
testing workflow but also promotes efficiency and consistency throughout
the testing framework.
In the next section, you will see how you can generate the code using a test
generator with Codegen.
About test generator Codegen
Playwright Codegen is a helpful and effective tool provided by the Playwright that
is meant to improve the productivity of writing code for browser automation and
testing. This tool proves immensely valuable for developers and QA engineers who
want to automate the application without writing a single line of code.
Following are some key features of Playwright Codegen:
Element selection and code generation: Playwright Codegen generates
code with the necessary selectors to help in the identification and selection
of various web page items, including buttons, input fields, links, and more.
The generated code often includes selectors, such as XPath, CSS selectors,
or text content, depending on the preferences and requirements of the user.
Multiple programming languages: Codegen supports several popular
programming languages, including Python and JavaScript, Java, TypeScript,
and C#. Users have the option to choose the language that works best for
them.
Assertions generation: Playwright Codegen automatically generate
assertions which typically involve checking values, states, or elements on the
page to ensure that the application is behaving correctly.
Code customization: Users have flexibility to customize the code;
generated code from Codegen can be edit by adding assertion, condition, and
other logic to meet specific testing or automation needs.
Recording Script with Playwright Codegen
You can use codegen command followed by the URL of the website. As an
alternative, always execute the command without the URL and put it right into the
browser window.
Run the command npx playwright codegen
https://qaautomationlabs.com/ to start Playwright Codegen tool. As we
press Enter, two windows are displayed. First, with Playwright window and, in
another window, browser open with provided URL. Refer to the following Figure
5.10:
Figure 5.10: Playwright Codegen
To learn more about Playwright Codegen, record some scenarios and see how code
is generated for the interaction that we do in UI. Before recording the scenario, let
us see the various commands using Codegen that we can use in different scenarios:
npx playwright codegen https://qaautomationlabs.com/:
This command will open Playwright window with browser, navigate to the
specified URL.
npx playwright codegen -b firefox
https://qaautomationlabs.com/:
This command will open Playwright window with firefox browser, navigate
to the specified URL.
npx playwright codegen --device "Pixel 5"
https://qaautomationlabs.com/:
This command will open Playwright window Pixel 5 android device.
npx playwright codegen --viewport-size "900, 400"
https://qaautomationlabs.com/:
This command will open Playwright window in "900, 400"view port in
android device.
npx playwright codegen --color-scheme=dark
https://qaautomationlabs.com/:
This command records scripts with emulating the color scheme=dark.
Steps for recording test in Playwright
In the following steps, we will cover how to record test using Codegen. For demo
purposes, we are taking reference from the site https://ecommerce-
playground.lambdatest.io/.
1. Open the above URL.
2. Click on My Account.
3. Enter a valid username.
4. Enter a valid password.
5. Click on Login button.
6. Click on Logout.
7. Verify user logout from the application with message “Account Logout”.
Run the command npx playwright codegen https://ecommerce-
playground.lambdatest.io to start Playwright Codegen tool.
This command opens a new browser and the Playwright inspector. The browser
opens with the provided url and the Playwright Inspector is on a new page, ready
to listen to whatever action you perform in the browser. From the Playwright
Inspector, you can select the language in which you want to record the script. In
our case, we are recording script in JavaScript language. Refer to the Figure 5.11:
Figure 5.11: Playwright Inspector
In the following figure in tooltip, you can see recorded selector is displays by
Playwright:
Figure 5.12: Recorded Selector
The following code is generated for above scenario:
1. import { test, expect } from '@playwright/test';
2. test('test', async ({ page }) => {
3. await page.goto('https://ecommerce-
playground.lambdatest.io/');
4. await page.getByRole('button', { name: 'My account'
}).click();
5. await page.getByPlaceholder('E-Mail Address').click();
6. await page.getByPlaceholder('E-Mail
Address').fill('lambdatestnew@yopmail.com');
7. await page.getByPlaceholder('Password').click();
8. await
page.getByPlaceholder('Password').fill('Lambda123');
9. await page.getByRole('button', { name: 'Login'
}).click();
10. await page.getByRole('link', { name: 'Logout'
}).click();
11. await page.getByRole('heading', { name: 'Account
Logout' }).click();
12. });
Copy the above script and paste it under the path, e.g.,
tests/lambdatest.spec.js, and execute the recorded script. In the following
figure, you can see the recorded script successfully passed:
Figure 5.13: Executed test case
Playwright will capture your movements and provide the test code straight within
VS Code. In Playwright release version 1.40, you can generate the script for
assertion, too. Just choose the element on which you want to apply the assertion.
Choose the appropriate icon and hover over the element assertion, which is
automatically captured in the script. In the next section, you will see all type
assertion that Playwright support during recording the script using Codegen.
The following assertions can be generated:
assert visibility to assert that an element is visible.
assert text to assert that an element contains specific text.
assert value to assert that an element has a specific value.
To explain the above assertion, let us take an example of https://ecommerce-
playground.lambdatest.io/. In the following points, we are covering all three
assertions.
1. Open the above URL.
2. Click on the search box.
3. Enter text ‘apple’.
4. Use assert visibility to verify the image exists.
5. Click on the first image.
6. Use assert text to verify the text.
7. Finally, verify the default Qty in the cart using the assertion assert value.
Let us record the assertion as well. In Figure 5.14, you can see all three assertions
assert visibility, assert text, assert value the icons are displaying.
To capture the different assertion, click on the respective icon and apply the
assertion. One icon Assert visibility highlighted below other two can be seen in
the figure:
Figure 5.14: Assertion
The following script is generated along with the different assertions. As you can
see in line #7, assert visibility assertion is used to verify whether the image exists
or does not exist. In line#9, assertion assert text is used to verify the text, and
finally, in line#10 assertion assert value used to verify the default quantity i.e. ‘1’:
1. import { test, expect } from '@playwright/test';
2. test('test', async ({ page }) => {
3. await page.goto('https://ecommerce-
playground.lambdatest.io/');
4. await page.getByRole('textbox', { name: 'Search For
Products' }).click();
5. await page.getByRole('textbox', { name: 'Search For
Products' }).fill('apple');
6. await page.getByRole('button', { name: 'Search'
}).click();
7. await expect(page.locator('#mz-product-grid-image-34-
212469')).toBeVisible();
8. await page.locator('#mz-product-grid-image-34-
212469').click();
9. await expect(page.locator('h1')).toContainText('iPod
Shuffle');
10. await expect(page.getByRole('spinbutton', { name: 'Qty'
})).toHaveValue('1');
11. });
In the next section, we will see how to debug the script in Playwright.
Learn about Playwright Inspector and debug script
Debugging scripts are a vital part of software development as it helps in finding
and solving problems, boost output, and make sure a program works as intended.
Debugging not only allows for error rectification but also provides performance
improvements for the code. Developers may discover inefficient methods, needless
computations, or bottlenecks during debugging. By quickly detecting and fixing
problems, developers may reduce the likelihood of new defects and preserve clean,
readable, and maintainable code. In Playwright, there are two ways of debugging
the script, which are as follows:
Debug playwright scripts using VS Code: In VS Code, you can debug
your test script by using the VS Code plugin. You can walk through your
tests, create breakpoints, and view error messages.
Set breakpoints at any line in the script, then right-click and select the option
Debug Test, as shown in the following figure:
Figure 5.15: Debug Test
In the following figure, the browser window will open, and script execution
will stop at the breakpoint. After this line, move further and click on Step
Over icon:
Figure 5.16: Debug script
Using Step Over, we can move through the script line by line. If there is any
issue in the script, it will stop executing further. In the following figure, you
can see that the script fails at line#9 because the expected string varies from
the actual one:
Figure 5.17: Error in debug
As we found the issue in line #9, we can fix it easily. This debugging is very
effective when we have large lines of code.
Debug Playwright scripts using Playwright Inspector: This is another
way of debugging the script using Playwright Inspector.
Playwright Inspector is an important GUI tool designed specifically for
debugging Playwright tests, with a feature set that will help you get started
faster. With this graphical interface, users may completely evaluate their
tests and watch the code's step-by-step execution. Playwrights have the
capability to allow users to edit the locators, enabling them to modify
element locators in real-time while debugging. Furthermore, actionability
logs are visible through the Playwright Inspector, giving users a better
understanding of the execution process and the ability to spot any issues or
bottlenecks. Through the user-friendly presentation of actionable logs, the
tool helps identify and resolve problems quickly.
To debug the script in Playwright Inspector, you have to pass --debug flag in
the command to open the inspector. There are various way to pass --debug
flag in the command. The following commands can be used to debug the test
cases with various parameters:
Debug all test: By using this command, each test will be executed
individually, opening a browser window and the inspector for each.
npx playwright test –debug
Debug particular test: This command will debug a single test in each
browser as per the configuration done in playwright.config file.
npx playwright test lambdatest.spec.js --debug
Debug test on browser: This command debugs test in a particular
browser. Once configured, you can debug tests on a specific browser or
mobile viewport using the --project flag.
npx playwright test --project=" chromium" --debug
npx playwright test --project="Microsoft Edge" --
debug
Debug particular test on browser: This command debugs particular test
in a single browser.
npx playwright test lambdatest.spec.js --
project=chromium –debug
In the following figure, you can see the Inspector's toolbar at the top allows you to
play, pause, or walk through each test one by one. The element that matches the
action that is now highlighted in the code is highlighted in the browser window. In
the log tab, you can see details about whether the element is visible, enabled, and
stable. This is illustrated in the following Figure 5.18:
Figure 5.18: Playwright Inspector debugging
Running tests in UI Mode
It was a little challenging to view the active test case in the user interface of earlier
versions of Playwright. Playwrights launch UI mode in version 1.32. UI Mode
allows you to navigate, execute, and troubleshoot tests using a time-travel
experience enhanced with watch mode. You may expand the test files in the testing
sidebar by clicking on the corresponding block, then run, inspect, monitor, and
debug each test separately.
The UI Mode in Playwright helps you to:
Execute individual tests or all of them at once.
Browse your test files and see their structure.
View detailed results for each test, including logs, DOM snapshots, and trace
information.
Hover over any point in the test to see what was happening at that moment.
Filter tests by text or @tag or bypassed, failed, and skipped tests.
The following command is used to run the test case in UI Mode in Playwright:
npx playwright test –ui
All your test files will be listed when launching the Playwright UI Mode. Clicking
the triangle symbol in the sidebar will launch all your tests. Hovering over the
name and selecting the triangle next to it will also allow you to run a single test
file, a block of tests. Refer to Figure 5.19:
Figure 5.19: UI Mode
There are different tabs in UI Mode, and each tab has its own purpose:
Actions: In the Actions tab, you can see all the steps of the test cases. By
hovering over the steps, you can see the change in the DOM snapshot.
Metadata: The metadata tabs consist of details of Browser, Time, Viewport
detail etc.
Locator: In the locator tab, you can capture the locator of the element by
clicking on Pick Locator icon.
Source: Source tabs consist of the complete code of the particular test case.
Call: This tab shows you information about the action, such as the time it
took, and which locator was used, etc.
Log: In Log tab, you can View the whole test log to have a better
understanding of the operations that Playwright does in the background,
such as scrolling into view, waiting for an element to become visible,
enabled, and stable, and clicking, filling, pressing, and so on.
Errors: In the error tab, you see the detail of the error when some test case
is failed.
Console: In the console tab, you can view console logs from your test and
from the browser. To indicate whether the console log originated from the
test file or the browser, different symbols are shown.
Network: All the network requests performed during your test are displayed
in the Network tab. You can sort the request in the network tab by types,
status codes, methods, content kinds, durations, and sizes.
Attachment: By choosing the Attachments option, you may review
attachments. When conducting visual regression testing, you may compare
screenshots by examining the image diff, the real image, and the expected
image.
Learn about Trace Viewer
The Playwright Trace Viewer allows you to examine recorded Playwright traces
once the script has finished running. There are two ways of recording a trace: one
is locally, and the other is recording a trace on CI.
Recording a trace locally
To record a trace during development mode, pass flag --trace to on your tests. You
have to use the following command to run the script with trace.
npx playwright test --trace on
Once the above command is executed, run the command npx playwright show-
report to open the html report with Trace. Refer to Figure 5.20:
Figure 5.20: Traces
By clicking on trace icon from the above figure, you can open the trace logs. Refer
to Figure 5.21:
Figure 5.21: Trace Logs
In the above figure, when you click on the expected image, you can use the slider
to slide one image over the other. This way, you can easily see the differences in
the before-and-after screenshots.
Recording a trace on CI
Recording traces in Playwright on CI can be beneficial for debugging failing tests.
Traces should be run during continuous integration on the first try to retry a failed
test using the trace: 'on-first-retry' option in the test configuration file. Setting
trace: 'on-first-retry' in configuration will produce a trace.zip file for each
test that was retried.
We can open the trace.zip file using the command npx playwright show-trace
path/to/trace.zip. Make sure to add the full path to where your trace.zip file is
located.
We can pass other option in trace under configuration file playwright.config.js.
We can pass the following options in playwright.config.js configuration file:
'on-first-retry': Capture a trace exclusively during the initial retry of a
test.
'on-all-retries': Record traces for every retry attempt of a test.
'off': Do not record any trace during test execution.
'on': Record a trace for each test run. (not recommended as it is
performance-heavy).
'retain-on-failure': Record the trace for every test but discard it from
successful test runs.
Conclusion
This chapter equipped readers with a well-rounded understanding of record and
playback, code generation, test generation with Codegen, Playwright Inspector for
debugging, UI mode execution, and the Trace Viewer for insightful analysis.
In the next chapter, we will explore about the reporters', hooks, annotations, and
assertions. Playwright offers built-in and custom reporters to present test results,
hooks to manage test setup, teardown, and execution flow. Annotations can define
test timeouts, specify fixtures to inject, and even mark tests as skipped. Finally, we
end the chapter with assertions that can be used to check element visibility, text
content, attribute values, and more, ensuring your tests validate functionality.
Multiple choice questions
1. What is the primary purpose of record and playback in test
automation?
a. To manually write code for each test case
b. To automatically generate code by recording user interactions
c. To simulate user actions during testing
d. To analyze the performance metrics of the application
2. What is the purpose of a test generator with Codegen?
a. To analyze test results
b. To generate code for automated tests
c. To optimize code execution speed
d. To debug code in real-time
3. Which tool is commonly used for inspecting and debugging Playwright
scripts?
a. Browser developer tools
b. Visual Studio debugger
c. Playwright Inspector
d. Codegen debugger
4. What is the advantage of running tests in UI mode?
a. Faster execution speed
b. Requires less memory
c. Suitable only for small test suites
d. Simulates user interactions more accurately
5. What is the role of Trace Viewer in script development?
a. It allows you to view and analyze test traces
b. It generates code snippets
c. It records user interactions
d. It is used for debugging purposes
Answers
1. b.
2. b.
3. c.
4. d.
5. a.
Join our book’s Discord space
Join the book's Discord Workspace for Latest updates, Offers, Tech happenings
around the world, New Release and Sessions with the Authors:
https://discord.bpbonline.com
CHAPTER 6
Reporter, Assertion, Annotations, and
Hooks in Playwright
Introduction
In Chapter 5, Exploring Playwright Tools in Depth, we learned how to generate code
using test generator, record, and playback the script using Codegen. You have also
learned about Playwright inspector, how to debug using Playwright Inspector. We
learned about how to run the test cases in UI mode and, finally, about trace viewer. In
this chapter, we will look into different types of reports in Playwright. You will learn
about assertions and their types with examples. You will also learn about annotations
and finally see the various available hooks in Playwright.
Structure
This chapter covers the following topics:
Reporters in Playwright
Learn about assertions in Playwright
Annotations in Playwright
Learn about hooks in Playwright
Objectives
By the end of this chapter, you will have a comprehensive understanding of the
different types of reporters in Playwright, examining how they contribute to effective
testing and reporting processes. Gain a thorough understanding of hooks in Playwright
and their role in enhancing the flexibility and customization of test scripts. Grasp the
concept of assertions within the Playwright framework, learning how to use them
effectively to validate expected outcomes and ensure the reliability of test results.
Finally, you will learn how test annotations effectively handle issues such as failures,
flakiness, skipping tests, focusing on specific tests, and tagging tests for better
organization and management.
Reporters in Playwright
Reporting is one of the key components of any tool, and from automation perspective
reporting plays a vital role in the analysis of the execution of test script. A well-written
report presents statistics as well as simplifies complicated information in a way that is
easy to understand and use. Playwrights support multiple types of reporting to generate
reports in various formats according to your requirements and use cases. Playwright
test comes with in-built, custom, and third-party reporter, which allows you to generate
reports in a variety of patterns with great flexibility. Due to its adaptability, users may
generate reports in a variety of formats, ensuring precision and freedom in the
dissemination of results from automated test runs.
Various benefits of a well-constructed report are mentioned as follows:
Test execution visibility: Well-constructed reports provide a comprehensive
view of test execution report with details that indicate whether a test case is
passed, failed, skipped, and showcase any issue that we encounter during the
execution.
Debugging: Nowadays, most reporters support screenshots and videos, making it
easy for the QA team to debug failed test cases. Robust error messages and
comprehensive logs provide effective debugging, allowing teams to quickly
resolve problems.
Fast issue identification: Good report helps identify the issues fast. Using the
report in Playwright can provide detailed information on the failure, enabling
quick identification and resolution of issues.
User-friendly interface: An effective reporting interface improves the user
experience by making it simple for team members to explore and retrieve the
necessary data. The overall efficacy of a reporting system is enhanced by its user-
friendliness.
Historical analysis: Report helps in analyzing the historical records of test case
execution. Historical data can help in trend analysis, identifying patterns in
failures, and understanding the overall health of the application over time.
Performance monitoring: In most of the tools, we have past reports of test case
execution; this helps you check and compare the performance of test cases with
the latest execution, allowing teams to monitor the performance of their test suite
and identify potential bottlenecks.
Continuous integration/continuous delivery (CI/CD): Automated tests are
often executed as part of the pipeline in CI/CD setups. Test automation reports
are essential to the CI/CD process because they offer an unbiased assessment of
the quality of codes and assist teams in deciding whether to move on with the
deployment or not.
Playwrights provide various in-built reporters; all built-in reporters provide
comprehensive details about failures, and mostly differ in verbosity for successful runs.
The built-in reporters are as follows:
List reporter
Line reporter
Dot reporter
HTML reporter
JSON reporter
JUnit reporter
GitHub Actions annotations
Before explaining the above reports, let us first set up the Playwright for demo
purposes. We are taking the reference from this site: https://ecommerce-
playground.lambdatest.io/ and covering the following scenario:
Open the URL https://ecommerce-playground.lambdatest.io/.
Click on My Account.
Enter a valid username.
Enter a valid password.
Click on Login button.
Click on Logout.
Verify user logout from the application with the message Account Logout.
There are two ways by which we can use each reporter: one is by doing configuring in
config file another way is to use command line by passing the report type. In the
following section, you will see both the configuration with an example of each built-in
reporter:
List reporter: List reporter is the type of report that you can use to display your
test case execution result; it will display test names and their status (pass or fail)
in a list manner for presenting test result. There are two ways in which we can
use list reporter, and users have the option to choose between the two available
approaches:
One is by passing list in playwright.config.ts file, as seen in the following
input:
import { defineConfig } from ‘@playwright/test’;
export default defineConfig({
reporter: ‘list’,
});
Another way is passing parameter list in the command line as follows:
npx playwright test –reporter=list
You have seen how to set up the reporter list now; let us execute the preceding
scenario. In the following output of the execution of the test cases, you can see
the execution of two test cases displayed in list format. One test case is passed,
and the other test case is failing.
Figure 6.1: List reporter
Line reporter: Line reporter is a type of report that you can use to display test
results in a simple list format for showing the status of each test case. It will
display test names and their status (pass or fail) in single line output for
presenting test results.
There are two ways by which we can use line reporter, and Users have the
option to choose between the two available approaches. One is by passing
line in playwright.config.ts file as follows:
import { defineConfig } from '@playwright/test';
export default defineConfig({
reporter: 'line',
});
Another way is passing the parameter line in the command line as follows:
npx playwright test --reporter=line
You have seen how to set up the reporter line now; let us execute the above-
mentioned scenario. The subsequent figure displays the output of the execution
of the test cases where you can see the execution of two test cases displayed in
line format: one test case is passed, and the other test case is failing.
Figure 6.2: Line reporter
Dot reporter: Dot reporter is a type of report that you can use to display test
results with dots, where each dot represents a passed test. It will display test
names and their status (pass or fail) in dotted manner for presenting test results.
There are two ways by which we can use line reporter, and users have the
option to choose between the two available approaches. One is by passing dot
in playwright.config.ts file, which is shown as follows:
import { defineConfig } from '@playwright/test';
export default defineConfig({
reporter: 'dot',
});
Another way is passing parameter dot in the command line:
npx playwright test --reporter=dot
You have seen how to set up the reporter dot. Now, let us execute the above-
mentioned scenario. Let us execute the preceding scenario. The following figure
showcases the output of the execution of test cases where you can see execution
of two test cases displaying in dot format where one test case is passing, and
other test case is failing. Passed test case displaying with green dot(.) and failed
test case displaying with red color (T). Refer to the following Figure 6.3:
Figure 6.3: Dot reporter
HTML reporter: HTML reporter is a type of report that you can use to display
your test case execution result. It will display test names and their status (pass or
fail) in html format for presenting test results.
There are two ways by which we can use html reporter, and users have the
option to choose between the two available approaches. One is by passing
html in playwright.config.ts file as follows:
import { defineConfig } from '@playwright/test';
export default defineConfig({
reporter: 'html',
});
import { defineConfig } from '@playwright/test';
export default defineConfig({
reporter: 'html',
});
Another way is passing parameter html in the command line as follows:
npx playwright test --reporter=html
You have seen how to set up the reporter html now let us execute the above-
mentioned scenario. As test case execution is completed, it opens the html report
in a new tab. The following figure displays an output of the execution of test
cases where you can see the execution of two test cases displaying in html format
—one test case is passed, and the other is failing:
Figure 6.4: HTML reporter
Also, playwright-report folder is created with index.html file under the project.
index.html which contains all the details of test case execution along with Pass/Fail
and others. Refer to the following Figure 6.5:
Figure 6.5: Index.html
JSON reporter: JSON reporter is type of report that you can use to display your
test case execution result, it will display test names and their status (pass or fail)
in JSON format for presenting test results. JSON reporter produces an object
with all information about the test run.
There are two ways by which we can use JSON reporter, and users have the
option to choose between the two available approaches. One is by passing
json in playwright.config.ts file as follows:
import { defineConfig } from '@playwright/test';
export default defineConfig({
reporter: [['json', { outputFile: 'results.json' }]],
});
Another way is passing parameter json in the command line as follows:
npx playwright test --reporter=json
Now that you have seen how to set up the reporter json, let us execute the
preceding scenario. As test case execution is completed, you can see
results.json is created in the root folder. Refer to the following Figure 6.6:
Figure 6.6: JSON reporter
When you run the command npx playwright test --reporter=json in the
terminal, it will display the JSON in the output/terminal. Refer to the following
Figure 6.7:
Figure 6.7: JSON format
JUnit reporter: JUnit reporter is type of report that you can use to display test
results according to the JUnit XML schema. This format is commonly used for
integrating test reports with continuous integration systems.
There are two ways by which we can use JUnit reporter, and users have the
option to choose between the two available approaches. One is by passing
junit in playwright.config.ts file as follows:
import { defineConfig } from '@playwright/test';
export default defineConfig({
reporter: [['junit', { outputFile: 'results.xml'}]],
});
Let us execute the preceding scenario. As test case execution is completed,
you can see results.xml is created in the root folder. Refer to the following
Figure 6.8:
Figure 6.8: Results.xml
Another way is passing parameter junit in the command line as follows:
npx playwright test --reporter=junit
When you run the command npx playwright test --reporter=junit in the
terminal, it will display the JSON in the output/terminal. Refer to the following
Figure 6.9:
Figure 6.9: JUnit reporter
GitHub Actions annotations: GitHub actions may be used to automatically
generate failure annotations by using the built-in GitHub reporter. This reporter
likely annotates the GitHub Actions build log with information about test results.
This can be helpful for identifying issues directly within the GitHub interface.
We have to do the following setting in playwright.config.js file:
import { defineConfig } from '@playwright/test';
export default defineConfig({
// 'github' for GitHub Actions CI to generate
annotations, plus a concise 'dot'
// default 'list' when running locally
reporter: process.env.CI ? 'github' : 'list',
});
Third-party reporter
Playwright supports various third-party reporters. Here, we are discussing the Allure
report in detail. Allure Report is a flexible, lightweight, and multi-language test report
tool that provides clear graphical reports. It helps in understanding the test results by
providing detailed insights into the test execution.
Reasons to use Allure Report
Allure Report is an open-source, multi-language test reporting tool that provides a clear
and concise way to present test results, making it easier for both technical and non-
technical stakeholders to understand the quality and status of a software project.
Here are some reasons why Allure Reports are commonly used in software
development and testing:
Rich and interactive reports: Allure Reports generate visually appealing and
interactive reports that include detailed information about test executions. The
reports often include charts, graphs, and other visual elements that make it easy
to identify trends, patterns, and issues.
Ease of use: Allure Reports are user-friendly and can be easily understood by
individuals with varying levels of technical expertise. The reports provide a high-
level overview as well as detailed information, catering to both management and
technical teams.
Integration with test frameworks:
Allure Reports can be integrated with popular testing frameworks such as JUnit,
TestNG, Cucumber, and more. This allows teams to continue using their
preferred testing tools while benefiting from Allure's reporting capabilities.
HTML and XML formats: Allure Reports are generated in HTML and XML
formats, making them easily shareable and accessible across different platforms.
The HTML format is particularly useful for viewing reports in web browsers.
Screenshots and attachments: Allure allows for the attachment of screenshots
and other files to test reports, making it easier to understand and debug test
failures.
Setting up Allure Report
We have seen the benefits of using Allure Report. In this section, you will see how we
set up Allure Reports:
1. The first step is to install Allure for Playwright using the following command:
npm i -D @playwright/test allure-playwright
OR
yarn add @playwright/test allure-playwright –dev
2. The next step is to install Allure Command Line utility using the following
command:
npm install allure-commandline --save-dev
In the following figure, you can see that both utilities mentioned in the preceding
section are installed. Refer to Figure 6.10:
Figure 6.10: Package.json
3. Execute test cases and generate Allure Report.
a. In order to create an Allure Report, we must take the following actions into
account:
i. In the command npx playwright test, we must pass reporter=line,
which will produce a report in plain text format.
ii. After receiving data in plain text (number of test cases passed/failed),
Allure uses this data to build HTML report.
b. Run the following command to generate report in plain text:
npx playwright test --reporter=line,allure-playwright
c. Once test execution is done, you should see a folder with the name allure-
results. Refer to the following Figure 6.11:
Figure 6.11: allure-results
d. Now you can generate an HTML report using the following plain text file
generated under allure result:
npx allure generate ./allure-results
e. As we run the above command, it will generate the folder allure-report
with index.html file. Refer to the following Figure 6.12:
Figure 6.12: index.html
f. Run the following command to open allure HTML report:
npx allure open ./allure-report
g. As we run the above commands, it will open the Allure Report. Refer to
Figure 6.13 and Figure 6.14:
Figure 6.13: Allure Report
In the following figure, you can see the Allure Report in detail with number of pass test
cases and other details:
Figure 6.14: Allure Detail Report
Learn about assertions in Playwright
Assertions in testing are conditions that the tester assumes to be true in order to verify
the correctness of the application being tested. These assertions are used to check
whether the actual results of a test match the expected results. The primary purpose of
assertions is to identify issues or bugs in the software by comparing the actual and
expected behavior. Assertions in Playwright are used to validate whether certain
conditions are met during the execution of a test. Playwright, being a browser
automation library, allows you to perform actions on web pages and then assert that the
expected state or behavior is achieved.
Need for assertions
Assertions are statements or declarations that assert or claim a fact. In software
development, specifically in programming, assertions play a crucial role in ensuring the
correctness and reliability of code. Here are some reasons why assertions are important:
Bug detection: Assertions help in identifying and detecting bugs early in the
development process. By incorporating assertions that represent expected
program states, developers can quickly catch and address issues during testing
and debugging.
Control flow execution: Assertions can be used to control the flow of test
execution. For example, if a critical assertion fails, it might be desirable to stop
the test execution immediately rather than continue with subsequent steps.
Avoid silent failures: In the absence of assertions, code may behave
unexpectedly or fail without warning, which might result in silent failures.
Assertions make these failures explicit, drawing attention to them during
development and testing.
Help in debugging: Assertions help in identifying and locating bugs or errors in
the code. By including assertions in the code, developers can express
assumptions about the state of the program at a particular point. If these
assumptions are violated during execution, an assertion failure is triggered,
helping developers quickly pinpoint the issue.
Runtime checks: Assertions can be used for runtime checks to catch unexpected
conditions that may lead to undefined behavior. For example, checking for null
references before dereferencing them can prevent null pointer exceptions.
Performance monitoring: Assertions can be employed to monitor the
performance of a program. By including assertions related to performance
metrics, developers can detect and handle performance-related issues during
development or testing.
In the next section, you will learn about the types of assertion. In high-level, there are
two main types of assertions: hard assertions and soft assertions. These assertion types
can be further categorized based on their behavior and impact during the execution of
automated tests.
About hard and soft assertions
Hard assertions are assertions that, when they fail, immediately stop the execution of
the test script. In other words, if a hard assertion fails, the test will be marked as failed,
and the subsequent code after the failed assertion will not be executed.
Soft assertions, on the other hand, allow the test script to continue executing even if an
assertion fails. Soft assertions collect information about assertion failures but do not
immediately terminate the test. This can be useful when you want to capture multiple
issues in a single test run before marking the test as failed.
The following table lists all characteristic differences between hard and soft assertion:
Characteristic Hard assertions Soft assertions
Stops test execution immediately Continues test execution even if an assertion
Execution behavior
upon failure. fails.
Marks the entire test as failed after Does not mark the entire test as failed on
Test termination
the first failure. failure.
Isolates the first failure, making Allows the test to continue running, collecting
Isolation of failures
subsequent code unreachable. information on multiple failures.
Simplifies debugging by pinpointing May require additional effort to identify all
Debugging
the exact location of the first failure. failures, as the test continues running.
Suitable for critical checks where Useful when you want to capture multiple
Use cases subsequent steps depend on the issues within a single test run before marking
success of the current one. the test as failed.
Table 6.1 : Hard and soft assertions
These characteristics highlight the contrasting behaviors of hard and soft assertions,
providing flexibility in how test failures are handled during the execution of automated
tests. Developers and testers can choose the appropriate assertion type based on the
specific requirements of their test scenarios.
Assertions in Playwright
Playwright is a Node library for automating browsers, which means it allows you to
automate tasks in web browsers programmatically. When it comes to assertions in
Playwright, they are typically used to verify the expected state of the web page or
application during a test.
In the next section, different types of assertions in Playwright are explained in detail.
Auto-retrying assertions
Auto-retrying assertions in Playwright are typically considered a form of hard
assertion. In hard assertions, the test can fail immediately when the specified condition
is not met. In the case of auto-retrying assertions in Playwright, the assertion will keep
retrying until either the condition is met, or a timeout is reached. Once the condition is
met, the test proceeds. If the timeout is reached without the condition being satisfied,
the test will fail immediately.
Here is a tabular representation of the provided auto-retrying assertions in Playwright:
Assertion Description
await expect(locator).toBeAttached() To verify element is attached
await expect(locator).toBeChecked() To verify checkbox is checked
await expect(locator).toBeDisabled() To verify element is disabled
await expect(locator).toBeEditable() To verify element is editable
await expect(locator).toBeEmpty() To verify container is empty
await expect(locator).toBeEnabled() To verify element is enabled
await expect(locator).toBeFocused() To verify element is focused
await expect(locator).toBeHidden() To verify element is not visible
await expect(locator).toBeInViewport() To verify element intersects viewport
await expect(locator).toBeVisible() To verify element is visible
await expect(locator).toContainText() To verify element contains text
await expect(locator).toHaveAttribute() To verify element has a DOM attribute
await expect(locator).toHaveClass() To verify element has a class property
await expect(locator).toHaveCount() To verify list has an exact number of children
await expect(locator).toHaveCSS() To verify element has a CSS property
await expect(locator).toHaveId() To verify element has an ID
await expect(locator).toHaveJSProperty() To verify element has a JavaScript property
await expect(locator).toHaveScreenshot() To verify element has a screenshot
await expect(locator).toHaveText() To verify element matches text
await expect(locator).toHaveValue() To verify input has a value
await expect(locator).toHaveValues() To verify select has options selected
await expect(page).toHaveScreenshot() To verify page has a screenshot
await expect(page).toHaveTitle() To verify page has a title
await expect(page).toHaveURL() To verify page has a URL
await expect(response).toBeOK() To verify response has an OK status
Table 6.2 : Assertion with description
To explain some of the preceding assertions, let us take assistance of the site:
https://ecommerce-playground.lambdatest.io/index.php?route=account/register in
the following code, which covers most of the assertions:
import { test, expect } from '@playwright/test';
test('Registration Form', async ({ page }) => {
await page.goto('https://ecommerce-
playground.lambdatest.io/index.php?route=account/register');
const cickPrivacyCheckbox = page.locator("//label[@for='input-
agree']");
await cickPrivacyCheckbox.click();
//To be checked
await expect(cickPrivacyCheckbox).toBeChecked();
const firstName = page.locator("//input[@id='input-
firstname']");
//To be disabled
await expect(firstName).not.toBeDisabled();
const clickContinueBtn =
page.locator("input[value='Continue']");
//To be Editable
await expect(firstName).toBeEditable();
//To be Enabled
await expect(clickContinueBtn).toBeEnabled();
const focus = "//input[@id='input-firstname']";
await page.locator(focus).focus();
//To be Focused
await expect(page.locator(focus)).toBeFocused();
const loginVisible = page.locator("aside[id='column-right']
a:nth-child(1)");
//To be Visible
await expect(loginVisible).toBeVisible();
await clickContinueBtn.click();
const warningMsg = page.locator("//div[contains(text(),'First
Name must be between 1 and 32 characters!')]");
//To Contain Text
await expect(warningMsg).toContainText("First Name must be
between 1 and 32 characters!");
const newsLetterSubscribe = page.locator("#input-newsletter-
no");
//To Have Attribute
await expect(newsLetterSubscribe).toHaveAttribute("type",
"radio");
const h1 = page.locator("//h1");
//To Have Class
await expect(h1).toHaveClass("page-title h3");
const formControl = page.locator("//input[@class='form-
control']");
//To Have Count
await expect(formControl).toHaveCount(6);
const firstname = page.locator("#input-firstname");
//To Have CSS
await expect(firstname).toHaveCSS("display", "block");
const firstNameid = page.locator("//input[@name='firstname']");
//To Have ID
await expect(firstNameid).toHaveId("input-firstname");
//To Have Text
const firstNamelable = page.locator("label[for='input-
firstname']");
await expect(firstNamelable).toHaveText("First Name");
const newsletterYesRadio = page.locator("#input-newsletter-
yes");
//To Have Value
await expect(newsletterYesRadio).toHaveValue("1");
//To have Title
await expect(page).toHaveTitle("Register Account");
//To have URL
await expect(page).toHaveURL(/register/);
});
The execution results of different types of assertion are shown in Figure 6.15:
Figure 6.15: Assertion result
Soft assertions
Another type of assertion in Playwright is soft assertion. If a soft assertion fails, the test
is marked as unsuccessful rather than being terminated. To explain soft assertion, let us
take an example using the site https://ecommerce-playground.lambdatest.io/.
In the following code, we are using expect.soft to check whether a warning message
contains the expected text- First Name must be between 1 and 33 characters! We
have a hard assertion using expect(page).toHaveTitle("Register Account"). This
checks that the page has the title "Register Account".
You can see we have intentionally failed assertion expect.soft(), and as we run this
code, it allows the execution of a test to continue even after encountering a verification
failure. This is depicted in the following code:
import { test, expect } from '@playwright/test';
test('Soft assertion Example ', async ({ page }) => {
const clickContinueBtn =
page.locator("input[value='Continue']");
await page.goto('https://ecommerce-
playground.lambdatest.io/index.php?route=account/register');
await clickContinueBtn.click();
const warningMsg = page.locator("//div[contains(text(),'First
Name must be between 1 and 32 characters!')]");
await expect.soft(warningMsg).toContainText("First Name must
be between 1 and 33 characters!");
await expect(page).toHaveTitle("Register Account");
});
In Figure 6.16, you can see the soft assertion failing, but still, we can verify the title of
the page in the console:
Figure 6.16: Soft assertion
We have seen different types of assertions until now. In the next section, you will learn
about annotations in Playwright.
Annotations in Playwright
Annotations in Playwright test provide a way to manage and handle various aspects of
tests, such as failures, flakiness, skipping, focusing, and tagging.
Explanations for each annotation with examples are given in the subsequent section:
test.skip()
Purpose: Marks the test as irrelevant, indicating that it should not be executed
by Playwright test.
Use case: When the test is not applicable in certain configurations or
scenarios.
import { test, expect } from '@playwright/test';
test.skip('skip this test', async ({ page }) => {
// This test will not run
});
test.fail()
Purpose: Marks the test as failing, and Playwright test ensures that it does
indeed fail during execution. If the test does not fail, Playwright Test raises an
issue.
Use case: Useful for explicitly identifying tests that are expected to fail and
checking if they behave as expected.
import { test, expect } from '@playwright/test';
test('not yet ready', async ({ page }) => {
test.fail();
// ...
});
test.fixme()
Purpose: Marks the test as failing, but Playwright test does not execute this
test. This is in contrast to test.fail(), which runs the test even if it's expected
to fail.
Use case: Suitable when running the test is slow or prone to crashes, and
executing it every time is not practical.
import { test, expect } from '@playwright/test';
test.fixme('test to be fixed', async ({ page }) => {
// ...
});
test.slow()
Purpose: Marks the test as slow and triples the test timeout. This allows for
accommodating longer execution times for slower tests.
Use case: Use this annotation when a test is inherently slow, and you want to
adjust the timeout accordingly to prevent premature failures.
import { test, expect } from '@playwright/test';
test('slow test', async ({ page }) => {
test.slow();
// ...
});
Group tests: In Playwright, tests can be grouped with logical names; this is
particularly useful for streamlining setup and teardown procedures for specific
test groups, enhancing code structure and maintainability in the testing process.
Take a look at the following example:
import { test, expect } from '@playwright/test';
test.describe('two tests', () => {
test('one', async ({ page }) => {
// ...
});
test('two', async ({ page }) => {
// ...
});
});
Tags tests: Playwright allows you to tag tests as either @smoke or @regression.
Use the command line options --grep and --grep-invert to run tests with
particular tags only. This allows for efficient test categorization and targeted
execution based on tags, enhancing test customization and execution control.
Take a look at the following example:
import { test, expect } from '@playwright/test';
test('Test login page @smoke', async ({ page }) => {
// ...
});
test('Test the Complete Scenario of Login with all
scenario’s report @regression', async ({ page }) => {
// ...
});
To run the above code, use the command npx playwright test --grep @smoke
This command only runs smoke test cases.
Use the command npx playwright test --grep "@smoke|@regression" to
execute the test cases containing either tag (logical OR operator).
If you want tests with a specific tag that can be skipped, use this command: npx
playwright test --grep-invert @slow.
Learn about hooks in Playwright
Hooks are functions that let you execute setup and takedown code at different stages of
the test process. In Playwright, we have various hooks, they are listed as follows:
test.beforeEach(): This hook executes before each test in the suite. It is
frequently used to establish the starting conditions or state needed for every test.
When this hook is used inside the test file, it will run before each test in the file.
When called inside a test.describe()group, it runs before each test in the
group.
test.beforeAll(): This hook runs once before any tests in the test suite are
executed. It is often used for setup tasks that need to be performed once before
any tests begin. When this hook is used inside the test file, it will run before all
the tests in the file. When called inside a test.describe() group, runs before
all the tests in the group.
test.afterAll(): Once all test cases of the test suites are finished, this hook is
activated. It is usually utilized for cleaning chores that need to be completed.
When this hook is used inside the test file, it will run after all the tests in the file.
When called inside a test.describe() group, it runs after all tests in the
group.
test.afterEach(): This hook executes after every test in the test suite. it is helpful
for clearing out resources and restoring the state. When this hook used inside the
test file it will runs after each test in the file. When called inside a
test.describe() group, runs after each test in the group.
Let us see an example of all the hooks:
const { test } = require("@playwright/test");
test.beforeAll(async () => {
console.log("Run before ALL Test ");
});
test.beforeEach(async ({}) => {
console.log("Run Before Each Test Step");
});
test.afterAll(async ({}) => {
console.log("Run After all test");
});
test.afterEach("do this after each test ends", async ({}) => {
console.log("Run After Each Test Step");
});
test("Test Case 1", async ({}) => {
console.log("This is My FIRST Test Case");
});
test("Test Case 2", async ({}) => {
console.log("This is My SECOND Test Case");
});
Let us execute the code from the previous section and see in which order these hooks
run by following the steps given in Figure 6.17:
Figure 6.17: Hooks execution order
You can see the order of execution initially in Figure 6.17, the beforeAll() hook is
executed, followed by the beforeEach() hook. Subsequently, the first test case is
executed. Finally, upon completion of the test case, the afterEach() hook is executed.
For second test case initially, beforeEach() is executed, followed by the execution of
the test case itself. Subsequently, the afterEach() hook is triggered. Finally, after the
completion of the second test case, the afterAll() hook is executed.
In summary, the beforeAll() and afterAll() hooks are triggered once for the entire
test suite. beforeEach() and afterEach() hooks are executed twice for each individual
test case.
Conclusion
In this chapter, we have navigated through diverse reports in Playwright, discussed the
intricacies of assertions, determined the distinctions between hard and soft assertions,
providing illustrative examples. Additionally, we explored various annotations in
Playwright. Lastly, we expanded into the versatility of hooks in the context of
Playwright.
In the upcoming chapter, we will discuss the Page Object Model (POM) to elevate
web development test automation. We will look into the pros and cons of the POM
design pattern, offering a comprehensive understanding of its benefits and potential
challenges. You will learn how to implement POM in new projects and
parameterization ruby nning tests with different sets of data.
Multiple choice questions
1. What is the purpose of assertions in Playwright tests?
a. To define test fixtures
b. To execute setup tasks
c. To verify expected outcomes and conditions
d. To measure code coverage
2. Which Playwright function is commonly used for making assertions in
tests?
a. evaluate ()
b. assert ()
c. verify ()
d. expect ()
3. What is the purpose of the afterEach hook in Playwright?
a. Run cleanup tasks after all tests
b. Run setup tasks before each test
c. Run cleanup tasks after each test
d. Run setup tasks before all tests
4. How do you annotate a test to be skipped in Playwright?
a. @skipTest
b. @ignore
c. @skip
d. @disabled
5. Which of the following is a purpose of using reporters in Playwright
testing?
a. To define test suites
b. To generate test output and reports
c. To install Playwright dependencies
d. To declare test hooks
Answers
1. c.
2. d.
3. c.
4. c.
5. b.
Join our book’s Discord space
Join the book's Discord Workspace for Latest updates, Offers, Tech happenings around
the world, New Release and Sessions with the Authors:
https://discord.bpbonline.com
CHAPTER 7
Page Object Model Pattern in
Playwright
Introduction
In the preceding chapter, Chapter 6, Reporter, Assertion, Annotations, and Hooks
in Playwright, we discussed the different types of reports in Playwright. You have
learned about assertions and their types with examples as well as annotations.
Finally, you have seen various Hooks available in Playwright.
In this chapter, we begin with an introduction to the Page Object Model (POM),
highlighting its significance in enhancing the structure and efficiency of web
development test automation. We will explore the potential implications of not
adopting POM, emphasizing the importance of this model in improving test
maintainability and scalability. We look into the practical aspects of implementing
POM in Playwright. Furthermore, we will address the concept of data-driven
testing by incorporating external files into Playwright tests.
Structure
This chapter covers the following topics:
Introduction to Page Object Model
Implications of not using Page Object Model
Implementing Page Object Model with Playwright
Data-driven from external files to Playwright tests
Objectives
By the end of this chapter, you will be able to know about POM. You will also
have a clear understanding of the consequences of not using POM in web
development test automation. Furthermore, you will be equipped with the skills to
implement POM in a new project. Lastly, you will explore the process of
parameterizing tests, enabling the execution of test cases from external files,
particularly in the format of .json files.
Introduction to Page Object Model
POM is a popular design pattern used in software development, specifically in the
context of test automation for web applications. It provides a structured and
efficient way to organize and manage automated test scripts. POM creates a
dedicated object repository for each web page. This repository contains the
definitions of web elements (such as buttons, text fields, etc.) and the methods to
interact with them. This centralizes the information about the page components,
making it easier to manage and update. POM helps in reducing code complexity
and redundancy. With web elements and their interactions encapsulated in
separate classes, there is less duplication of code across test scripts. Any changes
in the web page structure can be addressed in the corresponding class, avoiding
widespread modifications in the test scripts.
In POM, you create a dedicated class file, called Page Object, for each distinct
webpage or component in your web application. This class file serves as a
representation of that specific page or component in the codebase. The Page
Object classes encapsulate both the UI elements (like buttons, checkboxes, text
fields, etc.) and the corresponding functionalities associated with those elements.
This encapsulation helps in organizing and managing the code related to a
particular webpage or component in a modular and self-contained manner. Each
Page Object represents a specific page within the web application. It acts as an
abstraction layer that shields the test scripts from the complexities of the
underlying HTML structure and provides a clean interface for interaction.
Essential components of POM
The POM consists of several key components that contribute to its structure and
functionality in web automation. Here are some of the essential components of
POM:
Page classes: Each web page in the application has its own dedicated class
file. The page class encapsulates all the elements and behaviors related to
that specific page. It contains definitions of web elements and methods to
interact with those elements.
Web elements: Web elements are the objects on a web page that can be
interacted with, such as buttons, text fields, checkboxes, etc. In POM, these
elements are identified and defined in the corresponding page class.
Elements are usually declared using a locator strategy (XPath, CSS selector,
etc.) and associated with methods for interacting with them.
Page methods/actions: Page methods are functions defined in the page
class that represent actions or operations which can be performed on the
web page. These methods encapsulate the interaction logic with the web
elements, providing a clean interface for test scripts. Examples of page
method includes clicking a button, entering text into a field, or validating
the state of an element.
Constructor: The constructor in the page class is responsible for
initializing the web elements when an instance of the page class is created.
Assertions: Assertions are used to validate the expected behaviour of the
application. They can be integrated within the page methods or used directly
in the test scripts.
Playwright and POM for test automation
Playwright is a good option for incorporating the POM pattern in test automation.
Let us break down the reasons:
Cross-browser consistency: Playwright supports various browsers,
allowing teams to write tests that ensure the application works consistently
across different browser environments. This is crucial for providing a
reliable user experience.
Unified API across browsers: Playwright offers a single API for various
browsers (Chromium, Firefox, Safari), simplifying browser automation and
reducing code duplication. POM, when combined with Playwright, further
promotes this consistency across pages and elements.
Rich feature set: Playwright provides advanced features like page
recording, network interception, and mobile device emulation. POM allows
these features to be integrated seamlessly within page objects, enhancing
test capabilities.
POM for maintainability: Implementing POM with Playwright helps in
organizing and maintaining automation code effectively. Page classes
encapsulate the interaction logic, making the codebase modular and easier
to manage.
Built-in support for async operations: Playwright has built-in support for
asynchronous operations. This aligns well with the implementation of the
POM, where asynchronous operations are common. It allows for more
efficient and seamless handling of asynchronous tasks in test scripts.
Active community and ecosystem: Playwright has a growing community
and ecosystem with libraries, tools, and plugins. POM integrates well with
these tools, improving overall testing workflow and efficiency.
Benefits of using POM
The POM is a design pattern used in test automation to enhance the
maintainability and reusability of test scripts. It is particularly popular in web
application testing. Here are some of the benefits of using the Page Object Model:
Modularity and reusability: POM promotes the creation of separate
classes for each web page or component of a web application. This
modularity makes it easier to maintain and update code since changes to
one page or component do not impact other parts of the codebase.
Code maintainability: POM separates the test code from the page-specific
code. This makes it easier to locate and update elements or functionality
associated with a specific page without affecting the entire test suite.
Readability and understandability: Page Objects encapsulate the details
of interacting with a particular page, making the test scripts more readable
and understandable. Test scripts become more like high-level descriptions
of user interactions with the application rather than a series of low-level,
technical commands.
Easy debugging: In case of any failures, it is easier to identify the root
cause by looking at the specific Page Object class rather than digging
through the entire test script. Debugging becomes more straightforward as
the code is organized in a modular way.
Improved test script maintenance: When the application undergoes
changes, such as UI modifications or functionality updates, only the
corresponding Page Object needs to be modified. The changes are localized
and do not require updates across the entire test suite.
Enhanced test stability: By encapsulating the interaction with page
elements within Page Objects, tests become less prone to failure due to
changes in the UI or structure of the application. When elements or actions
change, only the corresponding Page Object needs to be updated.
Reusable components: Page Objects can contain methods that represent
reusable components or actions. This makes it easy to reuse these
components across different tests.
Abstraction of UI changes: POM abstracts the UI changes by
encapsulating the locators and methods within the Page Object classes. This
allows testers to focus on creating and maintaining tests without being
overly concerned about UI changes.
In the next section, before explaining POM in detail, we will first see what the
implications are when we are not using POM.
Implications of not using Page Object Model
Let us see with examples what are the implications of not using POM. If you do
not use the POM in Playwright with JavaScript, your test code might become less
modular, more prone to duplication, and harder to maintain.
The following section is an example used to illustrate the scenario of not using
POM. Suppose you have a web application with a login page without the POM;
your test code might look something like this:
const { test, expect } = require('@playwright/test');
test('Login Test without POM', async ({ page }) => {
// Navigate to the login page
await page.goto('https://example.com/login');
// Perform login without separating the page elements
await page.fill('input[name="username"]',
'your_username');
await page.fill('input[name="password"]',
'your_password');
await page.click('button[type="submit"]');
// Check if the login was successful
const successMessage = await page.textContent('.success-
message');
expect(successMessage).toContain('Welcome');
});
In this example, the test logic is directly interacting with the page elements using
Playwright methods. The selectors: 'input[name="username"]',
'input[name="password"]', 'button[type="submit"]', etc., are hard-coded into
the test, making it less maintainable and more prone. There is no separation
between the test logic and the structure of the web page.
Now, let us imagine that the UI of the login page changes. Without POM, you
would need to update every occurrence of these selectors in your test script. This
could lead to maintenance challenges, especially if similar selectors are used
across multiple test scripts.
Now, let us see how the same test can be structured using the POM:
loginPage.js
exports.LoginPage = class LoginPage {
constructor(page) {
this.page = page;
}
async navigateTo() {
await this.page.goto('https://example.com/login');
}
async login(username, password) {
await this.page.fill('input[name="username"]',
username);
await this.page.fill('input[name="password"]',
password);
await this.page.click('button[type="submit"]');
}
async getSuccessMessage() {
return await this.page.textContent('.success-message');
}
}
test.js
const { test, expect } = require('@playwright/test');
const LoginPage = require('./loginPage');
test('Login Test with POM', async ({ page }) => {
const loginPage = new LoginPage(page);
// Navigate to the login page using the Page Object
await loginPage.navigateTo();
// Perform login using the Page Object
await loginPage.login('your_username', 'your_password');
// Check if the login was successful using the Page Object
const successMessage = await
loginPage.getSuccessMessage();
expect(successMessage).toContain('Welcome');
});
In this updated example, a Login Page class is created to encapsulate the
functionality related to the login page. The test script is now more readable,
modular, and less prone to breaking if the page structure changes. The POM helps
in maintaining a separation between the test code and the details of the UI, making
the codebase more scalable and maintainable.
Let us take one more example without POM:
In this example, we are using the site https://www.saucedemo.com/ to automate
interactions with the test script. The test script performs a series of actions, such as
logging in, adding an item to the cart, removing it, and logging out. The last line
checks if the login button is visible after all the interactions.
import { test, expect } from '@playwright/test';
test('test', async ({ page }) => {
await page.goto('https://www.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('a').filter({ hasText: '1' }).click();
await page.locator('[data-test="remove-sauce-labs-
backpack"]').click();
await page.getByRole('button', { name: 'Open Menu'
}).click();
await page.getByRole('link', { name: 'Logout' }).click();
await page.getByRole('heading', { name: 'Password for all
users:' }).click();
await expect(page.locator('[data-test="login-
button"]')).toBeVisible();
});
This script follows a sequence of actions, interacting with various elements on the
page. It is important to note that this script does not use the POM explicitly, as it
directly interacts with page elements using the locators and methods of
Playwright. Each await statement represents an asynchronous action, and the test
assertions are performed using the expect function provided by the Playwright test
library. This example demonstrates how you can create a test script without the
use of POM by directly interacting with elements on the page using locators and
methods provided by Playwright.
Without POM, the code might become less modular and harder to maintain as the
application evolves. Changes in the UI structure might require updates in multiple
places in the script. Reusability of code becomes more challenging when specific
actions are scattered throughout the script. To address these implications, consider
adopting the POM, as it involves creating classes that represent pages and
encapsulating the interaction logic for each page within its corresponding class.
This approach promotes code organization, reusability, and easier maintenance.
Now, let us see how the same test can be structured using the POM:
LoginPage.js
exports.LoginPage = class LoginPage {
constructor(page) {
this.page = page;
}
async login(username, password) {
await this.page.goto('https://www.saucedemo.com/');
await this.page.locator('[data-
test="username"]').click();
await this.page.locator('[data-
test="username"]').fill(username);
await this.page.locator('[data-
test="password"]').click();
await this.page.locator('[data-
test="password"]').fill(password);
await this.page.locator('[data-test="login-
button"]').click();
}
}
With POM, your test script appears as follows:
TestPage.js
import { test, expect } from '@playwright/test';
import LoginPage from './LoginPage'; // Assuming you have a
LoginPage class defined
test('test', async ({ page }) => {
const loginPage = new LoginPage(page);
await loginPage.login('standard_user', 'secret_sauce');
});
In this updated example, a LoginPage class is created to encapsulate the
functionality related to the login page. The test script is now more readable,
modular, and less prone to breaking if the page structure changes.
In the next section, you will see how we can implement POM in a new project
using Playwright.
Implementing Page Object Model with Playwright
Organizing your Playwright tests using a folder structure is a good practice for
maintainability and readability. Here is a suggested folder structure for a
Playwright project using POM. In the following screenshot, you can see we have
created two folders under the Page Object Model (POM) folder.
The Tests folder would contain your test scripts. The Pages folder would contain
your Page Objects, where locators and methods for interacting with the
application are defined.
Figure 7.1: Page Object Model
To implement POM in Playwright let us take an example of the site
https://talent500.co/auth/signin. Let us implement the following use case with
POM.
Use case one
In the first use case, we will open the site and log in with valid credentials. After
login, we will make sure that the user is logged in and then log out of the
application.
1. Open the website https://talent500.co/auth/signin.
2. Login with a valid Email and Password.
3. Verify that the user has logged into the site.
4. After logging in, log out of the application.
Following is a sample project structure in POM, which covered the preceding use
case. You can see we have created a page class (LoginPageTalent500.spec.js) and
test class (Talenet500TestCase.spec.js) under respective folder, refer to Figure
7.2:
Figure 7.2: Create Pages and Tests class
Let us create page script under LoginPageTalent500.spec.js, as shown here:
const { expect } = require("@playwright/test");
exports.LoginPage = class LoginPage {
constructor(page, expect) {
this.page = page;
this.username_field = page.getByPlaceholder("Email");
this.password_field = page.getByPlaceholder("Password");
this.login_button = page.locator('[data-id="submit-
login-btn"]');
this.drop_down = page.locator('[alt="DropDown
Button"]');
this.log_out = page.locator('[data-id="nav-dropdown-
logout"]');
}
async openLoginPage() {
await
this.page.goto("https://talent500.co/auth/signin");
}
async loginIntoSite(username, password) {
await this.username_field.fill(username);
await this.password_field.fill(password);
await this.login_button.click();
}
async homePageConetnt(homeContent) {
const text = await
this.page.locator('//div[@id="progress-
bar"]').textContent();
expect(text).toContain(homeContent);
}
async logoutFromSite() {
await this.drop_down.click();
await this.log_out.click();
}
async homePageConetntAfterLogout(homeContentAfterLogout) {
const homePageTextAfterLogout = await
this.page.locator('//h2[normalize-space()="Opportunities
favor the bold"]').textContent();
expect(homePageTextAfterLogout).toContain(homeContentAfterLo
gout);
}
};
This code represents a POM class for a login page in a web application. It is using
Playwright for browser automation and the @playwright/test library for
assertions (expect).
Now, let us break down the preceding code:
Importing dependencies: Importing the expect function from the
@playwright/test library. This function is used for making assertions in
your tests.
Exporting the LoginPage class: Exporting a class named LoginPage, which
represents the login page of the web application.
Constructor: The constructor initializes the class instance with the page
object and locators on Playwright for various elements on the login page
such as, username field, password field, login button, dropdown button, and
logout button.
openLoginPage method: The openLoginPage method navigates the browser
to the specified login page URL.
loginIntoSite method: Enters the provided username and password into the
respective fields and clicks the login button.
homePageContent method: Asserts that the content of the home page
contains the specified homeContent.
logoutFromSite method: Performs a logout action by clicking on the
dropdown button and then the logout button.
HomePageContentAfterLogout: Asserts that the content of the home page
after logout contains the specified homeContentAfterLogout.
Until now, we have created methods that initialize the class instance with page
objects and locators on Playwright for various elements.
Now, to call the previously created method, it is required to create a test class. Let
us create test class and give name Talenet500TestCase.spec.js. This test script
performs a series of actions using the LoginPage object to navigate to the login
page, log in, check home page content, log out, and then verify the home page
content after logout, as shown here:
const { test } = require("@playwright/test");
const { LoginPage } =
require("../Pages/LoginPageTalent500.spec");
test("Login test", async ({ page }) => {
const Login = new LoginPage(page);
await Login.openLoginPage();
await
Login.loginIntoSite("applitoolsautomation@yopmail.com",
"Test@123");
await Login.homePageConetnt(" Discover jobs
");
await Login.logoutFromSite();
await Login.homePageConetntAfterLogout("Opportunities favor
the bold");
});
Let us break down the preceding code step by step:
Import dependencies: The line @playwright/test imports the test function
from the Playwright test framework. It is s used for defining and running
test cases and ../Pages/LoginPageTalent500.spec imports the LoginPage
class from the specified file.
Create LoginPage object: Creates an instance of the LoginPage class by
passing the page object to its constructor. This login object will be used to
interact with the login page.
Perform login: The openLoginPage method is used to navigate to the login
page. The loginIntoSite method is called to perform a login using the
specified username and password.
Check home page content: The homePageContent method is used to check
if the home page content contains the text "PROFILE". This method likely
uses an assertion (expect) to verify the expected content.
Logout: The logoutFromSite method is used to log out from the site.
Check home page content after logout: The homePageConetntAfterLogout
method is used to check if the home page content, after logging out,
contains the text Opportunities favor the bold.
Let us execute the above use case:
Open the terminal and run the command npx playwright test --ui. This
command runs all tests in the default browser specified in your configuration file.
In the following screenshot, you can use the user login with valid credentials;
verify if you are logged in and re-direct to the home page. Finally, log out from
the application and validate the text displayed in Figure 7.3 to ensure that the user
is successfully logged out:
Figure 7.3: Login test execution result
The next section includes one more example to explain the implementation of
POM in Playwright.
Use case two
In the second use case, we will log in, select the Software category, add a product
'iPad' to the cart, remove it, and verify the "shopping cart empty" message.
1. Open the website:
https://ecommerceplayground.lambdatest.io/index.php?
route=account/login.
2. Login with a valid Email and Password.
3. Verify that the user is logged into the site.
4. Select the category Software.
5. Search the product iPod.
6. Add the product to the cart.
7. Verify that the product has been added successfully.
8. Remove the added product.
9. Verify the message Your shopping cart is empty!.
To cover the preceding use case using POM, we have to create a Page and Test
class folder, the structure for which is attached in the following screenshot, Figure
7.4. As you can see, we have created various page classes for different pages and
test classes:
Figure 7.4: Pages and Tests classes
Let us create page class for different Pages:
LoginPageLambdaTest.spec.js class is used for logging into the site, the
command for which is as follows:
const { expect } = require("@playwright/test");
exports.LoginPage = class LoginPage {
constructor(page, expect) {
this.page = page;
this.emailInput = page.locator("#input-email");
this.passwordInput = page.locator("#input-
password");
this.loginButton =
page.locator('//input[@value="Login"]');
}
async enterEmail(email) {
await this.emailInput.fill(email);
}
async enterPassword(password) {
await this.passwordInput.fill(password);
}
async clickLoginButton() {
await this.loginButton.click();
}
};
HomePageLambdaTest.spec.js class is used to verify content in Home page
that confirm user is logged-in, the command for which is as follows:
const { expect } = require("@playwright/test");
exports.HomePage = class HomePage {
constructor(page) {
this.page = page;
this.myAccountLink = page.locator("text=My Account");
}
async verifyUserLoggedIn(homePageContentAfterLogin) {
const homePageTextAfterLogin = await this.page
.locator('//a[normalize-space()="Change your
password"]')
.textContent();
expect(homePageTextAfterLogin).toContain(homePageContentAfte
rLogin);
}
};
SearchProductLambdaTest.spec.js, class is used to select the category and
search the product, the command for which is as follows:
const { expect } = require("@playwright/test");
exports.SearchPage = class SearchPage {
constructor(page) {
this.page = page;
this.categoryAllCategories = page.locator(
'#entry_217822 button:has-text("All Categories")'
);
this.categorySoftware = page.locator("#entry_217822 >>
text=Software");
this.searchField = page.getByRole("textbox", {
name: "Search For Products",
});
this.searchButton = page.locator("text=Search");
this.hover = page.locator(".lazy-load");
}
async selectTheCategory() {
await this.categoryAllCategories.click();
await this.categorySoftware.click();
}
async searchForTheProduct(product) {
await this.searchField.fill(product);
await this.searchButton.click();
}
};
AddProductIntoCartLambdaTest.spec.js class is used to add product into
the cart and finally remove the added product, the command for which is as
follows:
const { expect } = require("@playwright/test");
exports.ProductCartPage = class ProductCartPage {
constructor(page) {
this.page = page;
this.hover = page.locator(".lazy-load");
this.clickFirstElement = page.locator(".product-action >
button");
this.viewCart = page.locator("text=View Cart");
this.addedProduct = page.locator("#content >> text=iPod
Touch");
this.cartPopup = page.locator('div[role="alert"]');
this.removeProduct = page.locator('//button[@class="btn
btn-danger"]');
this.message = page.locator(
'(//p[contains(text(),"Your shopping cart is
empty!")])[2]'
);
}
async addProductIntoCart() {
await this.hover.first().hover();
await this.clickFirstElement.first().click();
await expect(this.cartPopup).toContainText('Success: You
have added iPod Touch to your shopping cart!')
await this.viewCart.click();
await expect(this.addedProduct).toBeVisible();
}
async removeProductIntoCart() {
await this.removeProduct.click();
await expect(this.message).toBeVisible();
}
};
We have created Page classes next step is to create Test class:
Create test class with name LambdaTestCase.spec.js. This test class is used to
call all created methods inside page classes. The command is as follows:
const { test, expect } = require("@playwright/test");
const { LoginPage } =
require("../Pages/LoginPageLambdaTest.spec");
const { HomePage } =
require("../Pages/HomePageLambdaTest.spec");
const { SearchPage } =
require("../Pages/SearchProductLambdaTest.spec");
const {ProductCartPage} =
require("../Pages/AddProductIntoCartLambdaTest.spec");
test("Login and Navigate to My Account", async ({ page }) =>
{
const Login = new LoginPage(page);
const homePage = new HomePage(page);
const searchPage = new SearchPage(page);
const productIntoCart = new ProductCartPage(page);
// Open the website
await page.goto(
"https://ecommerce-playground.lambdatest.io/index.php?
route=account/login"
);
// Perform login
await Login.enterEmail("lambdatestnew@yopmail.com");
await Login.enterPassword("Lambda123");
await Login.clickLoginButton();
// Validate user logged in and can Add/Delete the product
await homePage.verifyUserLoggedIn("Change your password");
await searchPage.selectTheCategory();
await searchPage.searchForTheProduct("iPod");
await productIntoCart.addProductIntoCart();
await productIntoCart.removeProductIntoCart();
});
Now, let us execute the above-created test case. In the following screenshot, you
can see test case is executed successfully, Figure 7.5:
Figure 7.5: Test execution result
In the following screenshot, you can see the product added to the cart and, finally,
remove the message Your shopping carts is empty! can be verified. Refer to the
Figure 7.6:
Figure 7.6: Validate message
Data-driven from external files to Playwright tests
Data-driven testing involves using external data to drive the execution of tests.
This means that you can separate your test logic from the test data, making it
easier to maintain and scale your test suite. External data files, such as JSON or
CSV files, are commonly used to store the test data. In the following section, you
will learn how to retrieve data from the .json file and use it in test case execution.
Data-driven testing offers several benefits, and here are some of the key
advantages:
Separation of test logic and test data: By using external data files, you
can separate the test logic (the steps to execute) from the test data (input
values, expected results). This separation makes it easier to manage and
maintain your test scripts. Changes to the test data can be made without
modifying the test logic, and vice versa.
Centralized data storage: Storing test data in external files (e.g., JSON,
CSV) allows for centralized management. If there are changes or updates to
the test data, you can modify the external file without touching the test
script itself. This reduces the chance of errors and makes maintenance more
straightforward.
Reuse of test logic: Since the test logic is independent of the test data, you
can reuse the same test script with different datasets. This promotes code
reusability and reduces duplication, as you can apply the same testing logic
to various scenarios.
Scalability: As your test suite grows with more test cases, maintaining data-
driven tests becomes more efficient. You can easily add, update, or remove
test cases by modifying the external data file without needing to alter the
actual test logic. This scalability is crucial for managing complex
applications with numerous scenarios.
Parameterized testing: External data files enable parameterization of tests.
You can run the same test logic with different input values, helping to
identify edge cases and variations. This flexibility is especially useful when
testing various configurations of an application.
Steps to implement data-driven testing in Playwright using external files:
1. Create an external data file: Create a file such as, testData.json to store
your test data. This file will contain an array of objects, each representing a
set of input data for a test case. For example:
{
"data": [
{
"email": "lambdatestnew@yopmail.com",
"password": "Lambda123",
"homePageMessage": "Change your password",
"searchProduct": "iPod",
"addProductMessage":"Success: You have added iPod Touch
to your shopping cart!"
}
]
}
In the following screenshot you can see testData .json file where we are
keeping the data for the test case.
Figure 7.7: testData.json
2. Modify your Playwright test script: Adjust your Playwright test script to
read and use the data from the external file. Here is an example:
In the following figure, you can see we have imported the data and then
used that data to log in and validate products in the cart. Refer to Figure
7.8:
Figure 7.8: Import Test Data
3. Run the test script: Now, let us execute the updated script. In the
following screenshot, you can see that the test case is executed
successfully:
Figure 7.9: Shopping cart validation message
Conclusion
To recap, this chapter delved into the POM with the goal of enhancing web
development test automation. We explored the consequences of not utilizing POM
and examined the advantages and disadvantages associated with it. Furthermore,
we addressed the practical aspects by demonstrating how to implement POM in a
new project. Additionally, we explored the process of parameterizing tests,
allowing the execution of test cases from external files, specifically in the format
of .json files.
In the upcoming chapter, we will attempt to understand what Cucumber is and
how it facilitates collaboration between technical and non-technical team
members, promoting a shared understanding of requirements. Learn about how
BDD scenarios can be written in a Given-When-Then format, making them easy
to understand by all stakeholders. Furthermore, we will explore the process of
creating Feature and Step Definition files. Additionally, understand the role of
Cucumber Hooks and Tags in test execution.
Multiple choice questions
1. What is the primary purpose of using POM?
a. To optimize the performance of Playwright scripts
b. To organize and manage page-related actions and elements
c. To integrate external libraries with Playwright
d. To automate browser installation and configuration
2. How does POM contribute to code maintainability in Playwright?
a. By reducing code duplication
b. By organizing actions and elements in separate classes
c. By promoting a modular approach to script development
d. All of the above
3. How can you organize your Playwright POM classes for multiple
pages?
a. Use a single class for all pages
b. Use a separate class for each page
c. Use a global class for all Playwright actions
d. Use a generic class for managing pages
4. How does the Page Object Model enhance the maintainability of
Playwright test scripts when the UI changes?
a. It does not have any impact on script maintainability
b. It requires rewriting the entire test script
c. It requires updating only the Page Object classes with modified
locators
d. It necessitates updating the Playwright library
5. How does the Page Object Model contribute to test reusability in
Playwright scripts?
a. By promoting the duplication of codes
b. By avoiding the use of functions in Page Objects
c. By centralizing the locators and interactions, reducing code
duplication
d. By creating separate scripts for each test case
Answers
1. b.
2. d.
3. b.
4. c.
5. c.
CHAPTER 8
Playwright Cucumber/BDD
Framework Integration
Introduction
In the preceding chapter, we were introduced to the Page Object Model
(POM), highlighting its significance in enhancing the structure and efficiency
of web development test automation. We explored the potential implications of
not adopting POM, emphasizing the importance of this model in improving test
maintainability and scalability. We looked into the practical aspects of
implementing POM in Playwright. Furthermore, we addressed the concept of
data-driven testing by incorporating external files into Playwright tests.
In this chapter, we will begin with an exploration of Cucumber, a powerful
testing tool widely employed for behavior-driven development (BDD) in
software testing. We will learn about the Hooks, Tags, Background and Data
Table in Cucumber, which are the pivotal components that enhance the
flexibility and organization of Cucumber tests. You will see the integration of
Cucumber with Playwright and the chapter concludes by introducing the
Cucumber HTML Reporter, a valuable tool for generating visually compelling
and interactive HTML reports from Cucumber test results.
Structure
This chapter covers the following topics:
Introduction to behavior-driven development
Introduction to Cucumber
Understanding features and scenarios
Understanding Cucumber hooks and Tags
Understanding Cucumber Background, Data Tables
Integrating Cucumber with Playwright
Cucumber HTML reporter
Objectives
By the end of this chapter, you will be able to acquire the knowledge and skills
to effectively utilize Cucumber for BDD, create feature and step definition
files, implement Hooks and Tags, integrate Cucumber with Playwright for web
testing, and leverage the Cucumber HTML reporter for insightful test result
visualization.
Introduction to behavior-driven development
Daniel Terhorst-North spearheaded the development of BD in the early 2000s,
as detailed in his 2006 article, Introducing BDD. Originating as a response to
test-driven development (TDD), BDD aimed to guide programmers on
nascent agile teams to swiftly navigate the crucial aspects of testing and
coding, reducing misunderstandings. Over time, BDD has undergone
expansion, encompassing both analysis and automated testing at the acceptance
level. Liz Keogh, a fellow BDD pioneer, commenced extensive discussions and
writings on the subject in 2004.
The Given/When/Then template, a core element of BDD, was developed to
express the acceptance criteria of a story in an executable form. This template
was influenced by the As a..., I..., So that... template used for writing user
stories, which had become a recognized standard.
BDD encompasses the creation of test cases based on the expected behavior of
software functionalities. These test cases are articulated in straightforward
English statements within a feature file generated by humans. The statements
within acceptance test cases specifically concentrate on user actions, providing
a user-centric perspective to ensure alignment with desired software behavior.
BDD commonly uses the Given-When-Then syntax to structure Scenarios
within Feature files, as follows:
Given: Describes the initial context or setup for the scenario. It sets the
preconditions or the starting state before the main action takes place.
For example: Given the user is on the login page.
When: Represents the action or event that triggers the scenario. It
specifies what the user does or what event occurs.
For example: When the user enters valid credentials.
Then: Defines the expected outcome or result of the scenario. It
describes what the system should do or how it should behave after the
specified action has taken place.
For example: Then the user should be redirected to the dashboard.
Putting it all together in a complete scenario:
Feature: User Authentication
Scenario: Successful Login
Given the user is on the login page
When the user enters valid credentials
And clicks the login button
Then the user should be redirected to the
dashboard
This syntax provides a structured way to express requirements, making it
easy for both technical and non-technical team members to understand
and collaborate on the expected behavior of the system.
BDD lifecycle
BDD lifecycle typically involves a series of steps aimed at promoting
collaboration and ensuring that software development is driven by the desired
behavior of the system Let us use the following figure to create a scenario
related to the login functionality:
Figure 8.1: BDD life cycle
Following are the steps to create the scenario mentioned in Figure 8.1
1. Write Scenario (login feature): Define a feature file using the Gherkin
language. This file outlines the desired behavior of a particular feature or
functionality. It typically includes Scenarios with Given, When, and
Then steps, as shown here:
a. Feature: User Login
b. Scenario: Valid User Login
i. Given the user is on the login page
ii. When the user enters valid credentials
iii. And clicks the login button
iv. Then the user should be redirected to the dashboard.
2. Run the Scenario: Execute the defined Scenarios as part of the BDD
testing process. Initially, these Scenarios will fail as there is no
corresponding automation code.
3. Fail the Scenario: The initial run of Scenarios will result in failures
since the automation code is not yet implemented.
4. Create step definition and write code to pass the Scenario: Write step
definitions for each step in the Scenarios. Step definitions link the
Gherkin steps to the actual automation code. Develop the actual
automation code that corresponds to each step in the step definitions.
This code interacts with the application to simulate user actions.
5. Run and pass the test: Execute the BDD tests, and with the
implemented automation code, the Scenario should now pass
successfully.
Benefit of using BDD
BDD offers several benefits to development teams, stakeholders, and the
overall software development process. The following are some key advantages
of using BDD:
User-centric focus: BDD places a strong emphasis on understanding and
prioritizing user needs. Scenarios and specifications are written from the
perspective of end-users, ensuring that the software addresses real-world
business requirements.
Collaboration and communication: BDD promotes collaboration
between different stakeholders, including developers, testers, product
owners, and business analysts.
Tool support: BDD is supported by various tools that assist in writing,
executing, and managing behavior specifications.
Focus on business value: BDD prioritizes features and behaviors based
on their business value. This ensures that development efforts are
directed towards delivering functionality that brings the most significant
value to end-users and stakeholders.
Introduction to Cucumber
In 2005, the RSpec project was founded, supporting BDD in the Ruby
programming language. RSpec became a popular tool for implementing BDD
practices in the Ruby ecosystem, allowing developers to express specifications
and behavior in a readable and domain-specific language. The founding
members of the RSpec project included Dave Astels, Steven Baker, Aslak
Hellesøy, and David Chelimsky. With the intention of creating an enhanced
version of RSpec Story Runner, Aslak Hellesøy initiated a new project initially
named Stories.
Seeking a catchier name for the project, Aslak asked his then-fiancée for
suggestions. She came up with the name Cucumber, and the name resonated
with Aslak, ultimately becoming the official name of the project. The choice of
the name Cucumber reflects the commitment of the project to provide a tool
that is not only powerful and effective for developers but also accessible and
understandable for non-coders within the development team.
Cucumber is a tool that supports BDD written in Ruby, which is used to
develop test cases for the behavior of the software and its functionality. It is
widely used because it provides an easily understandable testing script for
system acceptance and automation testing. Cucumber uses a simple English
text called Gherkin language to write test cases for the behavior of the
software and its functionality. It allows business analysts, developers, testers,
etc. to automate functional verification and validation in an easily readable and
understandable format (for example, plain English). Cucumber facilitates
collaboration between technical and non-technical team members by allowing
them to define and discuss software behaviors in a common language. The tool
then generates executable tests from these specifications.
This is how Cucumber works:
Cucumber is a tool used in conjunction with BDD to facilitate communication
and collaboration between developers, testers, and stakeholders. Here is how it
works:
Feature files: The starting point is defining feature files. These are plain
text files written Gherkin, which is designed to be human-readable and
understandable by both technical and non-technical people. Feature files
outline Scenarios, which are specific examples of how the system should
behave under different conditions. Each scenario describes:
Given: The initial state of the system before some action takes place.
When: The action that is taken.
Then: The expected outcome of the action.
Step definitions: While Gherkin provides a common language for
describing behavior, it does not tell you how to achieve that behavior in
code. This is where step definitions come in. Step definitions are written
in a programming language of your choice (for example, Python, Ruby,
Java) and map specific Gherkin steps to actual code that implements the
desired functionality. Each step definition associates a Gherkin step with
a piece of code, translating human-readable expectations into concrete
actions.
Execution and reporting: When you run Cucumber, it parses the feature
files and identifies the Scenarios. For each Scenario, Cucumber looks for
matching step definitions. It executes the code associated with each step
definition, simulating the interaction of the user with the system. Finally,
Cucumber compares the actual outcome with the expected outcome
(defined in the Then part of the scenario) and generates a report. This
report indicates whether each scenario passed or failed, providing
valuable feedback on the behavior of the system against defined
expectations.
Cucumber test example:
Let us take an example to create Cucumber feature file for logging into
https://talent500.co/auth/signin, verifying that the user is logged in, and then
logging out:
Feature: Login and Logout on Talent500
Scenario: Successful login and logout
Given User is on the Talent500 login page
When User enters valid username and password
And User clicks the login button
Then User should be redirected to the Talent500
dashboard
And User should see a welcome message
When User clicks on the logout button
Then User should be successfully logged out
And User should be redirected to the Talent500 login
page
Let us break down the elements of this feature file:
Feature: This is a high-level description of the functionality being tested.
In this case, it is the Login and Logout on Talent500.
Scenario: This is a specific test scenario related to the feature. The
scenario is Successful login and logout, which implies that the purpose
of this test is to ensure that a user can successfully log in and log out of
the Talent500 platform.
Given: This describes the initial state or preconditions necessary for
the scenario to start. It states that the user is on the Talent500 login
page.
When: This section outlines the actions taken by the user. It mentions
that the user enters a valid username and password, clicks the login
button, and then clicks on the logout button.
And: The And keyword is used after the initial When step to indicate
additional actions performed by the user.
Then: This section describes the expected outcomes or assertions
after the actions have been performed. The expected outcomes are:
After entering valid credentials and clicking the login button, the
user should be redirected to the Talent500 dashboard.
The user should see a welcome message.
After clicking the logout button, the user should be successfully
logged out.
Finally, after logging out, the user should be redirected to the
Talent500 login page.
Understanding features and scenarios
A Feature is a high-level description of a software feature or functionality. It
typically represents a user story or a business requirement. Features are written
in a natural language format, typically using the Gherkin language (Given-
When-Then). A feature file may contain multiple Scenarios related to that
specific feature.
A Scenario is a specific test case that validates a particular aspect of the feature.
Scenarios are written in a Given-When-Then format, defining the initial state
(Given), the action or event (When), and the expected outcome (Then).
Multiple Scenarios can exist within a single feature file, covering different test
cases for the same feature.
Following is the simple example of feature files. In single feature file we can
have one or multiple Scenarios:
Feature file with single scenario:
Feature: User Authentication
Scenario: Successful Login
Given the user is on the login page
When the user enters valid credentials
Then the user should be logged in successfully
Feature file with multiple scenario:
Feature: User Authentication
Scenario: Successful Login
Given the user is on the login page
When the user enters valid credentials
Then the user should be logged in successfully
Scenario: Invalid Login
Given the user is on the login page
When the user enters invalid credentials
Then an error message should be displayed
By using these Features and Scenarios, Cucumber enables teams to create
living documentation that is both human-readable and executable. The feature
files act as a shared understanding between business stakeholders and
development teams.
Understanding Cucumber hooks and tags
In this section, you will learn about Cucumber hooks and tags. Hooks and tags
are two important features in Cucumber that allow developers to customize and
control the behavior of their test Scenarios.
Let us go through the following points in detail, one by one:
Cucumber hook: Cucumber hooks are special blocks of code that allow
you to perform actions at various points in the execution lifecycle of a
scenario. These actions can include setting up or tearing down resources,
performing specific tasks before or after each scenario, or handling
Scenarios that fail or pass.
Purpose of Cucumber hooks:
Code workflow management: Hooks help in managing the code
workflow by allowing you to define actions that need to be
performed before or after Scenarios.
Code redundancy reduction: By defining common actions in
Hooks, you can reduce code redundancy across multiple Scenarios.
Types of Cucumber hooks:
@Before hook: Executes code before each Scenario. It is suitable
for setting up prerequisites like establishing database connections,
setting up test data, etc.
@After hook: Executes code after each scenario. It is useful for
tasks such as closing the browser, terminating database
connections, clearing test data, logging.
Cucumber tag: In Cucumber, a tag is a way to associate metadata with
Scenarios or feature files. Tags are prefixed with the @ symbol, and they
can be used to categorize and organize Scenarios. Tags provide a
convenient mechanism for selectively running specific sets of Scenarios
during test execution.
Benefit of tags in Cucumber feature file:
Grouping Scenarios: Tags allow you to group related Scenarios
together. This is particularly useful when you have a large number
of Scenarios in a feature file, and you want to organize them based
on common characteristics or functionalities. For example, you
might use tags like @smoke, @regression, @positive, or @negative
to categorize Scenarios based on their purpose.
Selective execution: During test execution, you can choose to run
only specific Scenarios or groups of Scenarios by specifying the
tags. This is beneficial when you want to focus on a particular set
of tests, such as running only smoke tests before a deployment or
running only positive test cases for a specific feature. Saves time
and resources by avoiding unnecessary test runs.
Parallel execution: Tags can be used to facilitate parallel test
execution. Scenarios with different Tags can be run concurrently,
improving test suite execution time.
Integration with continuous integration/continuous
deployment: Tags can be integrated into continuous
integration/continuous deployment (CI/CD) pipelines. Testing
subsets can be defined using tags, and CI/CD tools can be
configured to execute specific tagged Scenarios based on the
pipeline stage.
Let us consider an example related to an e-commerce website testing scenario.
Assume you have a feature file for testing different aspects of the shopping cart
functionality. You can use tags to categorize Scenarios based on the type of
testing or the area of the application. Here is an example feature file with tags:
Feature: Shopping Cart Functionality
@addToCart
Scenario: Adding items to the shopping cart
Given the user is on the product page
When the user adds items to the cart
Then the cart should display the correct items
@checkout
Scenario: Checking out items from the shopping cart
Given the user has items in the cart
When the user proceeds to checkout
Then the user should be directed to the payment page
@removeFromCart
Scenario: Removing items from the shopping cart
Given the user has items in the cart
When the user removes items from the cart
Then the cart should reflect the changes
@emptyCart
Scenario: Emptying the shopping cart
Given the user has items in the cart
When the user empties the cart
Then the cart should be empty
In this example, Scenarios are tagged with different categories, such as
@addToCart, @checkout, @removeFromCart, and @emptyCart. These tags help you
organize your test Scenarios based on functionality or testing type. when you
run your Cucumber tests, you can use tags to execute specific sets of Scenarios.
Add multiple tags in feature file
Multiple tags can be used to convey different aspects or perspectives related to
a scenario. For example, a scenario might need to be considered for both
@regression testing and as part of a smoke test suite.
Here is an extension of the previous example to demonstrate this:
Feature: Shopping Cart Functionality
@addToCart @regression
Scenario: Adding items to the shopping cart
Given the user is on the product page
When the user adds items to the cart
Then the cart should display the correct items
@checkout @regression @smoke
Scenario: Checking out items from the shopping cart
Given the user has items in the cart
When the user proceeds to checkout
Then the user should be directed to the payment page
@removeFromCart @regression
Scenario: Removing items from the shopping cart
Given the user has items in the cart
When the user removes items from the cart
Then the cart should reflect the changes
@emptyCart @smoke
Scenario: Emptying the shopping cart
Given the user has items in the cart
When the user empties the cart
Then the cart should be empty
In this case, the scenario Adding items to the shopping cart is tagged with both
@addToCart and @regression. This indicates that the scenario is related to
adding items to the cart and should be part of regression testing. The flexibility
of using multiple Tags allows you to customize test execution based on
different testing criteria or perspectives, making it a powerful feature in
Cucumber for organizing and executing Scenarios in various testing Scenarios.
Understanding Cucumber Background, Data Tables
Background keyword in Cucumber allows you to define a set of steps that
should be executed before each scenario in a feature file. Cucumber supports
the use of Data Tables, which allow you to pass tabular data to your step
definitions. This is particularly useful when you need to test multiple sets of
input data or verify multiple expected outcomes within a single scenario.
Let us go through the following points in detail, one by one:
Cucumber Background: In Cucumber feature file, the Background
section is used to define a set of common steps that are shared among all
Scenarios within a specific feature file. The primary purpose of the
Background is to set up a common context or perform actions that are
applicable to every scenario in that particular feature. There are various
benefits of using Background in feature file, they are listed as follows:
Common steps: The Background section allows you to specify steps
that are common to all Scenarios in the feature file.
Avoiding redundancy: Benefit of using Background is to avoid
duplicating the same set of steps in each scenario. Common setup
steps are centralized in the Background, making feature files more
concise and easier to maintain.
Execution before each scenario: The steps in the Background are
executed before the steps of each individual Scenario, this ensures a
consistent starting point for every scenario.
Improved readability: Background enhances the readability of the
feature file by separating generic setup steps from scenario-specific
steps. It makes feature files more focused on the unique aspects of
each Scenario.
Let us see some examples by using Background. In this example, the
common step Given the user is on the login page is placed in the
Background section. This step will be executed before the steps of
both the Successful Login and Invalid Login Attempt Scenarios.
Feature: User Authentication
Background:
Given the user is on the login page
Scenario: Successful Login
When the user enters valid credentials
Then the user should be logged in
Scenario: Invalid Login Attempt
When the user enters invalid credentials
Then the user should see an error message
Let us take one more example where we have used Background:
Feature: Purchase Products on E-commerce Site
Background:
Given the e-commerce site is open
And a user is logged in
Scenario: Add product to cart
When the user searches for "laptop"
And adds the "Dell Laptop" to the cart
Then the cart should contain "Super Laptop"
Scenario: Remove product from cart
When the user navigates to the shopping cart
And removes the "Dell Laptop" from the cart
Then the cart should be empty
In the preceding example The Background section contains steps that are
common to all Scenarios, like opening the e-commerce site and ensuring that a
user is logged in.
Cucumber Data Table: Cucumber Data Table is a way to provide input data in
a structured tabular format within a Gherkin scenario.
Let us consider an example using the site https://www.saucedemo.com/ where
we want to test the login functionality with a Data Table for the username and
password, as follows:
Feature: Login functionality on https://www.saucedemo.com/
Scenario: User login with different credentials
Given the user is on the login page
When the user enters the following credentials
| Username | Password |
| locked_out_user | secret_sauce |
And clicks the login button
Then the user should be on the home page
The When step includes a Data Table with credentials that is used for login into
the application. The | Username | Password | line defines the header of the
Data Table. The subsequent rows | locked_out_user | secret_sauce |
represent different sets of credentials.
Let us create a feature file with a data table to test multiple sets of input
parameters:
Feature: Registration on site.
Scenario: User registration with various details
Given the user is on the registration page
When the user enters the following details
| First Name | Last Name | Email |
Password |
| <FirstName> | <LastName> | <Email> |
<Password> |
And clicks the register button
Then the user should receive a registration
confirmation
Examples:
| FirstName | LastName | Email |
Password |
| John | Doe | john.doe@example.com |
secret123 |
| Alice | Smith | alice.smith@example.com |
password456 |
| Bob | Johnson | bob.johnson@example.com |
secure789 |
Let us see the explanation of each of the line of the above example:
Feature: Specifies the feature registration on a website.
Scenario: Describes a specific test scenario for user registration.
Given: Sets the initial state that user on the registration page.
When: Describes the action when user enters different set of data using
placeholders <FirstName>, <LastName>, <Email>, and <Password>.
Placeholders are replaced with actual values in the examples section to
test various Scenarios.
And: Describes an additional action (user clicks the register
button).
Then: Specifies the expected outcome (user should receive a
registration confirmation).
Examples: Provides concrete test data for the scenario, testing multiple
cases with different user details.
Integrating Cucumber with Playwright
Integrating Playwright with Cucumber streamlines the automation of web
applications through the structured approach of BDD. By combining the
intuitive behavior-driven approach of Cucumber with the powerful automation
capabilities of Playwright, developers can seamlessly write and execute end-to-
end tests that ensure their web applications function flawlessly.
The following steps can be implemented to integrate Cucumber with
Playwright:
1. Run the following command npm i @cucumber/cucumber as it will install
Cucumber:
Figure 8.2: Install Cucumber
2. Create two folders with name features and step_definations under
tests folder.
a. features: Folder for to create the features file.
b. step_definations: Where we write our actual test implementation.
In the following screenshot you can see two sub folders with name
'features and step_definations are created under tests folder:
Figure 8.3: Features and Step definition
3. Create feature file under the folder feature which covers the login
function of the site https://talent500.co/auth/signin.
As an example, we are covering the following use case:
a. Open the site https://talent500.co/auth/signin.
b. Enter email and password.
c. Click on login button.
d. Verify user should logged in successfully.
e. Finaly verify user is log-out successfully.
Let us create the feature file of the above use case:
Feature: Login Functionality
Scenario: Login Functionality
Given User navigates to the application
When I enter the username
"applitoolsautomation@yopmail.com" and password as
"Test@123"
When I click on login button
Then User should logged in successfully
Then Logout from the application
4. reate a runner file in the root directory of the project to execute the test
case, let us give it the name cucumber.json.
{
"default" :{
"paths":["tests/features/"],
"dryRun": false,
"publishQuiet":true,
"require":
["tests/step_definations/*.js","./hooks/*.js"]
}
}
5. After creating the feature file, the next step is to proceed with writing the
test. In the following example, we have outlined the various methods:
a. gotoLoginPage()
b. loginIntoApplication(username, password)
c. clickLoginButton()
d. verifyUserInHomePage(homeContent)
e. logoutFromApplication()
const { chromium, expect } =
require("@playwright/test");
exports.LoginPage = class LoginPage {
constructor(page, expect) {
this.page = page;
this.username_field =
this.page.getByPlaceholder("Email");
this.password_field =
this.page.getByPlaceholder("Password");
this.login_button = this.page.locator('[data-
id="submit-login-btn"]');
this.drop_down =
this.page.locator('[alt="DropDown Button"]');
this.log_out = this.page.locator('[data-id="nav-
dropdown-logout"]');
}
async gotoLoginPage() {
await
this.page.goto("https://talent500.co/auth/signin");
}
async loginIntoApplication(username, password) {
await this.username_field.fill(username);
await this.password_field.fill(password);
await this.login_button.click();
}
async clickLoginButton() {
await this.login_button.click();
}
async verifyUserInHomePage(homeContent) {
const text = await
this.page.locator('//div[@id="progress-
bar"]').textContent();
expect(text).toContain(homeContent);
}
async logoutFromApplication() {
await this.drop_down.click();
await this.log_out.click();
}
};
6. We have created various methods as per the use case the subsequent step
is to invoke these methods within the test case steps.
Step definitions are the piece of code that maps the plain-text Gherkin
steps in feature files to executable code. They define what actions should
be taken when each step in the scenario is encountered during test
execution. In this, we have created with name login.js as follows:
const { Given, When, Then } =
require("@cucumber/cucumber");
const { chromium, expect } =
require("@playwright/test");
const { LoginPage } = require("../login.spec");
let browser;
let page;
let loginPage;
Given("User navigates to the application", async ()
=> {
browser = await chromium.launch({ headless: false
});
const context = await browser.newContext();
page = await context.newPage();
loginPage = new LoginPage(page);
loginPage.gotoLoginPage();
});
When("I enter the username {string} and password as
{string}",
async (username, password) => {
loginPage.loginIntoApplication(username,
password);
}
);
When("I click on login button", async () => {
loginPage.clickLoginButton();
});
Then("User should logged in successfully", async ()
=> {
loginPage.verifyUserInHomePage("PROFILE");
});
Then("Logout from the application", async () => {
loginPage.logoutFromApplication();
});
7. Next step is to execute the test case run the command npm run test.
This command runs the script from package.json. The npm run
cucumber-js test command is typically used in a Node.js project with
Cucumber.js for running automated tests written in Gherkin syntax:
Figure 8.4: cucumber-js
In the following screenshot, you can see there is only one scenario with five
steps executed successfully:
Figure 8.5: Test Scenario execution
In the next section, you will learn how to generate the Cucumber HTML report.
Cucumber HTML reporter
Cucumber HTML reporter generates user-friendly HTML reports that offer a
visually appealing representation of test outcomes, making it easier for
stakeholders to understand the status and details of executed tests. For
generating the HTML report, we are using the default Cucumber report.
For the Cucumber HTML report demo, we are taking the example of the site
https://www.saucedemo.com/. We are covering the following use case for
demo purposes:
Scenario 1: Test login and logout functionality
1. Open the site https://www.saucedemo.com/
2. Login into the site with valid data
3. Logout from the application
4. Verify after logout user in the log-in page
Scenario 2: Test user able to log in, add, then remove product from the cart
1. Open the site https://www.saucedemo.com/
2. Login into the site with valid data
3. Add the Product to the cart
4. Remove the added product
5. Logout from the application
6. Verify after logout user in the log-in page
Let us create the feature file and give name saucelogin.feature
Feature: Login Functionality
Background:
Given Open the URL
Scenario: Login and Logouut Functionality for site
https://www.saucedemo.com/
Given Open the URL
When User enter the username "<username>" and password
"<password>"
And User click on login button
Then User logged in successfully
When User click on HamburgerMenu
And Logout from the site
Then User should be in login page
Examples:
| username | password |
| standard_user | secret_sauce |
Scenario: Login,Add,Remove ProductFrom Cart
Functionality for site https://www.saucedemo.com/
When User enter the username "<username>" and password
"<password>"
And User click on login button
Then User logged in successfully
And User add product into the cart
And User remove the product from the cart
When User click on HamburgerMenu
When Logout from the site
Then User should be in login page
Examples:
| username | password |
| visual_user | secret_sauce |
Now, let us create methods that we can use to implement our Scenarios:
const { chromium, expect } = require("@playwright/test");
let browser
exports.SauceLoginPage = class SauceLoginPage {
constructor(page, expect) {
this.page = page;
this.username = this.page.locator('[data-
test="username"]');
this.password = this.page.locator('[data-
test="password"]');
this.login_button = this.page.locator('[data-
test="login-button"]');
this.addCart_button = this.page.locator('[data-
test="add-to-cart-sauce-labs-backpack"]');
this.homePage_Content =
this.page.locator('//span[@class="title"]');
this.removeThe_Product = this.page.locator('[data-
test="remove-sauce-labs-backpack"]');
this.humBurger = this.page.locator('[id="react-burger-
menu-btn"]');
this.addCart_button = this.page.locator('[data-
test="add-to-cart-sauce-labs-backpack"]');
this.logOut_button =
this.page.locator('[id="logout_sidebar_link"]');
}
async gotoLoginPage() {
await this.page.goto("https://www.saucedemo.com/");
}
async loginIntoApplication(username, password) {
await this.username.fill(username);
await this.password.fill(password);
}
async clickLoginButton() {
await this.login_button.click();
}
async addProductToCart() {
await this.addCart_button.click();
}
async removeProductFromCart() {
await this.removeThe_Product.click();
}
async clickOnHamburgerMenu() {
await this.humBurger.click();
}
async verifyUserInHomePage(homeContent) {
const text = await
this.homePage_Content.textContent();
expect(text).toContain(homeContent);
}
async logoutFromApplication() {
await this.logOut_button.click();
}
async userInloginPage() {
await expect(this.login_button).toBeVisible()
}
};
We have created various methods. The next step is to understand these created
methods in steps.
const { Given, When, Then } =
require("@cucumber/cucumber");
const { chromium, expect } = require("@playwright/test");
const { SauceLoginPage } = require("../saucelogin.spec");
let browser;
let page;
let loginPage;
Given("Open the URL", async () => {
browser = await chromium.launch({ headless: false });
const context = await browser.newContext();
page = await context.newPage();
loginPage = new SauceLoginPage(page);
loginPage.gotoLoginPage();
});
When(
"User enter the username {string} and password
{string}",
async (username, password) => {
loginPage.loginIntoApplication(username, password);
}
);
When("User click on login button", async () => {
loginPage.clickLoginButton();
});
When("User add product into the cart", async () => {
loginPage.addProductToCart();
});
When("User remove the product from the cart", async () =>
{
loginPage.removeProductFromCart();
});
Then("User logged in successfully", async () => {
loginPage.verifyUserInHomePage("Products");
});
When("User click on HamburgerMenu", async () => {
loginPage.clickOnHamburgerMenu();
});
Then("Logout from the site", async () => {
loginPage.logoutFromApplication();
});
Then("User should be in login page", async () => {
loginPage.userInloginPage();
});
Generate Cucumber HTML Reporter: To generate the default Cucumber, we
have to run the command: npm run test tests/features/saucelogin.feature
--format html:cucumber-report.html.
As we run the preceding command cucumber-report.html report is generated
in the root folder, as shown in Figure 8.6:
Figure 8.6: Cucumber-report-html
Open the generated HTML report as shown in Figure 8.7:
Figure 8.7: Cucumber HTML report
Conclusion
To recap, this chapter has provided an in-depth examination of Cucumber and
its various features essential for effective BDD in software testing. We explored
the fundamentals of Cucumber, including its hooks, tags, Background, and
Data Table functionalities, all of which contribute to the flexibility and
organization of testing Scenarios. Furthermore, we demonstrated how
Cucumber seamlessly integrates with Playwright, combining the power of both
tools for comprehensive testing solutions. Finally, we introduced the Cucumber
HTML reporter, a valuable resource for generating visually engaging and
interactive HTML reports from Cucumber test outcomes.
In the upcoming chapter, you will learn about the usage of Playwright for API
testing, discussing its relevance and benefits. We will look into various types of
HTTP requests commonly used in API testing, including GET, POST, PUT, and
DELETE requests. Additionally, we will discuss the best practices in API.
Multiple choice questions
1. What is Cucumber primarily used for in software development?
a. Unit testing
b. Integration testing
c. End-to-end testing
d. Performance testing
2. Which programming language is commonly used for writing step
definitions in Cucumber?
a. Java
b. Python
c. Ruby
d. JavaScript
3. What is the purpose of a step definition in Cucumber?
a. To define the behavior of a feature
b. To describe the acceptance criteria of a scenario
c. To provide the implementation for a step in a scenario
d. To document the feature files
4. What is the purpose of feature files in Cucumber?
a. To define test Scenarios in a human-readable format
b. To store test data
c. To configure test environments
d. To implement test logic
5. Which command is used to generate a Cucumber HTML report?
Which command is used to generate a Cucumber HTML report?
a. generate-report
b. create-html-report
c. cucumber-html-report
d. make-report
Answers
1. c.
2. c.
3. c.
4. a.
5. c.
Join our book’s Discord space
Join the book's Discord Workspace for Latest updates, Offers, Tech happenings
around the world, New Release and Sessions with the Authors:
https://discord.bpbonline.com
CHAPTER 9
API Testing Using Playwright,
Authentication, and Session Storage
Introduction
In the preceding chapter, we learned about Cucumber, a powerful testing tool
widely employed for behavior-driven development (BDD) in software testing.
We then explored Cucumber hooks, tags, Background and Data Table, pivotal
components that enhance the flexibility and organization of Cucumber tests. You
have seen the integration of Cucumber with Playwright and finally, the chapter
concluded by introducing the Cucumber HTML reporter, a valuable tool for
generating visually compelling and interactive HTML reports from Cucumber test
results.
This chapter is designed to equip readers with a comprehensive understanding of
API testing using Playwright, a robust testing framework. It looks at the
fundamentals of APIs, explores the significance of API testing, and elucidates why
Playwright stands out as an optimal choice for API testing. By providing step-by-
step instructions, it guides readers through setting up Playwright for API testing
and demonstrates practical examples for testing various HTTP methods such as
GET, POST, PUT, and DELETE requests. Additionally, it covers topics like
authentication, session storage, and how to effectively mock the APIs.
Structure
This chapter covers the following topics:
About application programming interface
Application programming interface testing
Playwright for API testing
Setup Playwright for API testing
GET request test with example
POST request test with example
PUT request test with example
DELETE request test with example
API testing with authentication
Authentication and session storage
Mock the APIs
Objectives
By the end of this chapter, you will have a solid foundation in API testing using
Playwright, enabling you to confidently conduct GET, POST, and PUT request
tests. The practical examples provided throughout the chapter will empower you
to apply your knowledge effectively in real-world scenarios, ensuring the
reliability and functionality of APIs in your projects.
About application programming interface
Application programming interface (API) serves as a communication channel
between different software components or applications. In the realm of
automation testing, APIs provide a programmatic way for automated tests to
interact with the backend functionalities of an application, bypassing the user
interface (UI). An API acts as a messenger between different software
applications or components. It defines a set of rules and specifications that govern
how these applications can communicate with each other.
API workflow
Here is an explanation of an API workflow using the analogy of a waiter and a
kitchen:
Figure 9.1: API workflow
Imagine a restaurant:
You (the user) are sitting at a table (the user application).
The waiter (the API) acts as an intermediary between you and the kitchen
(the backend services).
The menu (the API documentation) lists all the available dishes
(functionalities) and their descriptions (parameters).
Let us explore the steps mentioned above in detail with respect to API.
1. Placing an order (sending a request):
a. You browse the menu (API documentation) and decide on a dish
(desired functionality).
b. You tell the waiter (the API) what you want (send a request specifying
the endpoint and any necessary data).
This data might include your table number (user identification), desired
dish name (functionality name), and any preferences (parameters like size
and customizations).
2. Delivering the order to the kitchen (routing the request):
a. The waiter (the API) receives your request and understands what you
want based on the menu (API documentation).
b. The waiter does not cook the food themselves (the API does not
perform the action directly).
c. They take your order (request) to the kitchen (route it to the
appropriate backend service).
3. Preparing the food (processing the request):
a. The chefs in the kitchen (backend service) receive your order
(request) from the waiter (API).
b. They have the recipes and ingredients (application logic and data) to
prepare the dish (perform the functionality).
c. They cook the food according to your order (process the request based
on the data provided).
4. Bringing the food back (sending the response):
a. Once the food is prepared (the request is processed), the chefs
(backend service) give it back to the waiter (the API).
5. Serving the food (delivering the response):
a. The waiter (the API) receives the prepared dish (response) from the
kitchen (backend service).
b. They then bring it to you (the user application) at your table
(designated location).
6. Enjoying the food (using the response data):
a. You receive the food (response data) from the waiter (the API).
b. You can then enjoy it (use the data in your application) as intended.
c. This might involve displaying it on the screen, saving it for later use,
or triggering other actions based on the data.
In the next section, you will see the following topics: API testing, the API layer in
test automation pyramid, components of an API, and the benefits of API testing.
Application programming interface testing
API testing is a type of software testing that specifically targets the verification of
APIs. The primary goal of API testing is to ensure that APIs function correctly and
reliably. This involves checking not only the basic functionality but also aspects
such as performance and security. Unlike traditional testing methods that rely on
user inputs via keyboards or mouse clicks and outputs displayed through
graphical user interfaces (GUI), API testing involves sending requests directly
to the API endpoints. These requests can be made using specialized software or
programming scripts. The system's responses to these requests are then analyzed
and compared against expected outcomes.
API tests do not concern themselves with the visual aspects or user interface of
the software application. Instead, they concentrate on assessing the business logic
layer of the software architecture. This means that API testing is primarily
concerned with verifying the functionality and behavior of the software at the
programming interface level.
API layer in test automation pyramid
The test automation pyramid is a popular testing strategy that emphasizes a
hierarchical approach to automated testing, with different levels of tests arranged
in a pyramid shape.
The three main layers of the pyramid are:
Unit tests: These are the foundational tests at the base of the pyramid. Unit
tests focus on testing individual units or components of the software in
isolation. They are typically written and executed by developers and aim to
verify the correctness of the smallest units of code, such as functions or
methods.
API layer tests: The middle layer of the pyramid consists of tests that
interact with the application's APIs. These tests, also known as integration
tests, verify the interactions between different components of the system by
sending requests to APIs and validating the responses. They help ensure that
the APIs are functioning correctly and that different parts of the system can
communicate effectively with each other.
GUI tests: At the top of the pyramid are GUI tests, which focus on testing
the application's user interface. These tests simulate user interactions with
the application's graphical elements, such as buttons, forms, and menus, and
verify that the application behaves as expected from a user's perspective.
In the following figure, you will see the three main layers of the pyramid:
Figure 9.2: Testing pyramid
In most high-level software applications, there are three main layers: the
presentation layer, the business layer, and the database layer, as shown in the
following figure:
Figure 9.3: Different layers
In the following figure, you will see the three main layers of the pyramid.
Presentation layer: This layer, also sometimes called the user interface
(UI) layer, is the part of the application that users interact with directly. It
includes all the visual elements like buttons, menus, text boxes, and
graphics. The presentation layer focuses on providing a user-friendly and
intuitive experience for interacting with the application.
Business layer: This layer sits behind the scenes and handles the core
functionalities of the application. It receives input from the presentation
layer, processes it according to the application's business logic, and interacts
with the database layer to retrieve or store data as needed. The business
layer is responsible for implementing the business rules and logic of the
application, ensuring the smooth execution of its core functions. API testing
often focuses on the business layer, as APIs provide programmatic access to
these functionalities.
Database layer: This layer is responsible for storing and managing the
application's data. It interacts with the business layer to provide and store
data based on its instructions. The database layer ensures efficient data
storage, retrieval, and manipulation, often using a database management
system (DBMS) like MySQL, Oracle, or SQL Server.
Various components of the API
The following are the components of an API:
Endpoints: Endpoints are specific URLs within the API that define the
actions that it can perform. For example, an e-commerce API might have
endpoints for retrieving product information, adding items to a cart, or
processing payments.
Requests: Requests are messages sent to the API, specifying the desired
action and any necessary data. Analogously, it is like placing an order with a
waiter. You tell the waiter what you want (the dish) and any modifications
(like no onions or extra cheese). In API terms, requests include the HTTP
method (such as GET, POST, PUT, or DELETE) and any parameters or data
needed for the operation.
Responses: Responses are messages sent back from the API after
processing your request. They contain the results of the requested action or
any error messages. Imagine the waiter bringing your food or informing you
if a particular dish is unavailable. In APIs, responses include status codes
(e.g., 200 for success, 404 for not found) and the relevant data (such as
JSON or XML).
Protocols: Protocols define the format and structure of requests and
responses, ensuring smooth communication between the client (your
application) and the server (the API). It is like a language both sides
understand. Common protocols include:
Hypertext Transfer Protocol (HTTP): Used for web APIs. It defines
how data is transmitted over the internet.
Representational State Transfer (REST): A popular architectural style
for designing networked applications. RESTful APIs follow specific
conventions for creating, reading, updating, and deleting resources.
Benefits of API testing
Now, let us see some of the benefits of API testing:
Improved efficiency and faster releases:
Faster test execution: Compared to GUI testing, which can be slower
due to UI rendering and interaction, API tests are much faster to execute.
This allows for quicker feedback loops and faster identification of issues
during development.
Early bug detection: By focusing on the core functionalities exposed by
APIs, API testing can uncover bugs and potential errors early in the
development lifecycle. Fixing bugs earlier is easier and less expensive
than fixing them later in the development process.
Automation potential: A large portion of API testing can be automated
using testing tools. This reduces manual effort and allows for frequent
regression testing to ensure functionality remains intact with code
changes.
Enhanced quality and reliability:
Thorough functionality testing: API testing allows for testing core
functionalities in isolation from the UI. This enables a more focused and
in-depth validation of the application's logic and data handling.
Improved performance testing: API testing can be used to assess the
performance of the API under various load conditions. This helps ensure
the API can handle real-world usage patterns and user traffic without
compromising performance.
Security testing: API testing can be used to identify security
vulnerabilities like unauthorized access or data breaches. This helps to
ensure the API is secure and protects sensitive data.
Cost-effectiveness and maintainability:
Reduced testing costs: By automating a significant portion of testing
and catching bugs early, API testing can help to reduce overall testing
costs.
Lower maintenance effort: API tests are generally less susceptible to
changes in the UI compared to GUI tests. This reduces the maintenance
burden associated with keeping tests up to date.
Reusable tests: Since APIs provide programmatic access to core
functionalities, API tests can often be reused across different applications
that integrate with the same API.
Integration and reusability:
Shift-left testing: API testing can be integrated into earlier stages of the
development lifecycle (Shift-left testing). This allows for earlier
identification and resolution of issues, leading to a smoother
development process.
Improved integration testing: By testing APIs independently,
developers can ensure seamless integration between different parts of the
application or even between different applications that leverage the same
API.
Examples of APIs:
Weather apps: These use weather APIs to retrieve weather data and display
it to users.
Social media apps: They leverage social media APIs to allow users to share
content and interact with friends.
Payment gateways: Online stores integrate payment gateway APIs to
process secure online payments.
Map applications: These utilize map APIs to provide users with directions,
traffic information, and location-based services.
Playwright for API testing
Microsoft Playwright for API testing because of its robust features and
capabilities. With Playwright, developers can easily automate their API testing
processes with its user-friendly syntax and powerful functionalities. One key
advantage of using Playwright is its cross-browser compatibility, allowing
developers to test their APIs on different browsers without any hassle. Playwright,
while primarily known for end-to-end web application testing, offers valuable
functionalities for API testing as well.
Here are some key reasons why you might consider using Playwright for your API
testing needs:
Direct API interaction: Playwright allows you to directly send HTTP
requests to your application's API endpoints this enables you to test your
API's behavior without needing to launch a browser.
Enhanced control and flexibility: Compared to dedicated API testing
tools, Playwright provides more granular control over your requests and
responses. You can construct complex requests with various headers, body
formats (JSON, form-data, etc.), and authentication mechanisms, allowing
for thorough testing of different scenarios.
Efficient test suite management: Playwright integrates seamlessly with
JavaScript testing frameworks like Jest and Mocha, enabling you to manage
both UI and API tests within a single framework. This simplifies the testing
process and reduces context switching between different tools.
Integration with end-to-end testing: Playwright's ability to perform both
API and UI testing allows you to create comprehensive test scenarios that
involve interactions with both the backend and frontend components of
your application. This facilitates testing user journeys that involve API calls
and browser interactions. You can set up scenarios where API calls are
made within the browser context, simulating user interactions and
validating server responses alongside UI behavior.
Rich assertion library: Playwright provides a comprehensive assertion
library that allows you to verify various aspects of API responses, including
status codes, headers, response body content, and JSON schema validation.
Scalability and flexibility: Whether you are running a small set of API
tests or a large suite of complex scenarios, Playwright's scalability and
flexibility make it an ideal choice for teams of all sizes looking to improve
their testing capabilities.
Setup Playwright for API testing
Setting up a new project using Playwright is indeed a straightforward process. Let
me break down the steps for you:
1. Create a project directory:
a. Open your terminal or command prompt.
b. Run the following commands:
mkdir playwright-apiAutomation
cd playwright-apiAutomation
2. Create a new Playwright project:
a. Once you are inside the newly created directory, initialize the project
using npm (Node Package Manager). Run the following command:
npm init playwright@latest
3. Select the right options from the terminal during installation: During
the initialization process, you will be prompted to choose various options.
Here is what you should select:
Opt for JavaScriptas the language.
Save the tests in the test's directory. Keeping tests in a separate
directory helps organize your project structure and makes it easier to
manage.
In the next section, you will see the create, read, update, and delete (CRUD)
operation and how we can automate this operation in Playwright.
GET request test with example
A GET request test in Playwright verifies the behavior of an API endpoint that
uses the GET method to retrieve data. It is like asking for information from a
database or another service over the internet. The GET method is one of the HTTP
methods used for making requests. It is commonly used to fetch data.
Here is an example of a GET request test using Playwright:
For demo purposes, we are taking the example of the site https://reqres.in/.
Before taking the example of GET request, we have to set baseURL in config file:
use: {
baseURL: 'https://reqres.in',
extraHTTPHeaders: {
'Accept': 'application/json'
},
test('GET Request To verify Response Status', async ({
request }) => {
const response = await request.get('/api/users/3');
expect(response.status()).toBe(200)
})
Here is a breakdown of what this test is doing:
The test() function defines a new test case. The first argument is the name
of the test, and the second argument is an asynchronous function that
contains the test logic.
Inside the test function, we are using request.get('/api/users/3') to make
a GET request to the /api/users/3 endpoint.
Then using expect(response.status()).toBe(200) to assert that the
response status code is 200. response.status() returns the HTTP status
code of the response, and toBe(200) checks if it is equal to 200.
If the response status is not 200, the test will fail, indicating that there is an
issue with the API endpoint or the request itself.
Following is an example of GET request with getting user details and verifying
them:
test('GET Request With Getting User Detail And Verify it',
async ({ request }) => {
const response = await request.get('/api/users/2');
const responseBody = JSON.parse(await response.text())
expect(response.status()).toBe(200)
expect(responseBody.data.id).toBe(2)
expect(responseBody.data.first_name).toBe('Janet')
expect(responseBody.data.last_name).toBe('Weaver')
expect(responseBody.data.email).toBeTruthy()
})
Here is a breakdown of what each line does:
const response = await request.get('/api/users/2');: Sends a GET
request to the '/api/users/2' endpoint and awaits the response.
const responseBody = JSON.parse(await response.text()): Extracts the
response body and parses it as JSON.
expect(response.status()).toBe(200): Asserts that the response status is
200.
expect(responseBody.data.id).toBe(2): Asserts that the user ID in the
response body is 2.
expect(responseBody.data.first_name).toBe('Janet'): Asserts that the
user's first name is Janet.
expect(responseBody.data.last_name).toBe('Weaver'): Asserts that the
user's last name is Weaver.
expect(responseBody.data.email).toBeTruthy(): Asserts that the user's
email is truthy (i.e., not null or undefined).
POST request test with example
A POST request is an HTTP method used to send data to a server. It is commonly
used for creating or updating resources on the server.
1. POST request with Static data:
test('POST Request To Generate New Record In DB',
async ({ request }) => {
const response = await request.post('/api/users/',
{
data: {
name: 'Kailash',
job: 'QA Manager',
},
})
const responseBody = JSON.parse(await
response.text())
expect(responseBody.createdAt).toBeTruthy()
userId =responseBody.id;
expect(responseBody.id).toBe(userId)
expect(responseBody.name).toBe('Kailash');
expect(responseBody.job).toBe('QA Manager');
})
Here is a breakdown of what each line does:
const response = await request.post('/api/users/', {...}):
Sends a POST request to the '/api/users/' endpoint with the
provided data (name and job) and awaits the response.
const responseBody = JSON.parse(await response.text()): Extracts
the response body and parses it as JSON.
expect(responseBody.createdAt).toBeTruthy(): Asserts that a
'createdAt' field exists in the response body, indicating that the
record was successfully created and has a creation timestamp.
userId = responseBody.id;: Stores the ID of the newly created user
from the response body.
expect(responseBody.id).toBe(userId):
Asserts that the id field in
the response body matches the ID stored in the userId variable,
ensuring consistency.
expect(responseBody.name).toBe('Kailash'): Asserts that the name
of the created record is 'Kailash'.
expect(responseBody.job).toBe('QA Manager'): Asserts that the job
of the created record is 'QA Manager'.
2. POST request with dynamic data:
A POST request with dynamic data typically refers to sending a POST
request to a server with data that can change or vary dynamically based on
user input. In many web applications, POST requests are used to create or
update resources on the server. The data sent in the POST request body can
vary depending on the context or user interaction. This data might be
collected from a form, generated based on user input or retrieved from
other sources.
Here is an example scenario to illustrate a POST request with dynamic
data. For to generate the dynamic data we have to install two dev
dependency.
Faker: Faker.js is a JavaScript library commonly used for generating fake
data. It provides a wide range of methods to generate fake data for different
types of data, including names, addresses, phone numbers, emails, and
more.
luxon: Luxon is a JavaScript library that provides powerful and
comprehensive functionality for working with dates and times. Its primary
purpose is to make it easier to work with dates, times, and time zones in
JavaScript applications.
npm i @faker-js/faker
npm i luxon
import { test, expect } from '@playwright/test'
import { faker } from '@faker-js/faker';
const { DateTime } = require("luxon");
const randomFirstName = faker.person.firstName()
const randomLastName = faker.person.lastName()
const randomNumber = faker.string.numeric(4)
const currentDate = DateTime.now().toFormat('yyyy-MM-
dd')
const checkOutTime = DateTime.now().plus({ days: 5
}).toFormat('yyyy-MM-dd')
test.describe.parallel('API Testing', () => {
test('Create Booking', async ({ request }) => {
const response = await request.post(`/booking`, {
data: {
"firstname": randomFirstName,
"lastname": randomLastName,
"totalprice": randomNumber,
"depositpaid": true,
"bookingdates": {
"checkin": currentDate,
"checkout": checkOutTime
},
"additionalneeds": "Lunch"
}
});
console.log(await response.json());
expect(response.ok()).toBeTruthy();
expect(response.status()).toBe(200);
const responseBody = await response.json()
expect(responseBody.booking).toHaveProperty("firstname"
, randomFirstName);
expect(responseBody.booking).toHaveProperty("lastname",
randomLastName);
});
})
PUT request test with example
A PUT request is commonly used in RESTful APIs to update existing resources or
records. Testing a PUT request typically involves sending a request with updated
data to an API endpoint that handles updates and then verifying that the resource
has been correctly modified.
test('PUT Request - Update The Existing User', async ({
request }) => {
const response = await request.put('/api/users/2', {
data: {
name: 'Harry',
job: 'QA Architect',
},
})
const responseBody = JSON.parse(await response.text())
expect(response.status()).toBe(200)
expect(responseBody.name).toBe('Harry')
expect(responseBody.job).toBe('QA Architect')
expect(responseBody.updatedAt).toBeTruthy()
})
Here is a breakdown of what each part does:
const response = await request.put('/api/users/2', { ... }): Sends a
PUT request to the '/api/users/2' endpoint with the provided updated data
(name and job), and awaits the response.
const responseBody = JSON.parse(await response.text()): Parses the
response body as JSON.
expect(response.status()).toBe(200): Asserts that the response status is
200, indicating a successful update.
expect(responseBody.name).toBe('Harry'): Asserts that the name field in
the response body matches the updated name 'Harry'.
expect(responseBody.job).toBe('QA Architect'): Asserts that the job field
in the response body matches the updated job 'QA Architect'.
expect(responseBody.updatedAt).toBeTruthy(): Asserts that an
'updatedAt' field exists in the response body, indicating that the record was
updated and has a timestamp for the update.
DELETE request test with example
A DELETE request is one of the standard HTTP methods used to request the
removal of a resource from a server. When a client sends a DELETE request to a
server, it indicates that the client wants to remove the resource specified by the
request URL:
test('DELETE Request - Delete User', async ({ request }) =>
{
const response = await
request.delete('/api/users/'+userId)
expect(response.status()).toBe(204)
})
Here is a breakdown of what each part does:
const response = await request.delete('/api/users/'+userId): Sends a
DELETE request to the '/api/users/' endpoint with the specified user ID
appended to the URL, indicating the user to be deleted.
expect(response.status()).toBe(204): Asserts that the response status is
204, indicating a successful deletion. This ensures that the user record was
successfully removed from the database.
API testing with authentication
Testing the authentication functionality of an API involves verifying that the
authentication process works as expected and that authenticated users can perform
authorized actions.
The following script performs two tests: one to generate a token by authenticating
with a username and password and another to use that token to update data
associated with a booking.
const { test, expect } = require("@playwright/test");
var token;
test("Generate Token", async ({ request }) => {
const response = await request.post('/auth', {
data: {
username: "admin",
password: "password123",
},
});
console.log(await response.json());
expect(response.ok()).toBeTruthy();
expect(response.status()).toBe(200);
const responseBody = await response.json();
token = responseBody.token;
console.log("New Token is: " + token);
});
test("Used token to Update the Data", async ({ request }) =>
{
const updateRequest = await request.put('/booking/1', {
headers: {
Accept: "application/json",
Cookie: `token=${token}`,
},
data: {
firstname: "Kailash",
lastname: "Pathak",
totalprice: 121,
depositpaid: true,
bookingdates: {
checkin: "2024-06-01",
checkout: "2024-06-15",
},
additionalneeds: "Dinner",
},
});
expect(updateRequest.ok()).toBeTruthy();
expect(updateRequest.status()).toBe(200);
const updatedResponseBody = await updateRequest.json();
expect(updatedResponseBody).toHaveProperty("firstname",
"Kailash");
expect(updatedResponseBody).toHaveProperty("lastname",
"Pathak");
expect(updatedResponseBody).toHaveProperty("totalprice",
121);
});
Let us break down the above steps:
1. Generate token:
a. First, we will send a POST request to the /auth endpoint with
username and password.
b. It expects a successful response (status code 200) and extracts the
token from the response body.
c. The token is stored in the variable token.
2. Use the token to update data:
a. Send a PUT request to the /booking/1 endpoint with updated data.
b. It includes the token in the request headers for authentication.
c. It expects a successful response (status code 200) and checks if the
updated data matches the expected values.
Authentication and session storage
Authentication is the process of verifying the identity of a user, client, or system
before granting access to protected resources or services. On the other hand,
session storage refers to the mechanism used to store and manage session data,
such as user preferences, shopping carts, or any other relevant information that
needs to persist across API calls.
Explanation of authentication and session storage using Playwright:
The Playwright performs tests within isolated environments known as browser
contexts. This isolation model enhances reproducibility and mitigates the risk of
cascading test failures.
By allowing tests to leverage pre-existing authenticated states, the necessity to
authenticate in each test is eliminated, resulting in accelerated test execution.
Irrespective of the authentication approach you opt for, it is probable that you will
store authenticated browser states on the file system.
Normally, we have to keep the login part in before() or beforeEach() hooks if
there are number of test cases that are using the same authentication. To solve this
problem in Playwright we have a mechanism of login once and reuse that state
across all of our test cases.
Let us understand by taking the example of the site:
https://automationexercise.com/login.
We have two use cases explained ahead.
Example 1:
In the following example, we are verifying:
1. The user is logged in to the application.
2. Verifying text Logout after log-in.
3. Verifying text Test Cases after clicking on the link Test Cases.
import { test, expect } from '@playwright/test';
test('test 1', async ({ page }) => {
await page.goto("https://automationexercise.com/login");
await page.locator('input[data-qa="login-
email"]').fill("ncrmeet1@yopmail.com");
await page.locator('input[data-qa="login-
email"]').press("Tab");
await
page.locator('input[placeholder="Password"]').fill("Test
@1234");
await page.locator('button[data-qa="login-
button"]').click();
await expect(page.getByRole('link', { name: 'Logout'
})).toBeVisible();
await page.getByRole('link', { name: 'Test Cases', exact:
true }).click();
await
expect(page.locator('#form')).toContainText('Test
Cases');
});
Example 2:
In the following example, we are verifying:
1. The user is logged-in into the application.
2. Click on the cart section and verify that the cart is empty.
3. Verifying text APIs List for practice after clicking on the link API
Testing.
import { test, expect } from '@playwright/test';
test('test 2', async ({ page }) => {
await page.goto("https://automationexercise.com/login");
await page.locator('input[data-qa="login-
email"]').fill("ncrmeet1@yopmail.com");
await page.locator('input[data-qa="login-
email"]').press("Tab");
await
page.locator('input[placeholder="Password"]').fill("Test
@1234");
await page.locator('button[data-qa="login-
button"]').click();
await page.getByRole('link', { name: 'Cart'
}).click();
await
expect(page.locator('#empty_cart')).toContainText('Cart is
empty!');
await page.getByRole('link', { name: 'API Testing'
}).click();
await expect(page.locator('#form')).toContainText('APIs
List for practice');
});
In the above example, the login part is common, so there is duplication of the
code, but Playwright has a mechanism of login once and reuse that state across all
of our test cases. Let us see how we can implement it:
1. Create global file: We will create a global file; this global file will run
once before all of your test cases. In global file we will put the
authentication code then we will use Playwright storage state capability to
store the state of your application.
At the root level, create the following set-up file. Let us give the name
global-setup.js.
import {chromium } from "@playwright/test";
async function globalSetup() {
const browser = await chromium.launch();
const context = await browser.newContext();
const page = await context.newPage();
await
page.goto("https://automationexercise.com/login");
await page.locator('input[data-qa="login-
email"]').fill("ncrmeet1@yopmail.com");
await page.locator('input[data-qa="login-
email"]').press("Tab");
await
page.locator('input[placeholder="Password"]').fill("Tes
t@1234");
await page.locator('button[data-qa="login-
button"]').click();
await page.context().storageState({ path:
"./LoginAuth.json" });
await browser.close();
}
export default globalSetup;
In the following screenshot, you can see global set up file is created:
Figure 9.4: Global set up file
The code given above serves the purpose of setting up a browser
environment for automated testing using the Playwright framework.
Let us break down this code:
Launching browser and creating context:
The code uses Playwright's to launch a Chromium browser.
It creates a new browser context, which represents an isolated
browsing session.
Navigating to a login page:
It opens a new page in the created context and navigates to the
login page of https://automationexercise.com.
Performing login actions:
It locates the email input field using a specific attribute selector
and fills it with the email address: ncrmeet1@yopmail.com.
It simulates pressing the Tab key, possibly moving to the next
form field.
It locates the password input field by placeholder and fills it with
the password Test@1234.
It locates the login button using a specific attribute selector and
clicks on it.
Saving browser storage state:
It captures the storage state of the browser context, including
cookies and local/session storage.
The captured state is saved to a JSON file named LoginAuth.json
in the current directory.
Closing the browser:
Finally, it closes the browser, ensuring that the test setup is
cleaned up.
2. Update Playwright .config file: We have created the set-up file; now the
next step is to give the reference of the set-up file in the Playwright Config
file; otherwise, our test cases will fail.
Figure 9.5: Playwright .config file
3. Execute test cases: As we execute the test, it will generate the
LoginAuth.json file in the current directory, which includes browser
context, cookies, and local/session storage.
In the following screenshot for LoginAuth.json, we have all the details
about the browser context, cookies, and local/session storage:
Figure 9.6: .Json file
4. Set storage state in configuration file: To set the storage state in the
configuration file and instruct the test cases to use the state in
LoginAuth.json, you will need to update the configuration file. In the
following screenshot in line#33 you can see we have set the storage state:
Figure 9.7: Set storage states
5. Update test case: Since we have set the storage state so, from both the
above test cases, we can remove the following login part.
// await page.locator('input[data-qa="login-
email"]').fill("ncrmeet1@yopmail.com");
// await page.locator('input[data-qa="login-
email"]').press("Tab");
// await
page.locator('input[placeholder="Password"]').fill("Tes
t@1234");
// await page.locator('button[data-qa="login-
button"]').click();
Refer to the following screenshot to know which line needs to be removed
from both test cases:
Since we have set the storage state already so from the following
screenshot, we have removed the login script part:
Figure 9.8: Test case 1
The lines mentioned above were removed from the second test case as well.
Since we have already set the storage state, as shown in the following
screenshot, we have also removed the login script part:
Figure 9.9: Test case 2
6. Execute the test cases: We have set the storage state. Now, if we execute
the test cases, they will pass without any issues.
In the following screenshot we can see test case is passed successfully:
Figure 9.10: Test case execution results
In the next section, you will see how we can mock the API’s.
Mock the APIs
Mock APIs serve as simulated versions of real web APIs. When you are
developing an application that relies on external services or APIs, you may
encounter situations where those APIs are not fully developed yet or where you
want to isolate your development environment from external dependencies. This
is where mock APIs come into play. Playwright, a tool used for browser
automation and testing, offers features to mock and modify network traffic,
including HTTP and HTTPS requests. This means that any requests your web
application makes, such as XMLHttpRequests (XHRs) or fetch requests, can be
intercepted, tracked, modified, and even mocked using Playwright.
The following are the benefits of mocking the API’s:
Isolation: Sometimes, you want to isolate your development environment
from external dependencies. Mock APIs enable you to do this by providing
a controlled environment where you can mimic the behavior of the real API
without actually making external requests.
Independent development: When building software that relies on external
APIs, you might not always have access to those APIs, or they might still be
in development. Mock APIs allow you to continue developing your
application independently without being blocked by the availability or
readiness of the real APIs.
Testing: Mock APIs are invaluable for testing purposes. They enable you to
simulate various scenarios and responses, including success, error, and edge
cases. This helps ensure that your application behaves as expected under
different conditions and improves overall test coverage.
Reduced resource usage: Since mocks do not use real resources like
databases or external services, they reduce the load on these resources
during testing, which can be especially important in shared development
and testing environments.
Controlled environment: With Mock APIs, you have full control over the
responses and behavior of the API. This allows you to simulate different
states and scenarios, such as network errors or timeouts, to ensure that your
application handles them gracefully.
Mock API requests
Mock API requests are simulated HTTP requests made to mimic the behavior of
real API endpoints without actually interacting with the real backend server.
Developers create mock servers or endpoints that return predefined responses
based on the request parameters. Mocking is often used in scenarios where the
backend API is not fully developed, unavailable, or its usage would incur costs
(such as in testing environments).
For demo purposes, we are taking the example of the site
https://demo.playwright.dev/api-mocking.
In the following example, you can see we are intercepting the network requests to
a specific API endpoint ("/api/v1/fruits") and responding with predefined JSON
data. Then, it navigates to a web page and verifies that a specific fruits (Lucuma,
Raspberries, Kiwi, Peach, Fig) is visible on the page, confirming that the mocked
API response was used correctly.
The following is a code snippet for the example mentioned before:
const { test, expect } = require("@playwright/test");
test("Mock the API requests", async ({ page }) => {
// Mock the api call before navigating
await page.route("*/**/api/v1/fruits", async (route) => {
const json = [
{ name: "Lucuma", id: 11 },
{ name: "Raspberries", id: 12 },
{ name: "Kiwi", id: 13 },
{ name: "Peach", id: 14 },
{ name: "Fig", id: 15 },
];
await route.fulfill({ json });
});
// Go to the page
await page.goto("https://demo.playwright.dev/api-
mocking");
// Assert that the Raspberries fruit is visible
await expect(page.getByText("Raspberries")).toBeVisible();
});
The following figure is a screenshot of the executed code:
Figure 9.11: Mock API requests
Let us break down the previous code:
Importing modules: The code first imports the test and expects functions
from the @playwright/test package. These are used for writing and running
tests.
Test function: The code defines a test function using the test function
imported from Playwright. The test is named Mock the API requests. It is
an asynchronous function that takes an object with a page property.
API mocking: Within the test function, it sets up a mock response for API
requests made by the web page being tested. It uses page.route to intercept
requests to any URL containing "/api/v1/fruits". When such a request is
intercepted, it responds with a JSON array containing information about
different fruits: Lucuma, Raspberries, Kiwi, Peach, and Fig.
Navigation: After setting up the API mock, the code instructs the browser
to navigate to the URL https://demo.playwright.dev/api-mocking using
page.goto.
Assertion: Once the page is loaded, it verifies that the text "Raspberries" is
visible on the page using the expect function. If the text is visible, the test
passes; otherwise, it fails.
Modify API responses
Modifying API responses involves intercepting real API responses and altering
them before they reach the client application. Developers can modify response
data, headers, status codes, or other properties for testing or development
purposes. This approach is typically used when the real backend server is
available and functional, but certain responses need to be manipulated for specific
testing scenarios.
For demo purposes, we are taking the example of the site
https://demo.playwright.dev/api-mocking:
const { test, expect } = require("@playwright/test");
test("Modify API responses ({ page }) => {
// Get the response and add to it
await page.route("*/**/api/v1/fruits", async (route) => {
const response = await route.fetch();
const json = await response.json();
json.push(
{ name: "Dragon fruit", id: 11 },
{ name: "Gooseberries", id: 12 },
{ name: "Coconut", id: 13 }
);
// Fulfill using the original response, while patching
the response body with the given JSON object.
await route.fulfill({ response, json });
});
// Go to the page
await page.goto("https://demo.playwright.dev/api-
mocking");
// Assert that the new fruit is visible
await expect(page.getByText("Loquat", { exact: true
})).toBeVisible();
});
Following is the screenshot of the executed code:
Figure 9.12: Mock API responses
Let us break down the code:
const { test, expect } = require("@playwright/test");:
This line imports the test and expect functions from the @playwright/test
package, which is used for writing tests with Playwright Test.
test("Modify API responses ", async ({ page }) => { ... });: This
defines a test case with the description "Modify API responses". The test
case is an asynchronous function that takes a page object as an argument.
Inside this function, the test logic is implemented.
await page.route("*/**/api/v1/fruits", async (route) => { ... });:
This sets up a route interception for requests matching the pattern
"*/**/api/v1/fruits". When a request to this endpoint is made, the
provided callback function will be executed. Inside this callback, the
response from the intercepted request is modified by adding three new
objects to the JSON array.
await page.goto("https://demo.playwright.dev/api-mocking");: This
navigates the browser controlled by Playwright to the specified URL, where
the test will be performed.
await expect(page.getByText("Dragon fruit", { exact: true
})).toBeVisible();: This expectation verifies that after the modification of
the API response, the text "Dragon fruit" is visible on the page.
Conclusion
To recap this chapter, we got a comprehensive overview of API testing,
emphasizing its significance in ensuring the functionality, performance, and
security of APIs. We discussed the advantages of using Playwright for API testing.
Subsequently, we discussed practical examples of each HTTP method (GET,
POST, PUT, and DELETE). We addressed the importance of managing
authentication and session storage in API testing to ensure secure interactions
between clients and servers. Finally, the chapter concluded with insights into API
mocking techniques to simulate API behaviors and responses.
In the upcoming chapter, we will explore accessibility testing. Understanding the
importance of accessibility testing and adhering to the Web Content Accessibility
Guidelines (WCAG). Additionally, we have introduced axe-playwright as a
powerful tool for automating accessibility testing within the Playwright
framework.
Multiple choice questions
1. Why is API testing important?
a. To check the functionality, reliability, performance, and security of an
API
b. To ensure the user interface is visually appealing
c. To optimize website loading speed
d. To validate the database schema
2. What aspect of API testing involves validating user access and
maintaining session information?
a. Authorization
b. Authentication
c. Encryption
d. Validation
3. How can API mocking be implemented?
a. By using dummy data
b. By using tools like MockServer or WireMock
c. By using static data
d. By using dynamic data from production servers
4. Which Playwright method is used for authentication and session
storage during API testing?
a. page.context().storageState()
b. page.authenticate()
c. page.session()
d. page.auth()
5. How can you mock API responses in Playwright?
a. Use the page.route() method
b. Use the page.mock() method
c. Use the page.intercept()method
d. Use the page.response() method
Answers
1. a.
2. b.
3. b.
4. a.
5. a.
Join our book’s Discord space
Join the book's Discord Workspace for Latest updates, Offers, Tech happenings
around the world, New Release and Sessions with the Authors:
https://discord.bpbonline.com
CHAPTER 10
Accessibility Testing with
Playwright
Introduction
In the preceding chapter, we explore the understanding of API testing. We
learned the fundamentals of APIs and explored the significance of API
testing. We looked into practical examples of each HTTP method (GET,
POST, PUT, and DELETE) and addressed the importance of managing
authentication and session storage in API testing to ensure secure
interactions between clients and servers. Finally, the chapter concluded with
insights into API mocking techniques to simulate API behaviors and
responses.
In this chapter, we will explore accessibility testing. Understanding the
importance of accessibility testing and adhering to the Web Content
Accessibility Guidelines (WCAG). Additionally, we have introduced axe-
playwright as a powerful library for automating accessibility testing within
the Playwright framework.
Structure
This chapter covers the following topics:
About accessibility testing
Importance of accessibility testing
Web Content Accessibility Guidelines
Playwright for accessibility testing
Writing accessibility tests using Playwright
Objectives
By the end of this chapter, you will have a comprehensive understanding of
accessibility testing, its importance in web development, the guidelines
provided by WCAG, and practical techniques for writing and executing
accessibility tests using Playwright.
About accessibility testing
Accessibility testing is the practice of evaluating software applications and
websites to ensure that they meet established accessibility standards and can
be used effectively by people with disabilities. The goal is to identify and
remove potential barriers that could prevent users with visual, auditory,
motor, or cognitive impairments from accessing and interacting with the
product. Accessibility testing aims to ensure that the product adheres to
guidelines and standards such as the WCAG, Section 508 of the
Rehabilitation Act, or the Americans with Disabilities Act (ADA).
The accessibility tree is used by assistive technology to improve application
user interfaces (UIs) and make it easier for users with disabilities to interact
with them. An application or webpage's user interface components are
arranged according to a framework called the accessibility tree, which was
created with accessibility in mind. The Accessibility Tree is used by assistive
technology, including screen readers, magnifiers, alternate input devices, and
voice recognition software, to interpret and interact with the information.
Refer to Figure 10.1:
Figure 10.1: Accessibility tree
Source: Intuit blog
Accessibility testing is supported natively by Playwright, a modern
automation framework for end-to-end testing. This facilitates the
identification and resolution of accessibility problems in websites and online
applications. Playwright is compatible with axe-core, a well-liked
accessibility testing library that is frequently used to find accessibility
infractions according to the WCAG guidelines.
Importance of accessibility testing
Accessibility testing is crucial for several reasons, and its importance cannot
be overstated. Key arguments in favor of the necessity of accessibility
testing are as follows:
Inclusivity and equal access: People with disabilities, such as those
with visual, auditory, cognitive, or movement impairments, can use
digital products like websites and applications with the support of
accessibility testing. It promotes inclusivity and equal access to
information and services, enabling individuals with disabilities to
participate fully in the digital world.
Legal compliance: The ADA act in the United States, the
Accessibility for Ontarians with Disabilities Act (AODA) in
Canada, and the WCAG globally are just a few of the laws and
regulations that require accessibility standards for digital products in
many other countries and regions. Organizations can avoid potential
lawsuits and fines by adhering to these regulatory obligations with the
aid of accessibility testing.
Improved user experience: Accessible websites and applications
provide a better user experience for everyone, not just users with
disabilities. Clear layouts, appropriate color contrast, and keyboard
accessibility are examples of features that can improve usability for all
users, resulting in higher levels of engagement and pleasure.
Expanded market reach: Organisations can reach a larger audience
—which includes a sizable section of the population—by guaranteeing
accessibility, including those with impairments. Increased client
loyalty, positive brand perception, and possible income prospects can
result from this.
Search engine optimization: SEO: It can be enhanced by
implementing accessibility features. Accessible websites are given
preference by search engines like Google because they improve user
experience for all users, including those with disabilities.
Social responsibility: Accessibility testing demonstrates an
organization's commitment to social responsibility and ethical
practices. Companies that prioritize accessibility can also build a
positive public perception, fit with corporate social responsibility
aims, and help create a more inclusive and equal digital ecosystem.
Playwright, being a powerful automation tool for web applications, can also
be utilized for accessibility testing. Here are a few examples of accessibility
issues that Playwright can help identify:
Poor color contrast: Playwright can verify the color contrast between
text and its background, helping to identify instances where the text
might be difficult to read for users with vision impairments. Ensuring
sufficient color contrast is essential for making content accessible to
everyone.
Missing labels for UI controls: Playwright can check for UI controls
such as buttons, input fields, and checkboxes that lack proper labels or
have insufficient labeling. Screen readers rely on these labels to
provide context and functionality to users with visual impairments, so
ensuring proper labeling is crucial for accessibility.
Duplicate IDs for interactive elements: Playwright can detect
instances where interactive elements within the application have
duplicate IDs. Duplicate IDs can confuse assistive technologies such
as screen readers, making it difficult for users with disabilities to
navigate and interact with the content effectively.
Web Content Accessibility Guidelines
The World Wide Web Consortium (W3C) created the Web Content
Accessibility Guidelines (WCAG) as a collection of principles to make sure
that web content is accessible to individuals with disabilities. Making the
web easier to use and comprehend for everyone—including people with
physical, cognitive, verbal, auditory, visual, and neurological disabilities—is
the main objective of WCAG.
WCAG is organized into four principles, each with their own set of
guidelines and success criteria:
Perceivable: Users need to be able to perceive the information and UI
elements that are presented to them. This entails giving text
alternatives for non-text content, guaranteeing that content can be
presented in various formats (e.g., with larger font sizes or more
straightforward layouts), and facilitating the user's ability to see and
hear content.
Operable: The navigation and user interface elements need to be
operable. This implies that users need to be able to employ a variety of
input methods, including touch, mouse, and keyboard, to browse and
engage with the interface. In addition, it involves avoiding content that
induces seizures, giving users adequate time to read and utilise
content, and creating content in a way that reduces the possibility of
errors.
Understandable: Information and the operation of user interface must
be understandable. This involves making content readable and
understandable, ensuring users can easily navigate and find content,
and providing clear and consistent navigation mechanisms.
Robust: Content needs to be strong enough to be reliably understood
by a range of user agents, including assistive technologies. Assuring
compatibility with present and future technologies, writing markup
and code in accordance with web standards, and offering fallbacks for
material that some user agents might not support are all part of this.
Each of these principles contains specific guidelines and success criteria,
categorized into three levels of conformance: A (lowest), AA, and AAA
(highest). Conformance to WCAG is typically expressed as a level of
compliance (e.g., This website conforms to WCAG 2.0 Level AA).
Here is a brief overview of each level:
Level A (minimum level of conformance):
This level of compliance guarantees that certain users with
disabilities can access basic accessibility issues and addresses the
most fundamental accessibility concerns.
The goal of Level A conformance is to support accessibility on a
basic level. Examples of this include making sure that non-text
content has text alternatives available, making sure content can be
accessed and operated solely through a keyboard, and refraining
from relying solely on colour or sensory cues to convey
information.
Level AA (standard level of conformance):
Conformance at this level addresses a wider range of accessibility
issues and aims to make content more accessible to a broader
audience, including many users with disabilities.
All of the Level A requirements are met by Level AA conformance,
along with a few more. For example, text must have a sufficient
contrast ratio to be readable, captions must be provided for audio
content that has already been recorded and ensuring that content is
robust enough to be interpreted by a wide variety of user agents,
including assistive technologies.
Level AAA (enhanced level of conformance):
This level of conformance offers the most accessible experience for
persons with disabilities and represents the highest level of
accessibility. It tackles a wide range of accessibility issues.
Level AAA conformance includes all Level A and Level AA
criteria, Level AAA conformance adds even more strict guidelines,
like offering sign language interpretation for any audio content that
has been pre-recorded, making sure that the content is presented in
a predictable and understandable manner, and enabling users to
alter the presentation of the content to suit their own needs.
Here is a brief overview of levels of compliance with the Web Content
Accessibility Guidelines (WCAG):
WCAG 2.0 Level A (wcag2a):
These guidelines cover the most basic accessibility requirements.
They address issues that can be automatically detected by
automated tools.
Examples include providing alternative text for images and
ensuring proper color contrast.
WCAG 2.0 Level AA (wcag2aa):
These guidelines build upon Level A and focus on more advanced
accessibility requirements.
They address issues that require both automated testing and manual
assessment.
Examples include keyboard navigation, captions for videos, and
form field labels.
WCAG 2.1 Level A (wcag21a):
WCAG 2.1 extends the original guidelines to cover additional
accessibility aspects.
Level A requirements remain similar to WCAG 2.0 Level A.
Examples include providing text alternatives for non-text content
and ensuring readable fonts.
WCAG 2.1 Level AA (wcag21aa):
These guidelines combine WCAG 2.0 Level AA and the new
WCAG 2.1 requirements.
They address a broader range of accessibility issues.
Examples include adaptable content, focus indicators, and
accessible forms.
Tools for accessibility testing
To make sure that websites and applications are usable by people with
impairments, accessibility testing is an essential part of web development.
Developers and testers can assess accessibility compliance and spot possible
problems; a number of tools are available.
Popular tools for checking accessibility include the following:
Browser extensions
Following are some open-source browser extensions for accessibility testing:
Lighthouse: Lighthouse is an open-source tool developed by Google
that can be accessed directly within the Chrome DevTools or as a
Chrome extension. It provides automated audits for performance,
accessibility, progressive web apps, and more. Lighthouse generates
comprehensive reports with actionable recommendations for
improving accessibility.
Browser extension can be installed with the link
chromewebstore.google.com/detail/lighthouse/blipmdconlkpinefeh
nmjammfjpmpbjk.
axe DevTools: axe DevTools is an accessibility testing extension
available for Chrome and Firefox browsers. It integrates directly into
the browser's developer tools, allowing developers to analyze web
pages for accessibility issues. axe DevTools offers both automated and
manual testing features, along with detailed explanations and
remediation advice for identified issues.
Browser extension can be installed with the link
www.deque.com/axe/.
Web accessibility evaluation tool: WAVE is a web-based
accessibility testing tool developed by WebAIM. It allows users to
enter a URL or upload HTML documents for evaluation. WAVE
provides visual feedback by annotating web pages with icons and
indicators to highlight.
Browser extension can be installed with the link wave.webaim.org/.
Playwright for accessibility testing
We need to install the @axe-core/playwright library in order to test
accessibility using Playwright. @axe-core/playwright is a library that
integrates the axe-core accessibility testing engine with the Playwright
browser automation tool. It allows you to run accessibility tests on web
applications using Playwright scripts.
Axe-core is a popular open-source accessibility testing engine that can
analyze web content and detect potential accessibility issues based on the
WCAG. By combining Axe with Playwright, developers can automate the
process of running accessibility tests on web pages. This integration allows
developers to write scripts that open web pages in a browser using
Playwright, execute accessibility tests using Axe, and gather the results
programmatically.
Installation: To install @axe-core/playwright library you need to run the
following command:
npm i @axe-core/playwright
Once @axe-core/playwright is installed, you can see the dependency in
package.json. Refer to Figure 10.2:
Figure 10.2: package.json
Writing accessibility tests using Playwright
Using Playwright, you can write accessibility tests to make sure your
website is usable by people with disabilities, like those who use keyboard
navigation or screen readers. Playwright is an effective tool for browser
automation that lets you run different tests, including accessibility tests, and
mimic user activities. Playwright testing operates in the same way as
accessibility tests. Either make unique test cases for them or incorporate
accessibility assertions and scans into the test cases you already have.
Some basic accessibility testing scenarios are shown in the following
examples.
Scanning an entire page
We will use Axe-Playwright to test a complete page as our first exercise.
Here, we are injecting the axe-core into the page being tested and testing the
accessibility of the entire page using the code provided.
For example, the purpose of using site www.thetesttribe.com/my-account.
Let us first scan the entire page for the violation of the site
www.thetesttribe.com/my-account/. Refer to Figure 10.3:
Figure 10.3: Test Tribe Login page
const { test, expect } = require("@playwright/test");
import AxeBuilder from "@axe-core/playwright";
test("Scan the Complete Page", async ({page}) => {
await page.goto("https://www.thetesttribe.com/my-
account");
const accessibilityScanResults = await new
AxeBuilder({ page }).analyze();
expect(accessibilityScanResults.violations).toEqual([]);
});
Code walkthrough
This code is a simple test case written using the Playwright test framework
along with Axe for accessibility testing. Let us break it down:
const { test, expect } = require("@playwright/test");
import AxeBuilder from "@axe-core/playwright";
This section imports necessary functions and classes from the
Playwright and Axe libraries.
test and expect are functions imported from @playwright/test for
defining tests and making assertions, respectively.
AxeBuilder is imported from @axe-core/playwright for conducting
accessibility scans.
test("Scan the Complete Page", async ({page}) => {
The test function defines a test case named "Scan the Complete
Page".
It is an asynchronous function that takes an object with a page
parameter. This page represents the current page being tested.
await page.goto("https://www.thetesttribe.com/my-
account/edit-account/");
This line uses Playwright's page.goto() function to navigate the browser to
the specified URL, which is https://www.thetesttribe.com/my-
account/edit-account/ in this case.
const accessibilityScanResults = await new AxeBuilder({
page }).analyze();
Here, a new AxeBuilder instance is created, passing the page object as
a parameter.
The analyze() method is then called on this instance to perform an
accessibility scan of the current page.
The scan results are stored in the accessibilityScanResults variable.
expect(accessibilityScanResults.violations).toEqual
([]);
This line uses the expect function to assert that there are no
accessibility violations on the page.
It checks if the violations array in the accessibilityScanResults
object is empty.
If there are violations, the test will fail; otherwise, it will pass.
Output
The output of the executed test case shows that the 732 violations have been
detected. Refer to Figure 10.4 for the same:
Figure 10.4: Scan Complete Page
Scanning a specific part of a page
In the next scenario we are scanning the part of the page. In below
screenshot you can see we are scanning login tab section only. Refer Figure
10.5:
Figure 10.5: Test Tribe Login tab
const { test, expect } = require("@playwright/test");
import AxeBuilder from "@axe-core/playwright";
test("Scan the Partial Page Include Login Credentials
Section during Scan", async ({page}) => {
await page.goto("https://www.thetesttribe.com/my-
account/edit-account/");
const accessibilityScanResults = await new
AxeBuilder({ page }).include('.woocommerce').analyze();
expect(accessibilityScanResults.violations).toEqual([]);
});
Output
Execute the above code. Following is the output of the executed test case,
where you can see that 91 violations are detected. It means when we
decrease the scope of the scan, violations are decreased. Refer to Figure
10.6:
Figure 10.6: Scan Partial Page
Scanning a specific element of a page
In the next scenario, we are scanning the username field in the login screen.
test("Scan only the Username text field of the Login
Credentials Screen", async ({page}) => {
await page.goto("https://www.thetesttribe.com/my-
account/edit-account/");
const accessibilityScanResults = await new
AxeBuilder({ page }).include('#username').analyze();
expect(accessibilityScanResults.violations).toEqual([]);
});
Output
Execute the above code. Following is the output of the executed test case,
where you can see there are no violations. Refer to Figure 10.7:
Figure 10.7: Scan specific element
Scanning with WCAG violations
The AxeBuilder.withTags() method can be used to narrow down an
accessibility scan to rules that match particular WCAG success criteria.
Using this method, you can indicate which rules should be scanned based on
the tags that go with them. Use the tags wcag2a, wcag2aa, wcag21a, and
wcag21aa, for example, to mimic the behavior of accessibility insights for the
web's automated checks, which only contain rules for WCAG A and AA
success criteria.
test("Scan the Complete Page related to WCAG A or AA
violations", async ({page}) => {
await page.goto("https://www.thetesttribe.com/my-
account/edit-account/");
const accessibilityScanResults = await new
AxeBuilder({ page }).withTags(["wcag2a", "wcag2aa",
"wcag21a", "wcag21aa"]).analyze();
expect(accessibilityScanResults.violations).toEqual([]);
});
Output
Execute the preceding code. Following is the output of the executed test
case, where you can see that 412 violations are detected. Refer to Figure
10.8:
Figure 10.8: Scan with WCAG violations
Excluding elements from a scan
You can use the AxeBuilder.exclude() method to remove particular element
from an accessibility scan. This enables you to exclude elements from the
scan that have known problems until they can be fixed.
From the site for we have excluded login tab during our scan.
test("Exclude Login Tab during the Scan", async ({page})
=> {
await page.goto("https://www.thetesttribe.com/my-
account");
const accessibilityScanResults = await new
AxeBuilder({ page }).exclude('[data-
id="tab1"]').analyze();
expect(accessibilityScanResults.violations).toEqual([]);
});
Output
Execute the above code. Following is the output of the executed test case,
where you can see that 570 violations are detected. Refer to Figure 10.9:
Figure 10.9: Exclude in Scan
Disabling particular scan rules
If your application has numerous different pre-existing violations of a
specific rule. You can use the method AxeBuilder.disableRules() to disable
those individual rules until you are able to fix the issues.
Here, in this example, we are disabling the rule region:
test("Scan the Complete Page related to WCAG A or AA
violations with disabling the rule 'region'", async
({page}) => {
await page.goto("https://www.thetesttribe.com/my-
account/edit-account/");
const accessibilityScanResults = await new
AxeBuilder({ page }).withTags(["wcag2a", "wcag2aa",
"wcag21a",
"wcag21aa"]).disableRules(["region"]).analyze();
expect(accessibilityScanResults.violations).toEqual([]);
});
To find WCAG 2.0 Level A & AA rules follow the link:
github.com/dequelabs/axe-core/blob/develop/doc/rule-descriptions.md.
Output
Execute the preceeding code. Following is the output of the executed test
case where you can see violations decrees to 412 by disabling the rule
region. Refer to Figure 10.10:
Figure 10.10: Scan with disabling rule
Conclusion
To recap, in this chapter, we learned about accessibility testing, understood
its importance, and adhered to the Web Content Accessibility Guidelines
(WCAG). Additionally, we were introduced to axe-playwright, a powerful
tool for automating accessibility testing within the Playwright framework.
In the upcoming chapter, we will explore visual testing and its significance
in ensuring the quality and integrity of web applications. We begin by
elucidating the concept of visual testing, highlighting its pivotal role in
detecting UI inconsistencies, layout glitches, and design flaws that
traditional testing methods may overlook.
Multiple choice questions
1. What is one of the primary reasons accessibility testing is
considered important?
a. To improve the website loading speed
b. To enhance search engine optimization
c. To ensure websites are usable by individuals with disabilities
d. To increase website aesthetics
2. Which set of guidelines are commonly referenced in accessibility
testing?
a. HTML5 standards
b. CSS best practices
c. JavaScript coding conventions
d. Web Content Accessibility Guidelines
3. What is the purpose of writing accessibility tests using
Playwright?
a. To ensure consistent website layout across different devices
b. To check website responsiveness
c. To validate the accessibility features and functionalities of web
applications
d. To analyze website traffic metrics
4. Which organization is responsible for creating and maintaining
WCAG?
a. IEEE
b. W3C
c. ISO
d. IEC
5. Which of the following disabilities is accessibility testing
primarily concerned with addressing?
a. Color blindness
b. Claustrophobia
c. Arachnophobia
d. Hearing impairment
Answers
1. c.
2. d.
3. c.
4. b.
5. d.
Join our book’s Discord space
Join the book's Discord Workspace for Latest updates, Offers, Tech
happenings around the world, New Release and Sessions with the Authors:
https://discord.bpbonline.com
CHAPTER 11
Visual Testing with Playwright
Introduction
In the preceding chapter, we will explore about accessibility testing.
Understanding the importance of accessibility testing, adhering to the Web
Content Accessibility Guidelines (WCAG). Additionally, we have
introduced Axe-Playwright as a powerful library for automating accessibility
testing within the Playwright framework.
In this chapter, we will understand visual testing. We will guide you through
the process of conducting visual tests and the tools available for visual
testing. Discover the benefits that visual testing offers, from early bug
detection to ensuring consistent user experiences. To tie it all together, we
will demonstrate how Playwright can be utilized to execute visual tests
seamlessly.
Structure
This chapter covers the following topics:
About visual testing
Ways to perform visual testing
Tools for perform visual testing
Benefits of visual testing
Create visual tests using Playwright
Objectives
By the end of this chapter, you will have a comprehensive understanding of
accessibility testing, its importance in web development, the guidelines
provided by WCAG, and practical techniques for writing and executing
accessibility tests using Playwright.
About visual testing
Visual testing is a software testing technique that focuses on verifying the
appearance and behavior of a software application's user interface (UI) or
graphical user interface (GUI). It involves evaluating whether the UI
elements, such as buttons, menus, icons, images, fonts, colors, and layout,
are rendered correctly and interact with the user as intended.
Visual testing is particularly important in modern web development and
cross-platform application development, where the same application needs
to run seamlessly across various environments and screen resolutions. It
helps catch visual defects, layout issues, and rendering inconsistencies that
might not be detected by traditional functional testing methods.
Here are some key aspects of visual testing:
Baseline creation: A baseline or expected output is captured, typically
in the form of a screenshot or a set of screenshots, representing the
desired visual appearance of the application under test.
Comparison: During the testing process, the application's visual
output is captured and compared against the baseline images. Any
deviations or visual differences between the actual output and the
baseline are identified and reported as visual defects.
Visual validation: Visual testing tools provide various techniques to
validate the visual aspects of the application, such as comparing pixel-
by-pixel, analyzing layout and positioning, checking color contrast,
and verifying font sizes and styles.
Cross-browser and cross-device testing: Visual testing is crucial for
ensuring consistent UI rendering across different web browsers (e.g.,
Chrome, Firefox, Safari) and devices (desktops, tablets, smartphones)
with varying screen resolutions and aspect ratios.
Regression testing: Visual testing is often incorporated into
regression testing cycles to detect unintended visual changes or
regressions introduced by code modifications or updates.
Automation: While manual visual testing is possible, it can be time-
consuming and error prone. Visual testing tools and frameworks, such
as Applitools, Percy with visual validation capabilities, enable the
automation of visual testing, improving efficiency and consistency.
Reporting and debugging: Visual testing tools provide detailed
reports and visual comparisons, highlighting the differences between
the actual and expected outputs. This information helps developers
quickly identify and debug visual issues.
Ways to perform visual testing
There are mainly two main ways to perform the visual testing:
Manual visual testing: This is a straightforward approach where you,
the tester, manually inspect the application's UI across various devices
and browsers. Here is how it works:
a. Preparation:
a. Get familiar with the application's design mockups or UI
guidelines. This gives you a reference point for what the UI
should look like.
b. Decide on the platforms and browsers you will test on. Consider
popular devices and browsers your target audience might use.
b. Inspection:
a. Open the application on each chosen platform and browser
combination.
b. Visually inspect the UI elements like layout, alignment, fonts,
colors, and spacing.
c. Compare what you see with the design reference you have.
d. Pay attention to responsiveness, i.e., how the UI adjusts to
different screen sizes.
c. Documentation of issues:
a. Take screenshots of any inconsistencies or visual bugs you find.
b. Document the issue details—where it occurred, the platform and
browser, and how it deviates from the expected design.
Automated visual testing with tools: While manual testing is great,
automation can save you time in the long run. Automated visual
testing involves using software tools to verify the visual aspects of a
user interface or application automatically. These tools capture
screenshots or renderings of web pages, applications, or components
and then compare them against baseline images to detect any
discrepancies or visual regressions.
Here is how you could break down each step:
1. Screenshot capture: This involves taking screenshots of the website
or application at specific intervals or when changes are expected to
occur.
2. Baseline establishment: Store the initial screenshot (baseline)
securely. This serves as a reference point for future comparisons.
3. Comparison tools: Utilize tools or scripts to compare screenshots.
These tools should highlight any differences between the baseline
screenshot and the current screenshot.
4. Analysis and reporting: Review the differences identified by the
comparison tools. Determine if the changes are expected or
unexpected. Generate reports detailing the changes for further
analysis or action.
In the next section, you will see the available tools for visual testing.
Tools to perform visual testing
There are several tools available to perform visual testing, each with its own
unique features and capabilities. We will explore some of the most popular
and effective tools for visual testing as follows:
Applitools Eyes: It is a cloud-based visual testing tool that leverages
artificial intelligence (AI) and computer vision technology to
automate the visual testing process. It compares screenshots of your
application's UI against baseline images and highlights any visual
differences or defects. Applitools Eyes supports various programming
languages and frameworks, making it a versatile choice for visual
testing across different platforms.
Percy: Percy is a visual testing and review tool that integrates
seamlessly with popular CI/CD platforms. It captures screenshots of
your application's UI during the build process and provides a visual
diff tool for comparing changes between revisions. Percy also offers
features like visual approvals, baseline management, and visual
regression testing.
Katalon: It is an integrated test automation tool for web and mobile
apps. It provides a range of features for automated testing, including
UI testing, API testing, and mobile testing. One of its notable features
is visual testing, which allows you to verify the visual appearance of
your web applications across different browsers and devices.
Visual testing in Katalon Studio can be particularly useful for
ensuring consistent UI rendering across different browsers, devices,
and screen resolutions. It helps to catch visual defects early in the
development cycle, improving the overall quality of your web
applications. If any differences are detected, Katalon Studio
generates reports indicating the visual variances between the
baseline and current screenshots. This helps testers identify
potential UI issues that need to be addressed.
Sauce Lab: Sauce Labs is a cloud-based platform that provides
automated testing for web and mobile applications. One of the features
they offer is visual testing, which is a crucial aspect of ensuring the
visual integrity and consistency of web and mobile applications across
different browsers, devices, and screen sizes.
When you initially test an application, Sauce Labs captures
screenshots of the different pages or views of the application as the
baseline reference. As you make changes to your application or
deploy new versions, you can use Sauce Labs to automatically
compare the current state of the application against the baseline
reference. This comparison is done visually to detect any
differences in layout, styling, or content.
Playwright: It offers built-in functionalities for automating visual
testing through snapshot testing. Playwright captures screenshots of
web pages or specific elements during test execution. On the first run,
these captured screenshots become the baseline images for
comparison. In the following test runs, Playwright compares the newly
captured screenshots against the baseline versions. There are no
significant visual differences, and the test passes. Any discrepancies
between the screenshots indicate visual regressions, causing the test to
fail.
Cypress: Cypress itself does not have built-in functionalities for
visual testing, but you can achieve it using third-party tools and
plugins. Cypress utilizes third-party plugins to achieve visual testing.
These plugins capture screenshots of your application and compare
them to baseline images previously approved as the desired visual
state.
Benefits of visual testing
Visual testing offers several benefits, including the following:
Improved user experience (UX): In today's world, users expect
applications and websites to be not only functional but also visually
appealing and easy to use. Visual testing helps to ensure that the UI is
consistent and free of errors, which can lead to a more positive user
experience.
Increased efficiency: Visual testing can automate the process of
checking for visual inconsistencies, which can save testers a
significant amount of time. This allows them to focus on more
complex testing tasks.
Early detection of bugs: Visual testing can help to catch bugs early in
the development process before they become more expensive and
time-consuming to fix.
Improved cross-browser compatibility: Visual testing can help to
ensure that a website or application looks and functions the same way
on all browsers and devices.
Reduced risk of regressions: Visual testing can help to reduce the
risk of regressions, which are defects that are introduced into a product
after it has been fixed.
Benefits of automating visual testing
Automating visual testing using Playwright offers several benefits:
Cross-browser compatibility testing: Playwright supports multiple
browsers, including Chromium, Firefox, and WebKit, enabling you to
test your application's visual appearance across different browsers and
ensure consistent rendering.
Visual regression testing: Playwright allows you to capture and
compare screenshots of your application's user interface (UI)
components or entire pages across different test runs. This helps detect
visual regressions introduced by code changes, ensuring that the UI
remains consistent and free from unintended visual defects.
Automated visual validation: With Playwright, you can write
automated tests to validate the visual aspects of your application, such
as layout, positioning, colors, fonts, and other visual elements. This
reduces the need for manual visual inspection and improves the
overall testing process efficiency.
Parallel execution: Playwright supports parallel test execution,
allowing you to run multiple visual tests concurrently, significantly
reducing the overall testing time.
Regression detection: Automated visual testing helps in detecting
unintended changes or regressions in the UI caused by code
modifications, allowing developers to catch issues early in the
development cycle.
Create visual tests using Playwright
Creating visual tests using Playwright involves leveraging its capabilities to
automate the validation of visual elements within web applications.
Playwright, a powerful open-source automation framework, enables
developers to script interactions with web pages and verify visual
consistency across different browser environments.
In the following subsection, you will see various use cases of how to
perform visual tests using Playwright.
Create visual tests for single page
The toHaveScreenshot() function in Playwright's is used to perform visual
testing by comparing the current state of a web page or element with a
reference screenshot. This function allows you to assert whether the visual
appearance of the page or element matches the reference image.
toHaveScreenshot() function will wait until two consecutive page
screenshots yield the same result, and then compare the last screenshot with
the expectation.
Let automate the page of the site playwright.dev capture the screenshot, put
the assertion and then perform visual testing against a baseline image.
const { test, expect } = require('@playwright/test');
test('Basic Visual Testing', async ({ page }) => {
await page.goto('https://playwright.dev/');
await expect(page).toHaveScreenshot("visual-
partial.png")
});
Code walkthrough
const { test, expect } = require('@playwright/test')
This line imports the test and expect functions from the Playwright testing
library.
test('Basic Visual Testing', async ({ page }) => {
This line starts a new test case named "Basic Visual Testing". The async
keyword is used because the test involves asynchronous operations (like
opening a web page). The ({ page }) part is a parameter destructuring
syntax, which provides the page object representing the web page that will
be used in the test.
await expect(page).toHaveScreenshot("visual-
partial.png");
This line is where the visual testing happens. The expect(page) part creates
an assertion for the page object. The toHaveScreenshot("visual-
partial.png") method takes a screenshot of the current page and compares
it with a previously saved screenshot (or saves a new one if it does not exist).
If the screenshots match, the test passes; otherwise, it fails.
Test execution
When we execute the test case on the first run and it fails, we get the error
“Error: A snapshot does not exist” and the captured screenshots become
the baseline images for comparison. In following test runs, Playwright
compares the newly captured screenshots against the baseline versions.
When we run the test case again, the test case passes, and the screenshot is
captured under the folder tests; we can see the captured screenshot as
follows:
Figure 11.1: Visual of passed test with single page
Create visual tests for full page
We can take the visual of the full page below. The script will navigate to the
site https://playwright.dev/, take a screenshot of the entire page, and
perform visual testing against a baseline image. To take the visual of the full
page, we have to pass some parameters that you will see in the following
example:
const { test, expect } = require('@playwright/test');
test('Take the visual of full page', async ({ page }) =>
{
await page.goto('https://playwright.dev/');
await expect(page).toHaveScreenshot("visual-full.png",
{fullPage:true})
});
Code walkthrough
const { test, expect } = require('@playwright/test')
This line imports the test and expect functions from the @playwright/test
module. These functions are provided by Playwright for writing tests.
await expect(page).toHaveScreenshot("visual-full.png",
{fullPage:true}):
The following is a detailed explanation of the previous line:
This line uses the expect function imported from Playwright to define
an assertion. It expects the page to have a screenshot captured.
The toHaveScreenshot matcher checks if the page has a screenshot
with the specified name ("visual-full.png") and options.
{fullPage: true} is an option passed to the toHaveScreenshot
matcher, indicating that the screenshot should capture the entire page
(full-page screenshot).
Test execution
When we execute the test case on the first run, the test case fails, and we get
the error Error: A snapshot does not exist, and the captured full
screenshots of the page become the baseline images for comparison.
When we run the test case again, the test case is passed, and the screenshot is
captured. We can see the full screenshot is captured in the following figure.
Screenshot is also captured under the folder tests:
Figure 11.2: Visual of passed test with full page
Create visual tests with masking the element or object
We can take the visual of the page by masking the button, e.g., in this case,
we want to mask the ‘Get Started’ button. The following script will
navigate to the site https://playwright.dev/, with masking the button ‘Get
Started’, and then perform visual testing against a baseline image:
test('Take visual of full page with masking the button',
async ({ page }) => {await
page.goto('https://playwright.dev/');
await expect(page).toHaveScreenshot("visual-mask.png",
{fullPage:true,mask:[page.getByText('Get started')],})
});
Code walkthrough
const { test, expect } = require('@playwright/test')
This line imports the test and expect functions from the @playwright/test
module. These functions are provided by Playwright for writing tests.
await expect(page).toHaveScreenshot("visual-mask.png",
{fullPage:true,mask:[page.getByText('Get started')],}):
See the detail explanation of above lines:
expect(page): This sets up an expectation on the page object,
indicating that we expect something specific to happen on this page.
.toHaveScreenshot("visual-mask.png", { ... }): This expects that a
screenshot of the page will be taken and saved as "visual-mask.png".
The second argument is an object with options for the screenshot.
fullPage: true: This option indicates that the screenshot should
capture the entire page, not just the visible portion.
mask: [page.getByText('Get started')]: This option specifies an
element on the page to be masked before taking the screenshot. In this
case, it is masking the button with the text 'Get started'. Masking
usually means hiding or obscuring the specified element in the
screenshot.
Test execution
When we execute the test case, on the first run, the test case fails, and we get
the error “Error: A snapshot does not exist”. The screenshot is captured
with full screenshots with masking the button ‘Get Started’ and becomes
the baseline images for comparison.
When we run the test case again, the test case passes, and the screenshot is
captured under the folder tests. You can see the full screenshot is captured,
and the button ‘Get Started’ is masked in the following screenshot:
Figure 11.3: Visual of passed test with mask
Create visual tests with maxDiffPixels
We can take screenshots within a certain threshold of differing pixels. We
have to pass the parameter maxDiffPixels which indicating that the
maximum allowed difference between the actual screenshot and the expected
screenshot.
test('Take visual with maxDiffPixels', async ({ page })
=> {
await page.goto('https://playwright.dev');
await expect(page).toHaveScreenshot({ maxDiffPixels:
100 });
});
Code walkthrough
Let us see the following line in detail:
await expect(page).toHaveScreenshot({ maxDiffPixels: 100
});
The following is a detailed explanation of the previous line:
await is used to wait for the promise to resolve.
expect(page) sets up an expectation on the page object.
.toHaveScreenshot() is likely a custom matcher function provided by
a testing library or a plugin. This function probably takes a screenshot
of the current state of the page.
{ maxDiffPixels: 100 } is an option passed to the toHaveScreenshot
function, indicating that the maximum allowed difference between the
actual screenshot and the expected screenshot is 100 pixels.
Create visual tests with difference in entered text
In this use case, we will input data into the search text box, take a baseline
screenshot, and then rerun the test case with different text to compare and
observe any discrepancies.
test('Take visual of particular area with difference in
entered text', async ({ page }) => {
await page.goto('https://ecommerce-
playground.lambdatest.io/');
await page.getByRole("textbox", {name : "Search For
Products"}).fill('Hi Lambdatest')
await expect(page).toHaveScreenshot("visual-enter-
text.png");
});
Execute the above test case first; it will fail as we do not have any baseline
image. In the second run test case will pass, as you can see in the following
figure:
Figure 11.4: Visual of passed test with entering text
Now, let us change the text that we entered, in place of “Hi Lambdatest” let
us enter “Hello Lambdatest” and run the test case again.
In the following figure, you can see that the test case fails the first time with
“Error: Screenshot comparison failed: 223 pixels (ratio 0.01 of all
image pixels) are different”.
Figure 11.5: Visual of failed test with new text enters
Let us run the test case once more; you will notice the text differences are
highlighted in red, enabling us to identify any visual bugs.
Figure 11.6: Visual of failed test with difference in entered text
Conclusion
To recap, in this chapter, we understood visual testing. We discussed the
about visual testing, explored various tools at your disposal for this task, and
uncovered the numerous benefits it offers. Lastly, we have seen how we can
automate visual testing using Playwright.
In the upcoming chapter, we will learn about CI/CD and its implementation
using GitHub Actions. We will cover setting up a basic GitHub Actions
workflow, integrating Playwright for end-to-end testing, running Playwright
tests in the CI/CD pipeline, cross-browser testing, parallel test execution,
and test sharding across multiple machines. Additionally, we will discuss the
concept of browser context in Playwright.
Multiple choice questions
1. Which of the following best describes visual testing?
a. Testing focused solely on the GUI.
b. Testing the performance of visual elements in a software
application.
c. Evaluating the visual aesthetics of a website or application.
d. Ensuring the accessibility of images and graphics in software.
2. How can visual testing be performed effectively?
a. By analyzing code structure and algorithms.
b. Through automated comparison of screenshots.
c. By conducting user surveys and interviews.
d. Manually inspecting each line of code for visual defects.
3. What is a key benefit of visual testing?
a. Improved code performance
b. Enhanced security measures
c. Early detection of UI regressions
d. Streamlined database management
4. How can you create visual tests using Playwright?
a. By writing code to capture screenshots and compare them.
b. By manually inspecting each UI element.
c. By using a graphical user interface.
d. Playwright does not support visual testing.
5. Which of the following is not a benefit of visual testing?
a. Early detection of UI changes
b. Increased test coverage
c. Improved user experience
d. Reduced manual effort in regression testing
Answers
1. a.
2. b.
3. c.
4. a.
5. b.
CHAPTER 12
Integrate Playwright Tests with CI/CD
and Run Tests in Parallel
Introduction
In the preceding chapter, we explored the world of visual testing. We will guide
you through the process of conducting visual tests and the tools available for
visual testing. Discover the myriad benefits that visual testing offers, from early
bug detection to ensuring consistent user experiences. To tie it all together, we will
demonstrate how Playwright can be utilized to execute visual tests seamlessly.
In this chapter, we explored the fundamentals of CI/CD and introduced GitHub
actions as a tool for implementing continuous integration and deployment. We
covered the steps to set up a basic GitHub actions workflow and demonstrated
how to run Playwright tests within this framework. Additionally, we discussed
techniques for cross-browser testing with Playwright, as well as executing tests in
both serial and parallel modes. Finally, we examined the process of sharding tests
across multiple machines to optimize test execution.
Structure
The chapter covers the following topics:
About CI/CD
Introduction to CI/CD tool GitHub Actions
Setting up basic GitHub Actions workflow
Running Playwright tests in GitHub Actions
Cross browser testing with Playwright
Test execution in serial and parallel mode
Sharding tests between multiple machines
Objectives
By the end of this chapter, you will get a concise introduction to CI/CD, focusing
on GitHub Actions as the primary tool for automation. Readers will learn to set up
foundational workflows in GitHub Actions and integrate Playwright for automated
testing. The chapter covers essential topics such as cross-browser testing,
optimizing test execution in serial and parallel modes, and advanced techniques
like sharding tests across multiple machines.
About CI/CD
CI/CD, which stands for continuous integration and continuous
delivery/deployment, is a set of practices in software development processes
designed to automate and optimize the process of deploying code from
developers' workstations into production.
Here is a breakdown of the two parts of CI/CD:
Continuous integration (CI): This entails regularly combining code
modifications from several developers into a single repository. Automated
builds and tests are then run on the code to detect any errors or integration
issues early on. This allows developers to identify and fix problems quickly
before they become bigger issues later in the development process.
Key practices in CI:
Frequent commits: Developers commit code changes to the repository
regularly, ensuring that the codebase is always up to date.
Automated builds: Each commit triggers an automated build of the
application to ensure that the code compiles correctly.
Automated testing: Automated tests (unit tests, integration tests, etc.)
are run against the new code to identify issues early.
Continuous delivery/deployment (CD): This is the process of
automatically deploying or delivering code updates to several environments,
including testing, staging, or production. There is a distinction between
continuous delivery and continuous deployment:
Continuous delivery: This automates the steps to get code ready for
deployment to production, but it does not necessarily mean automatic
deployment itself. A manual approval step might be included before pushing
the code live.
Key practices in CD:
Automated deployments: Code changes are automatically deployed to
staging environments for further testing.
Release automation: The deployment process is automated, making it
easy to release new versions of the application at any time.
Manual approval: While the code is always ready for deployment, an
additional manual approval step may be required before deploying to
production.
Continuous deployment: This automates the entire deployment process,
including pushing the code directly to production.
Key practices in continuous deployment:
Fully automated pipeline: The entire process from code commit to
deployment in production is automated.
Monitoring and logging: Continuous monitoring and logging are
essential to detect and respond to any issues that arise from deployments.
Rollback mechanism: Implement rollback mechanisms to revert to
previous versions if needed.
Benefits of CI/CD
CI/CD offers numerous benefits that enhance the software development lifecycle.
Here are some key advantages:
Faster software releases: By automating tasks and catching bugs early,
CI/CD enables teams to release new features and updates more frequently.
This can give you a competitive edge and allow you to be more responsive
to customer needs.
Improved software quality: The automated testing process that is part of a
CI/CD pipeline helps to ensure that code changes are functional and don't
introduce new regressions. This leads to higher quality software overall.
Increased deployment frequency and reliability: Automated deployment
processes reduce human error, making deployments more reliable and
consistent. Ensuring that every code change is always ready for deployment
leads to more frequent releases and faster delivery of features and fixes.
Improved productivity: Automating repetitive tasks (builds, tests,
deployments) frees up developers to focus on writing code and solving
complex problems.
Cost efficiency: Efficient use of resources by automating processes and
reducing the need for extensive manual labor.
Enhanced collaboration and communication: CI/CD fosters better
collaboration between development, operations, and QA teams by using a
unified pipeline for code integration, testing, and deployment.
Popular CI/CD tools
There are many popular CI/CD tools available, each with its own strengths and
weaknesses. Here are a few of the most widely used:
Jenkins: Jenkins is a free and open-source CI/CD server that is highly
customizable and extensible. It can be a good option for teams that need a
lot of flexibility and control over their CI/CD pipeline.
GitHub Actions: GitHub Actions is a CI/CD platform that is built into
GitHub. It is a good option for teams that are already using GitHub for
version control. GitHub Actions is easy to use and integrates well with other
GitHub features.
GitLab CI/CD: GitLab is another CI/CD platform that is built into a
version control system, GitLab. Similar to GitHub Actions, GitLab CI/CD is
a good option for teams that are already using GitLab.
Azure DevOps: Azure is a CI/CD platform that is part of Microsoft's Azure
cloud platform. It is a good option for teams that are already using other
Azure services. Azure DevOps offers a wide range of features, including
build management, deployment management, and release management.
Circle CI: Circle CI is a cloud-based CI/CD platform that is easy to use and
get started with. It is a good option for teams that are looking for a simple
and straightforward CI/CD solution.
Travis CI: Travis is a cloud-based CI/CD platform that is popular with
open-source projects. It is a good option for teams that are looking for a free
and easy-to-use CI/CD solution.
Introduction to CI/CD tool GitHub Actions
GitHub Actions is a powerful tool for building CI/CD pipelines directly within
your GitHub repositories. You define these workflows using .yaml files, a human-
readable format. Workflows can be triggered by various events, such as pushing
code changes, opening pull requests, or scheduled times.
Here are some key features of GitHub Actions:
Workflow automation: You can create custom workflows to build, test,
and deploy your code automatically on every push to your repository or on
a defined schedule.
Matrix builds: GitHub Actions supports matrix builds, which allow you to
run your workflows across multiple combinations of operating systems,
platforms, and environments.
Parallel job processing: You can configure your workflows to run jobs in
parallel, which can significantly reduce build and test times.
Artifacts and caching: GitHub Actions allows you to persist data between
jobs and workflows using artifacts and caching, which can improve
performance and reduce build times.
Hosted runners: GitHub provides hosted runners for Linux, Windows,
and macOS environments, which you can use to run your workflows
without needing to set up your own infrastructure.
Self-hosted runners: If your workflow requires specific hardware or
software configurations, you can create and use self-hosted runners to run
your workflows.
Marketplace actions: GitHub offers a rich ecosystem of pre-built actions
in the GitHub Marketplace, which you can use to simplify and streamline
your workflows.
Third-party integrations: GitHub Actions integrates with various third-
party services like AWS, Azure, Google Cloud, and more, allowing you to
seamlessly incorporate these services into your workflows.
Secrets management: GitHub Actions provides secure storage and
management of sensitive data, such as access tokens, API keys, and other
secrets, which can be used in your workflows.
Powerful YAML syntax: GitHub Actions workflows are defined using a
simple yet powerful YAML syntax, which makes it easy to describe and
configure complex workflows.
Setting up basic GitHub Actions workflow
Let us go through the step-by-step process for setting up a basic GitHub Actions
workflow.
Pre-request: The user has a GitHub account to create the new repository.
1. Create a GitHub repository:
a. Sign in to GitHub: Go to GitHub and sign in with your account.
b. Create a new repository:
i. Click the plus sign (+) in the top right corner and select New
repository.
ii. Fill in the repository name and description (optional), and choose
whether it should be public or private.
iii. Click Create repository.
2. Add your project files:
a. Upload or create your project files in the new repository. You can do
this by:
i. Clicking Add file and then Upload files to upload files from your
computer.
ii. Clicking Add file and then Create new file to create a file directly
in the repository.
3. Create a .github directory:
a. Create the .github directory:
i. In your repository, click Add file and then Create new file.
ii. Name your new file .github/workflows/main.yml (make sure to
type the whole path).
iii. This will automatically create a .github directory with a
workflow's subdirectory and a main.yml file.
4. Define your workflow:
a. Edit the main.yml file:
i. In the main.yml file, you will define your workflow. Here is a basic
example to get you started:
name: CI
on: [push, pull]
jobs:
build:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v2
- name: Set up Node.js
uses: actions/setup-node@v2
with:
node-version: '18'
- name: Install dependencies
run: npm install
- name: Run tests
run: npm test
5. Commit the workflow:
a. Commit the changes:
i. Scroll down to the Commit new file section.
ii. Add a commit message (something like "Add GitHub Actions
workflow").
iii. Make sure Commit directly to the main branch is selected.
iv. Click Commit new file.
6. Trigger the workflow:
a. Push changes to the repository:
i. Whenever you push new changes to the repository, the workflow
will automatically run.
ii. You can trigger the workflow manually by making any small
change in the repository (like updating a README file) and
pushing it.
You have successfully set up a GitHub Actions workflow for your project. Now,
every time you push changes to your repository, GitHub Actions will
automatically run the workflow you defined.
Running Playwright tests in GitHub Actions
Running Playwright tests in GitHub Actions is a great way to automate and
integrate your Playwright testing suite with your continuous
integration/continuous deployment (CI/CD) pipeline.
Here is an overview of the steps you can follow to set it up:
1. Install Playwright in your project: If you have not already, install
Playwright in your newly created project by running:
npm init playwright@latest
2. Write your Playwright tests: Create your Playwright test files (e.g.,
*.spec.js) in your project's test directory. Following is the test case, which
opens the URL https://playwright.dev/ and verifies the title of the page.
Also click on ‘Get started’ button and verify the text ‘Installation’
should be visible:
// @ts-check
const { test, expect } = require('@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();
});
3. Create a GitHub Actions workflow file: In your repository's
.github/workflows/ directory, create a new YAML file (e.g.,
playwright.yml) to define your GitHub Actions workflow.
4. Define the workflow: In your playwright.yml file, specify the events that
should trigger the workflow (e.g., push, pull), and define the jobs and steps
to run your Playwright tests. Here is an example:
name: Playwright Tests
on:
push:
branches: [ main, master ]
pull_request:
branches: [ main, master ]
jobs:
test:
timeout-minutes: 60
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: actions/setup-node@v4
with:
node-version: 20
- name: Install dependencies
run: npm ci
- name: Install Playwright Browsers
run: npx playwright install --with-deps
- name: Run Playwright tests
run: npx playwright test
- uses: actions/upload-artifact@v4
if: always()
with:
name: playwright-report
path: playwright-report/
retention-days: 30
This workflow installs Node.js, installs your project dependencies, installs
Playwright, and then runs your Playwright tests using the npx playwright test
command.
Let us break down each part of the YAML configuration:
name: Defines the name of the workflow as "Playwright Tests".
on: Specifies the events that trigger the workflow.
push:The workflow runs when changes are pushed to the main or master
branches.
pull_request: The workflow also runs when a pull request is made to the
main or master branches.
jobs: Defines a collection of jobs to run as part of the workflow.
test: The name of the job.
timeout-minutes: Sets a timeout for the job to 60 minutes.
runs-on: Specifies the virtual environment to run the job on, which is
ubuntu-latest.
Steps: Defines a series of steps to be executed by the job.
uses: actions/checkout@v4: This step checks out the repository's code
using the actions/checkout action at version 4.
uses: actions/setup-node@v4: This step sets up a Node.js environment
using the actions/setup-node action at version 4.
with: Specifies parameters for the setup-node action.
node-version: 18: Sets the Node.js version to 18.
name: Install dependencies: This step runs npm ci to install the
project's dependencies.
name: Install Playwright Browsers: This step runs npx playwright
install --with-deps to install Playwright and its dependencies.
name: Run Playwright tests: This step runs npx playwright test to
execute the Playwright tests.
uses: actions/upload-artifact@v4: This step uses the actions/upload-
artifact action to upload artifacts.
if: always(): Ensures this step runs regardless of whether previous
steps succeeded or failed.
with: Specifies parameters for the upload-artifact action.
name: playwright-report: Sets the name of the artifact to playwright-
report.
path: playwright-report/: Specifies the path to the artifact, which is
the playwright-report/ directory.
retention-days: 30: Sets the retention period for the uploaded
artifact to 30 days.
Following are steps that cover how to commit/push the code and execute the test
cases in GitHub actions:
1. Commit and push the changes: Commit the playwright.yml file and your
Playwright test files and push the changes to your GitHub repository.
2. View the GitHub Actions run: In your GitHub repository, navigate to the
Actions tab to view the workflow runs and their statuses. You can click on
a specific run to see detailed logs and the Playwright test report artifact.
Figure 12.1: Actions tab
3. Click on a specific run to see detailed logs, in the following screenshot you
can see test case are executed successfully.
Figure 12.2: Test execution
4. Click on test, under Run Playwright tests you can see 2 test cases are
executed in three browsers Chromium, Firefox, and WebKit.
Figure 12.3: Playwright individual test execution
In the next section, you will see how test scripts are executed across different
browsers.
Cross browser testing with Playwright
Cross-browser testing ensures your web application works as expected across
different web browsers and their versions. Playwright is a powerful testing tool
developed by Microsoft, providing automation for Chromium, Firefox, and
WebKit browsers.
Cross-browser testing is a crucial step in the development and maintenance of any
web application. Here is why:
Users come with all sorts of browsers:
Diverse browser use: Users access the web through various browsers
such as Chrome, Firefox, Safari, and even older versions of these
browsers. Ensuring compatibility across this diversity is vital.
Different rendering engines: Each browser uses a different rendering
engine (Blink for Chrome, Gecko for Firefox, WebKit for Safari), which
can interpret and display web content differently. What works on one
browser may not work on another.
Bad browser experience = lost user:
User frustration: If your website does not function correctly in a user’s
preferred browser, they are likely to get frustrated and leave. A smooth
and functional browsing experience is crucial for user retention.
Potentialloss of users: A negative experience might not only cause users
to abandon your site but also turn to competitors whose sites work
seamlessly across different browsers. This can result in lost traffic,
revenue and damage to your brand reputation.
Testing ensures a smooth experience:
Identify and fix issues: Cross-browser testing helps you spot and
resolve issues specific to different browsers, ensuring that your
application performs well universally.
Consistency: It ensures that your web application provides a consistent
user experience across all browsers, which is critical for maintaining user
satisfaction and engagement.
Accessibility and inclusivity: Ensuring your application works across
all browsers makes it accessible to a broader audience, including those
using less common or older browsers.
Benefits of using Playwright for cross-browser testing
Following are some of the benefits of using Playwright for cross-browser testing:
Multi-browser support: Playwright supports Chromium (Chrome, Edge),
Firefox, and WebKit (Safari).
Consistency: Provides consistent APIs across different browsers.
Auto-waiting: Automatically waits for elements to be ready before
interacting with them.
Parallel testing: Supports running tests in parallel across multiple
browsers, which speeds up the testing process.
Rich reports: Detailed test reports and screenshots to help diagnose issues.
In the next section, you will see how test scripts are executed on different
browsers.
Running test scripts on different browsers
To run test scripts on multiple browsers, you can set up a configuration in
Playwright to define projects for each browser. Here is a detailed example:
playwright.config.js:
const { defineConfig } = require('@playwright/test');
module.exports = defineConfig({
projects: [
{
name: 'chromium',
use: { ...devices['Desktop Chrome'] },
},
{
name: 'firefox',
use: { ...devices['Desktop Firefox'] },
},
{
name: 'webkit',
use: { ...devices['Desktop Safari'] },
},
],
});
Following is a screenshot of the Playwright config file:
Figure 12.4: Playwright config file
Create test script: Create a test script, for example, example.spec.js, and include
the following code to run tests across multiple browsers:
// @ts-check
const { test, expect } = require('@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();
});
Run the tests: Execute the tests by running the following command:
Command: npx playwright test
Following is the HTML report generated after executing the test cases in headless
mode in three browsers (Chromium, Firefox, and WebKit):
Figure 12.5: HTML report
Running test scripts on a particular browser
By default, Playwright runs the test cases in all three browsers; if you want to run
the test cases on a particular browser, you can use the command: npx playwright
test --project=chromium, which allows you to run your Playwright tests
specifically in the Chromium browser. This command is particularly useful when
you have multiple projects configured in your Playwright configuration file
(playwright.config.js), and you want to target a specific browser for testing.
Similarly, you can run tests in Firefox or WebKit by replacing chromium with
firefox or webkit respectively:
npx playwright test --project=firefox
npx playwright test --project=webkit
In the following figure, you can see test cases are executed in firefox, in the
same way you can execute test cases in webkit:
Figure 12.6: Execution in Firefox
Running test scripts across browsers from VS Code
Another way of executing the test case is using VS Code. Click on the Testing tab
from the left navigation. Click on the Select Configuration dropdown where you
can see all three browsers, and by clicking on any of the browsers, you can
execute the test case.
Figure 12.7: Execution through VS Code
In the next section, you will see how test scripts are executed in serial and parallel
mode.
Test execution in serial and parallel mode
Parallel test execution is an essential feature in test automation, allowing tests to
run concurrently rather than sequentially, which can significantly reduce the
overall testing time. Playwright, a powerful browser automation tool, supports
both serial and parallel test execution.
Running test scripts in serial mode
By default, Playwright runs tests in parallel mode if you do not configure it for
serial execution. This means that one test will run after another, ensuring there is
no overlap, and each test completes before the next one begins.
To execute the test cases in serial, we have to do the setting ‘fullyParallel:
false’ in playwright.config.js file. Tests within the same spec file run serially,
meaning they execute one after another. This behavior can lead to underutilization
of a powerful machine capable of handling multiple workers. For instance, if your
machine has four workers but we have done the setting fullyParallel: false, the
tests will be executed sequentially, using only one worker at a time. Consequently,
the other workers remain idle, reducing the efficiency of your test execution.
There are various ways of executing the test cases serially. The first method is by
doing the following setting in the config file:
// playwright.config.js
module.exports = {
// Other configurations...
fullyParallel: false,
};
The second method is using the --workers=1 option: You can run all tests in serial
mode by setting the number of workers to 1. This forces the test runner to execute
tests sequentially.
npx playwright test --workers=1
Another way is by configure test.describe to run tests in serial mode by using
the configure method. Here is how you can set up your tests to run in serial mode
using this approach:
test.describe.configure(
{ mode: 'serial' }
);
test('runs first', async ({ page }) => {});
test('runs second', async ({ page }) => {});
In the following figure, you can observe the configuration for running test cases in
serial mode. Here, four test cases are being executed utilizing only one worker,
leaving the other workers idle.
Figure 12.8: FullyParallel:false
Running test scripts in parallel mode
You can configure tests to run concurrently across multiple worker processes,
drastically reducing test execution time. Use the fullyParallel: true option in
your Playwright configuration file (playwright.config.js). Setting this to true
instructs Playwright to maximize parallelism by launching separate workers for
each test file. Playwright spins up multiple worker processes, allowing tests from
different files to run concurrently. This significantly reduces overall execution
time.
// playwright.config.js
module.exports = {
// Other configurations...
fullyParallel: true,
};
By enabling fullyParallel, the following benefits are achieved:
Maximized worker utilization: All available workers on the machine can
be used simultaneously. For example, if your machine has four workers,
enabling fullyParallel allows all four workers to run tests concurrently,
rather than just one worker executing tests sequentially.
Faster test execution: Running tests in parallel significantly reduces the
total time taken to execute all tests. This is especially beneficial for large
test suites, as it can greatly improve the speed of the testing process.
Improved efficiency: With better resource utilization, the overall efficiency
of your test infrastructure is improved, leading to quicker feedback and
more rapid development cycles.
In the following figure, you will notice the setup for executing test cases in
parallel mode. Each test case runs within its own worker, ensuring no worker
remains idle. This allows for the efficient parallel execution of test cases,
maximizing resource utilization.
Figure 12.9: FullyParallel:true
Execute test scripts in serial parallel mode
Let us demonstrate executing the test case both in serial and parallel modes using
the example provided below for illustrative purposes.
// @ts-check
const { test, expect } = require('@playwright/test');
test('TF #01,TS #01 has title', async ({ page }) => {
await page.goto('https://www.lambdatest.com/blog');
await expect(page).toHaveTitle(/LambdaTest Blogs/);
});
test('TF #01,TS #02 get started link', async ({ page }) => {
await page.goto('https://www.lambdatest.com/blog');
await expect(page).toHaveTitle(/LambdaTest Blogs/);
});
test('TF #01,TS #03 has title', async ({ page }) => {
await page.goto('https://www.lambdatest.com/blog');
await expect(page).toHaveTitle(/LambdaTest Blogs/);
});
test('TF #01,TS #04 get started link', async ({ page }) => {
await page.goto('https://www.lambdatest.com/blog');
await expect(page).toHaveTitle(/LambdaTest Blogs/);
});
Let us first execute the test case in serial mode. In the following figure shown, you
can observe that all four test cases are executed sequentially, utilizing only one
worker.
Figure 12.10: Execution in serial mode
Execute the test case in parallel mode. In the screenshot below, you can see that
four test cases are executed by four workers. When test cases are executed in
parallel mode, the execution time is drastically reduced.
Figure 12.11: Execution in parallel mode
In the next section, you will see how we can shard tests among different machines.
Sharding tests between multiple machines
Sharding tests in Playwright allows you to distribute test execution across multiple
machines, which can significantly reduce the total test runtime. This is particularly
useful for large test suites or when running tests in parallel to speed up the CI/CD
pipeline.
By default, Playwright runs tests in parallel and strives for optimal utilization of
CPU cores on your machine. In order to achieve even greater parallelization, you
can further scale Playwright test execution by running tests on multiple machines
simultaneously. We call this mode of operation sharding.
Sharding:
Imagine you have a massive test suite. Running them all on a single
machine could take a long time.
Sharding comes to the rescue by splitting your test suite across multiple
machines (shards).
This is ideal for very large test sets that would overwhelm a single
machine's resources.
Benefits:
Significantly reduced overall test execution time.
Better utilization of available computing power across multiple
machines.
Here is a simple explanation of sharding tests in Playwright:
Imagine you have a huge pile of laundry to fold (your tests):
Working alone (single machine): You would have to fold every item
one by one, which takes a long time.
Sharding with friends (multiple machines): You split the pile into
smaller chunks and give each friend a chunk to fold. Everyone works on
their own pile at the same time, finishing much faster.
Here is how Playwright does this:
Dividing the work: Playwright splits your test suite into smaller groups
called shards, like dividing the laundry pile.
Sending shards to different machines: Each shard is sent to a different
machine (like a friend), where it runs independently.
Running tests simultaneously: All the machines work on their assigned
shards at the same time, just like friends folding laundry together.
Combining results: Once all the shards are finished, Playwright gathers the
results from each machine and puts them together into a single report, like
combining the folded laundry into one big pile.
In the next section, you will see how we apply the concept of sharding with an
example in Playwright.
Sharding tests in Playwright with GitHub action
To integrate sharding in your CI/CD pipeline with GitHub Actions for distributing
and running tests across multiple jobs, you can modify your existing workflow to
utilize matrix expansion and parameterize the number of shards.
For running tests across shared, we have to update the existing default .yml file
with the following code:
name: Playwright Tests
on:
push:
branches: [ main, master ]
pull_request:
branches: [ main, master ]
jobs:
playwright-tests:
timeout-minutes: 60
runs-on: ubuntu-latest
strategy:
fail-fast: false
matrix:
shard: [1/4, 2/4,3/4,4/4]
steps:
- uses: actions/checkout@v3
- uses: actions/setup-node@v3
with:
node-version: 20
- name: Install dependencies
run: npm ci
- name: Install Playwright browsers
run: npx playwright install --with-deps
- name: Run Playwright tests
run: npx playwright test --shard ${{ matrix.shard }}
- name: Upload blob report to GitHub Actions Artifacts
if: always()
uses: actions/upload-artifact@v3
with:
name: blob-report
path: blob-report
retention-days: 1
merge-reports:
# Merge the reports and Generate the Final report
if: always()
needs: [playwright-tests]
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- uses: actions/setup-node@v3
with:
node-version: 18
- name: Install dependencies
run: npm ci
- name: Download blob reports from GitHub Actions
Artifacts
uses: actions/download-artifact@v3
with:
name: blob-report
path: blob-report
- name: Merge into HTML Report
run: npx playwright merge-reports --reporter html
./blob-report
- name: Upload HTML report
uses: actions/upload-artifact@v3
with:
name: html-report--attempt-${{ github.run_attempt }}
path: playwright-report
retention-days: 14
This code is a GitHub Actions workflow for running Playwright tests and
generating reports.
Let us break it down:
Workflow triggers:
The workflow is triggered on push events to the main or master
branches, as well as on pull_request events targeting those branches.
Playwright tests job:
This job is named playwright-tests and runs on an Ubuntu latest
environment.
It has a timeout of 60 minutes and is configured to not fail fast.
The job uses a matrix strategy with four shards (1/4, 2/4, 3/4, 4/4) for
parallel test execution.
The steps include checking out the repository, setting up Node.js version
20, installing dependencies, installing Playwright browsers with
dependencies, running Playwright tests with the specified shard, and
uploading the blob report to GitHub Actions Artifacts.
Mergereportsjob:
Named merge-reports, this job runs on an Ubuntu latest environment.
It is configured to always run, regardless of the outcome of previous
jobs.
This job depends on the completion of the playwright-tests job.
Steps include checking out the repository, setting up Node.js version 18,
installing dependencies, downloading the blob reports from GitHub
Actions Artifacts, merging them into an HTML report using npx
playwright merge-reports, and uploading the HTML report as an
artifact.
Execution result in CI/CD GitHub action
In a CI/CD setup using GitHub Actions, when you push code changes to your
repository, GitHub will trigger the CI/CD workflow defined in your repository.
GitHub Actions starts executing the defined workflow steps. These steps can
include tasks such as checking out the code, installing dependencies, running
tests, and deploying the application.
As part of the workflow, test cases are executed. This could involve running
integration tests, or any other type of automated tests you have set up for your
project. After the test cases are executed, the CI/CD workflow reports the results.
In the following figure, you can see that we pushed the code, and the test case
started executing in CI/CD pipeline:
Figure 12.12: CI/CD pipeline
Four test cases are executed across four shardings, with each test case being
executed by a separate shard.
Figure 12.13: Test case executions across shard
Finally, the test cases are executed successfully, and you can see blob and HTML
reports are generated.
Figure 12.14: Blob and HTML report
When we download the HTML report by clicking on html-report-attempt-1, you
can see that all the test cases have been executed successfully in the opened
HTML report:
Figure 12.15: Combine HTML report
Conclusion
To recap, in this chapter, we explored the fundamentals of CI/CD and introduced
GitHub Actions as a tool for implementing continuous integration and
deployment. We covered the steps to set up a basic GitHub Actions workflow and
demonstrated how to run Playwright tests within this framework. Additionally, we
discussed techniques for cross-browser testing with Playwright, as well as
executing tests in both serial and parallel modes. Finally, we examined the process
of sharding tests across multiple machines to optimize test execution.
In the upcoming chapter, we will introduce ChatGPT and explore how it can be
effectively utilized in automation. We will demonstrate the creation of Playwright
automation test scripts with ChatGPT's assistance and show how to develop more
complex automation test scripts. Additionally, we will cover how to use ChatGPT
for creating and debugging scripts and discuss its limitations in these contexts.
Multiple choice questions
1. What does CI/CD stand for?
a. Continuous integration/continuous development
b. Continuous improvement/continuous deployment
c. Continuous integration/continuous deployment
d. Continuous innovation/continuous delivery
2. What is the first step in setting up a GitHub Actions workflow?
a. Writing a YAML file
b. Installing dependencies
c. Configuring environment variables
d. Running Playwright tests
3. How can tests be executed to optimize performance in GitHub Actions?
a. Sequentially
b. In serial mode
c. In parallel mode
d. Randomly
4. What is the purpose of sharding tests between multiple machines?
a. To reduce the number of tests
b. To increase the test execution time
c. To distribute tests for faster execution
d. To simplify the test scripts
5. What technique is described for distributing tests across multiple
machines?
a. Clustering
b. Sharding
c. Load balancing
d. Partitioning
Answers
1. c.
2. a.
3. c.
4. c.
5. b.
CHAPTER 13
Using AI with Playwright for E2E
Testing
Introduction
In the preceding chapter, we explored the fundamentals of CI/CD and
introduced GitHub actions as a tool for implementing continuous integration
and deployment. We covered the steps to set up a basic GitHub actions
workflow and demonstrated how to run Playwright tests within this
framework. Additionally, we discussed techniques for cross-browser testing
with Playwright, as well as executing tests in both serial and parallel modes.
Finally, we examined the process of sharding tests across multiple machines
to optimize test execution.
This chapter aims to provide an in-depth exploration of how artificial
intelligence (AI) is revolutionizing the field of test automation. By
understanding the various AI automation tools available, the chapter will
elucidate the role and impact of generative AI on automating and debugging
tests. Specifically, it will focus on leveraging ChatGPT, a powerful AI
language model, to automate and debug tests using Playwright. Through
practical examples, the chapter will guide readers on generating end-to-end
(E2E) scripts with ChatGPT and highlight the limitations of using ChatGPT
in test automation.
Structure
This chapter covers the following topics:
Artificial intelligence in test automation
AI automation tools
About generative AI
Leveraging ChatGPT to automate tests with Playwright
Leveraging ChatGPT to debug Playwright test
E2E script generation with ChatGPT
Limitations with ChatGPT
Objectives
By the end of this chapter, you will have a solid understanding of how AI
and Generative AI can be harnessed to streamline test automation processes,
with practical knowledge of using ChatGPT in conjunction with Playwright
for both automation and debugging tasks. Readers will have a
comprehensive understanding of how AI and generative AI, particularly
through tools like ChatGPT, are shaping the future of test automation. They
will be equipped with practical knowledge and skills to leverage these
technologies effectively while also being aware of their limitations and how
to address them.
Artificial intelligence in test automation
In the rapidly changing world of software development, automated testing
has emerged as a critical component in guaranteeing the reliability and
robustness of applications. The integration of AI into this domain has
revolutionized the generation of test cases, making the process more
efficient and sophisticated.
AI is bringing a new wave of innovation to the field of quality assurance
(QA) automation. By incorporating AI techniques, particularly machine
learning (ML), QA teams can significantly enhance their testing processes.
Here is how AI is transforming QA automation:
Test case generation: AI can automatically generate test cases based
on application behavior, requirements, or user stories. This helps
ensure comprehensive coverage and reduces the manual effort
involved in writing tests,
Test optimization: ML algorithms can analyze past test runs to
identify redundant or obsolete test cases, prioritize test execution
based on risk and impact, and suggest optimizations for better
performance.
Defect prediction: AI models can predict potential defects by
analyzing historical data, code changes, and patterns in previous bugs.
This helps in focusing testing efforts on high-risk areas and improving
the quality of releases.
Visual testing: Computer vision algorithms can be used for visual
testing, where AI compares the visual appearance of the application
with expected results. This is particularly useful for UI/UX testing,
ensuring that the interface looks and behaves as intended.
Self-healing tests: AI can enable self-healing capabilities in test
scripts. When an application changes, such as an element's location or
properties, AI can dynamically adjust the test scripts to accommodate
these changes without human intervention.
Benefits of AI in test automation
Here are some key benefits of using AI in test automation:
Increased efficiency: AI automates repetitive tasks like test case
creation and execution, freeing up testers' time for more strategic
activities. Additionally, AI can learn and adapt over time, further
streamlining the testing process.
Higher accuracy: Unlike manual testing, which can be susceptible to
human error, AI-powered tests are consistent and reliable. They can
meticulously execute even complex test scenarios, reducing the
chance of bugs slipping through the cracks.
Cost savings: By automating tasks and identifying defects early in the
development cycle, AI helps reduce the need for manual rework and
saves on overall testing costs. Early defect detection translates to
quicker fixes and faster time-to-market.
Scalability: As applications grow in size and complexity, traditional
test automation can become cumbersome. AI-powered testing tools
can effortlessly scale to handle massive test suites and intricate
software systems.
Smarter test data management: AI can generate realistic and diverse
test data sets covering a wide range of scenarios. This is especially
valuable for complex systems where data-driven testing is crucial.
Evolving and self-healing tests: AI-powered testing tools can learn
and adapt over time. They can identify changes in the application
under test and adjust test scripts accordingly. This reduces
maintenance overhead and keeps tests relevant.
AI automation tools
AI and ML have transformed the landscape of QA automation, providing
tools that enhance efficiency, accuracy, and coverage. Here is an overview of
some prominent AI tools available for QA automation:
Applitools: It leverages Visual AI to automate visual testing, ensuring
the UI looks and functions correctly across different devices and
browsers.
Use case:
Cross-browser testing: Verify the visual correctness of your
application on various browsers.
Responsive design testing: Ensure your application’s UI adapts
correctly to different screen sizes and resolutions.
Testim: It uses ML to author, execute, and maintain automated tests. It
adapts to changes in the application UI, reducing the maintenance
burden.
Use case:
UI test automation: Create robust UI tests that adapt to changes
in the application’s UI.
Test maintenance: Reduce test maintenance by allowing the
tool to adapt to minor UI changes automatically.
Functionize: It combines natural language processing (NLP) and
ML to create, execute, and analyze automated tests.
Use case:
Test creation using NLP: Write tests in plain English, allowing
non-technical stakeholders to contribute to test creation.
Self-healing tests: Automatically update tests when minor
changes occur in the application UI.
Mabl: It provides intelligent test automation for web applications,
utilizing ML to enhance test creation and maintenance.
Use case:
Intelligent test creation: Quickly create tests using a simple,
intuitive interface.
Self-healing tests: Reduce the need for manual updates by
allowing the tool to fix broken tests automatically.
Katalon Studio: It is an all-in-one test automation solution that
incorporates AI capabilities to enhance test creation, execution, and
maintenance.
Use case:
Test generation: Use AI-driven suggestions to create more
comprehensive test cases.
Smart Waits: Improve test stability by automatically handling
dynamic web elements.
Sauce Labs: It offers a cloud-based platform for testing web and
mobile applications, integrating AI to enhance test analysis and
reporting.
Use case:
Cross-platform testing: Test applications on a wide range of
devices and browsers in the cloud.
AI-powered analytics: Use AI to analyze test results and
identify potential issues quickly.
TestRigor: It is an AI-powered test automation tool that simplifies the
creation and maintenance of automated tests.
Use case:
Cross-browser and cross-device testing: Enables automated
testing across various browsers and devices to ensure consistent
user experience.
Natural language test writing: Allows users to write test cases
in plain English, which are then converted into executable
scripts.
KaneAI: The FirstAI generated testing agent in software testing that
relies on Large Language Models in testing endeavours. Due to the
nature of test generation, evolution, and management, KaneAI
provides a new perspective to the teams that are using it for testing
and automation.
Use case:
End-to-End Test Creation and Evolution: KaneAI allows
teams to create, debug, and adapt complex end-to-end tests
using natural language, making test automation more intuitive
and accessible. Another advantage is that the system is
intelligent and adapts tests as soon as there is any change in the
application.
Advanced AI-Powered Platform: Developed on modern
LLMs, KaneAI is able to make advanced understanding of the
commands and demands of users to allow QA teams create and
control test cases without the need of extensive technical
proficiency. This in turn minimizes on time expended in
desitests, and subsequently, in managing their execution.
Natural Language Test Writing: It also offers the ability for
users to write test cases in natural language. This feature can do
away with the requirement of coding expertise, allowing all
members of a team engage in test automation.
To understand how to leverage AI to automate tests with Playwright It’s is
mandatory to know about generative AI
About generative AI
Generative AI, short for generative artificial intelligence, refers to a category
of AI techniques and models that have the ability to generate new content
autonomously. This content can include text, images, audio, video, and even
code, often mimicking human creativity and output.
Generative AI models operate by learning patterns and structures from large
datasets using various ML algorithms, such as neural networks. These
algorithms enable the models to understand and replicate the underlying
patterns of the data they are trained on, allowing them to generate new,
original content based on input prompts.
Generative AI is revolutionizing various fields, from creative arts to practical
applications like content generation, data synthesis, and even predictive
modeling. It is powered by sophisticated algorithms like generative
adversarial networks (GANs), variational autoencoders (VAEs), and
Transformer models, which have significantly advanced the capabilities of
AI in producing high-quality, diverse, and contextually relevant outputs.
Generative AI encompasses various types of models designed to create
diverse forms of content.
Here are some key types of generative AI models:
Generative adversarial networks:
Description: GANs consist of two neural networks, a generator
and a discriminator. The generator creates new data instances, such
as images, while the discriminator evaluates them for authenticity
against a dataset.
Application: Widely used for generating realistic images, video
frames, and even text.
Variational autoencoders (VAEs):
Description: VAEs are a type of autoencoder that learns a
probabilistic distribution in the latent space, enabling the
generation of new data points that resemble the training data.
Application: Used for generating realistic images and text
sequences and learning latent representations of data.
Transformer models:
Description: Transformers are attention-based models that excel at
handling sequential data by capturing relationships and
dependencies across the input sequence.
Application: Dominant in NLP tasks like text generation, language
translation, and dialogue generation.
In the next section, you will see how we can use transformer models, e.g.
ChatGPT, in automation using Playwright.
Leveraging ChatGPT to automate tests with Playwright
Playwright is a powerful automation tool for web testing, supporting
multiple browsers and providing robust APIs. Integrating transformer
models with Playwright can bring AI-driven insights and capabilities to test
automation.
You can use transformer models like ChatGPT in automation with
Playwright.
Here is how you can leverage these capabilities:
Test case generation: Transformer models, such as ChatGPT, are
revolutionizing the way test scripts are generated by leveraging NLP
and deep learning.
Test script maintenance: AI models can help maintain test scripts by
understanding the changes in the application and suggesting or making
necessary updates to the scripts.
Intelligent error handling: Transformer models can predict potential
failures and suggest fixes based on historical data and patterns,
making error handling more efficient.
Before diving into how we can use prompts to generate
Playwright/JavaScript test scripts, let us first understand what are prompts.
About prompts
AI prompting indeed refers to the process of interacting with an AI system
by providing specific instructions or queries to achieve a desired outcome.
This process involves crafting prompts that guide the AI to generate the
desired response or perform a specific task.
In QA automation testing, AI prompts are crucial for efficiently generating
test scenarios, automating test script creation, managing test data,
maintaining test cases, and improving reporting and analysis. By providing
clear and specific instructions, AI prompts help streamline the testing
process, ensuring thorough and accurate validation of software applications.
Below are some of the examples of prompt in QA automation testing:
Generating test scenarios:
Prompt: Create test cases for user registration with various input
combinations including valid, invalid, and edge cases.
Output: A set of test cases covering different input scenarios for
user registration.
Automating test script creation:
Prompt: Generate a Selenium test script for verifying the search
functionality on the website.
Output: A Selenium script that performs search operations and
verifies results.
Enhancing test data management:
Prompt: Generate sample data for testing user profiles with
different subscription levels.
Output: A dataset with various user profiles and subscription
levels.
In the next section, you will see some of the prompts to generate the script
using Playwright and JavaScript.
ChatGPT prompts to generate the script
Here are a few prompts where you can use ChatGPT to help generate a
Playwright script for a given task.
Let us see various prompts for writing Playwright/JavaScript code.
Prompt 1: Write Playwright/JavaScript code for opening
the URL of the site http://qaautomationlabs.com/ and get
the title of the page.
Following is the generated code by ChatGPT for the asked query:
(async () => {
// Launch browser
const browser = await chromium.launch({ headless:
false });
// Create a new page
const page = await browser.newPage();
// Navigate to the QA Automation Labs website
await page.goto('http://qaautomationlabs.com/');
// Get the title of the page
const pageTitle = await page.title();
// Print the title
console.log(`Page Title: ${pageTitle}`);
// Close the browser
await browser.close();
})();
Now, make some minor changes and run the above code again. It is working
fine without any issues.
// @ts-check
const { test } = require("@playwright/test");
const playwright = require("@playwright/test");
test('has title', async ({ page }) => {
// Launch browser
const browser = await playwright.chromium.launch();
await page.goto('http://qaautomationlabs.com/');
// Get the title of the page
const pageTitle = await page.title();
// Print the title
console.log(`Page Title: ${pageTitle}`);
// Close the browser
await browser.close();
});
In the following figure in the console, we can see that the title is printed in
the console:
Figure 13.1: Prompt to print title
Prompt 2: Write Playwright/JavaScript code to Open the
URL https://www.saucedemo.com/ login with the username:
standard_user and the password: secret_sauce and then
click on the login button.
Here is the Playwright code in JavaScript to automate the login scenario for
the given URL:
const { chromium } = require('playwright');
(async () => {
// Launch the browser
const browser = await chromium.launch({ headless:
false });
const page = await browser.newPage();
// Open the URL
await page.goto('https://www.saucedemo.com/');
// Fill in the username
await page.fill('input#user-name', 'standard_user');
// Fill in the password
await page.fill('input#password', 'secret_sauce');
// Click on the login button
await page.click('input#login-button');
// Add a small delay to see the result before closing
await page.waitForTimeout(5000);
// Close the browser
await browser.close();
})();
After making some changes in the script, run the above code again, and it
will work fine without any issues. The user is able to log in without any
issues. Refer to the following figure:
Figure 13.2: Prompt to login
Let us take a complex scenario now.
Prompt 3: Write Playwright/ Javascript code to open the
URL https://www.saucedemo.com/ login with username:
standard_user and password: secret_sauce and then click
on the login button and click on ADD TO CART button,
then click on the shopping cart link and then verify add
an item with text Sauce Labs Backpack in the shopping
cart.
Here is the Playwright code in JavaScript to automate login, add to cart, and
verify the added item in the cart:
const { chromium } = require('playwright');
(async () => {
// Launch the browser
const browser = await chromium.launch({ headless:
false });
const page = await browser.newPage();
// Open the URL
await page.goto('https://www.saucedemo.com/');
// Fill in the username
await page.fill('input#user-name', 'standard_user');
// Fill in the password
await page.fill('input#password', 'secret_sauce');
// Click on the login button
await page.click('input#login-button');
// Click on the "ADD TO CART" button for the Sauce
Labs Backpack
await page.click('button[name="add-to-cart-sauce-labs-
backpack"]');
// Click on the shopping cart link
await page.click('.shopping_cart_link');
// Verify the item is in the shopping cart
const itemInCart = await
page.$('div.inventory_item_name');
const itemText = await itemInCart.innerText();
if (itemText === 'Sauce Labs Backpack') {
console.log('Test Passed: Sauce Labs Backpack is in
the shopping cart.');
} else {
console.log('Test Failed: Sauce Labs Backpack is not
in the shopping cart.');
}
// Add a small delay to see the result before closing
await page.waitForTimeout(5000);
// Close the browser
await browser.close();
})();
After making some changes in the script, run the previously mentioned code
again, and it will work fine without any issues.
// @ts-check
const { test } = require("@playwright/test");
const playwright = require("@playwright/test");
test("Verify ChatGPT Generated Code for Login Page",
async ({ page }) => {
const browser = await playwright.chromium.launch();
// Open the URL
await page.goto('https://www.saucedemo.com/');
// Fill in the username
await page.fill('input#user-name', 'standard_user');
// Fill in the password
await page.fill('input#password', 'secret_sauce');
// Click on the login button
await page.click('input#login-button');
// Click on the "ADD TO CART" button for the Sauce Labs
Backpack
await page.click('button[name="add-to-cart-sauce-labs-
backpack"]');
// Click on the shopping cart link
await page.click('.shopping_cart_link');
// Verify the item is in the shopping cart
const itemInCart = await
page.$('div.inventory_item_name');
const itemText = await itemInCart.innerText();
if (itemText === 'Sauce Labs Backpack') {
console.log('Test Passed: Sauce Labs Backpack is in
the shopping cart.');
} else {
console.log('Test Failed: Sauce Labs Backpack is not
in the shopping cart.');
}
// Add a small delay to see the result before closing
await page.waitForTimeout(5000);
// Close the browser
await browser.close();
})
In the following screenshot, you can see that the script is being executed
successfully:
Figure 13.3: Prompt For to add into the cart
Prompt 4: Generate the Cucumber feature file. Open the
URL https://www.saucedemo.com/, log in with username:
standard_user and the password: secret_sauce, and then
click on the LOGIN button.
Following is the generated feature file:
Feature: User login to SauceDemo
Scenario: User logs in with valid credentials
Given I open the SauceDemo login page
When I enter "standard_user" as the username
And I enter "secret_sauce" as the password
And I click on the LOGIN button
Then I should see the home page
Prompt 5: Generate the step definition for the feature
file. Open the URL https://www.saucedemo.com/, log in
with the username: standard_user and the password:
secret_sauce, and then click on the LOGIN button.
Generate the code in Playwright/JavaScript for the feature file:
Feature: User login to SauceDemo
Scenario: User logs in with valid credentials
Given I open the SauceDemo login page
When I enter "standard_user" as the username
And I enter "secret_sauce" as the password
And I click on the LOGIN button
Then I should see the home page
Following is the generated step definition for the previously mentioned
feature file:
const { Given, When, Then } =
require('@cucumber/cucumber');
const { chromium } = require('playwright');
let browser;
let page;
Given('I open the SauceDemo login page', async
function() {
browser = await chromium.launch({ headless: false });
const context = await browser.newContext();
page = await context.newPage();
await page.goto('https://www.saucedemo.com/');
});
When('I enter {string} as the username', async
function(username) {
await page.fill('#user-name', username);
});
When('I enter {string} as the password', async
function(password) {
await page.fill('#password', password);
});
When('I click on the LOGIN button', async function() {
await page.click('#login-button');
});
Then('I should see the home page', async function() {
await page.waitForSelector('.title'); // Adjust the
selector based on the actual home page content
await browser.close();
});
From the above prompts, it is clear that while ChatGPT can be a valuable
tool for generating code, it is important to recognize that the output may not
always be flawless. Users should anticipate making some modifications to
ensure the code functions as intended. However, the advantage is that the
generated code is often quite close to being runnable, providing a strong
foundation and saving significant development time.
Leveraging ChatGPT to debug Playwright test
ChatGPT can help identify syntax errors in your code by reviewing your
script and pointing out potential issues, such as missing brackets, typos, or
incorrect usage of functions.
Here are some ways ChatGPT can assist in debugging:
Identifying syntax errors: ChatGPT can help identify syntax errors in
your code by reviewing your script and pointing out potential issues,
such as missing brackets, typos, or incorrect usage of functions.
Example prompt: Can you review this Playwright
script for syntax errors?
Correcting selectors: ChatGPT can help verify that you are using the
correct selectors for elements on the web page. It can suggest
corrections based on the provided HTML structure or typical
practices.
Example prompt: Is #user-names the correct selector
for the username input on the SauceDemo login
page?
Explaining error messages: ChatGPT can interpret error messages
and explain what they mean, helping you understand the root cause of
issues. It can also suggest potential fixes based on the error message.
Example prompt: What does the error page.fill: Test
timeout of 30000ms exceeded mean, and how can I
fix it?
ChatGPT prompts to debug the script
Debugging scripts can be challenging, but using the right prompts can help
you identify and fix issues efficiently.
Here are some prompts to guide your debugging process. Let us ask a couple
of questions in the form of prompts:
// @ts-check
const { test,expect } = require("@playwright/test");
const playwright = require("@playwright/test");
test("Verify ChatGPT debussing capibility", async ({
page }) => {
const browser = await playwright.chromium.launch();
await page.goto("https://www.saucedemo.com/");
// Enter the username and password
await page.fill("#user-names", "standard_user");
await page.fill("#password", "secret_sauce");
// Click the login button
await page.click('[type="submit"]');
const title = await page.title();
expect(title).toHaveProperty('Example Domain');
// Close the browser
await browser.close();
});
We get the following error when we execute the above script:
waiting for locator('#user-names') in the following script:
Response: The error you are encountering is due to an incorrect selector for
the username input field. The correct selector for the username input field on
the SauceDemo website is #user-name, not #user-names. Additionally, there
is a typo in the expect statement.
Summary of changes:
Corrected the selector: Changed #user-names to #user-name.
Corrected the expect statement: Changed
expect(title).toHaveProperty('Example Domain') to
expect(title).toBe('Swag Labs').
In the next section, you will see how we can leverage ChatGPT to generate
end-to-end script for an e-commerce site from login to adding product in the
cart and finally logout from the application.
E2E script generation with ChatGPT
An E2E script is a set of instructions or code that automates a complete user
flow through a system, from start to finish. This could be anything from a
website interaction to a complex software application.
ChatGPT is a large language model capable of generating human-quality
text. When used for E2E script generation.
Let us take an example where we ask ChatGPT to generate an E2E script for
the following scenario. Let us see how it works.
Following is the scenario for a site www.saucedemo.com/ with login, add
the product into the cart and place the order, and finally log out from the
application:
1. Open the URL www.saucedemo.com/
2. Login with the username: standard_user and the password:
secret_sauce
3. Click on Add to cart button against the text Sauce Labs Fleece
Jacket
4. Click on the shopping cart icon in the top right side
5. Verify the product with name Sauce Labs Fleece Jacket
6. Click on Checkout button
7. Fill first name: John
8. Fill last name: Parker
9. Fill Zip/Postal code 201010
10. Click on the Continue button
11. Verify the text “Sauce Labs Fleece Jacket” in checkout page
12. Click on the Finish button
13. Verify the text: Thank you for your order!
14. Click on the Back Home button
15. Click on the hamburger menu
16. Click on Logout
17. Verify if the Login button should be visible
Following is the script generated by ChatGPT for the above-mentioned
scenario with minor tweaks:
// @ts-check
const { test } = require("@playwright/test");
const playwright = require("@playwright/test");
test("Verify ChatGPT Generated Code for Login Page New",
async ({ page }) => {
const browser = await playwright.chromium.launch();
// Step 1: Open the URL
await page.goto("https://www.saucedemo.com/");
// Step 2: Login
await page.fill("#user-name", "standard_user");
await page.fill("#password", "secret_sauce");
await page.click("#login-button");
// Step 3: Add "Sauce Labs Fleece Jacket" to cart
await page.click("text=Sauce Labs Fleece Jacket");
await page.click("text=Add to cart");
// Step 4: Click on shopping-cart icon
await page.click(".shopping_cart_link");
// Step 5: Verify the product in the cart
const productInCart = await
page.innerText(".inventory_item_name");
if (productInCart === "Sauce Labs Fleece Jacket") {
console.log("Product verified in cart: Sauce Labs
Fleece Jacket");
} else {
console.error("Product verification failed in
cart.");
}
// Step 6: Click on checkout button
await page.click("#checkout");
// Step 7,8,9,10: Fill checkout information
await page.fill("#first-name", "John");
await page.fill("#last-name", "Parker");
await page.fill("#postal-code", "201010");
await page.click("#continue");
// Step 11: Verify the product on the checkout page
const productInCheckout = await
page.innerText(".inventory_item_name");
if (productInCheckout === "Sauce Labs Fleece Jacket")
{
console.log("Product verified in checkout: Sauce
Labs Fleece Jacket");
} else {
console.error("Product verification failed in
checkout.");
}
// Step 12: Click on Finish button
await page.click("#finish");
// Step 13: Verify the text "Thank you for your
order!"
const thankYouText = await page.innerText(".complete-
header");
if (thankYouText === "THANK YOU FOR YOUR ORDER") {
console.log("Order completion message verified:
Thank you for your order!");
} else {
console.error("Order completion message verification
failed.");
}
// Step 14: Click on Back Home button
await page.click("#back-to-products");
// Step 15: Click on hamburger menu
await page.click("#react-burger-menu-btn");
// Step 16: Click on logout
await page.click("#logout_sidebar_link");
// Step 17: Verify Login button is visible
const loginButtonVisible = await
page.isVisible("#login-button");
if (loginButtonVisible) {
console.log("Login button is visible after
logout.");
} else {
console.error("Login button is not visible after
logout.");
}
// Close browser
await browser.close();
});
Let us execute the above-mentioned script:
When we execute the above E2E script in VS Code with minor changes, it
runs successfully without any errors, this demonstrate that AI models can
significantly aid in generating reliable and efficient E2E test cases. The
necessity for only minor tweaks underscores the robustness and utility of AI-
generated scripts in real-world scenarios.
In the following screenshot, you can see the script is run successfully with
minor tweaks:
Figure 13.4: Prompt E2E
Limitations with ChatGPT
While transformer models like ChatGPT are incredibly powerful and
versatile, they do have several limitations. So before using it, we need to
keep the following points in mind:
Inconsistent test case generation: Due to its probabilistic nature,
ChatGPT can produce different test cases for the same requirements,
making it challenging to maintain consistency and reliability.
False positives and negatives: ChatGPT might generate test cases
that produce false positives or negatives, leading to incorrect
conclusions about software quality.
Handling dynamic and complex scenarios: Complex or dynamic
web elements that change properties frequently can be challenging for
the model to handle correctly in generated scripts.
Security and privacy concerns: Using ChatGPT to generate or
discuss test cases can pose security and privacy risks, especially if
sensitive information is shared.
Error handling and debugging: Handling errors and debugging is
crucial. ChatGPT can suggest possible solutions or debugging steps,
but it lacks the ability to troubleshoot issues interactively or in real-
time, which is often necessary for resolving complex problems.
Limited context understanding: While ChatGPT can process and
understand text inputs, its understanding of complex scenarios, such as
those involving intricate software environments and specific
application states, may be limited.
Conclusion
To recap, AI is revolutionizing test automation by introducing advanced
tools and methodologies. AI automation tools streamline the testing process,
while generative AI, such as ChatGPT, enhances script creation and
debugging. Leveraging ChatGPT with Playwright enables E2E script
generation and simplifies debugging. These advancements offer significant
improvements in speed and accuracy, but careful integration and validation
are key to maximizing their benefits.
Multiple choice questions
1. What is the primary benefit of using AI in test automation?
a. Reduces test execution time
b. Increases manual testing effort
c. Decreases test coverage
d. Increases the complexity of test cases
2. Which AI-powered tool is commonly used for visual regression
testing?
a. Selenium
b. Applitools
c. JUnit
d. TestNG
3. How can ChatGPT assist in automating tests with Playwright?
a. By writing the test scripts
b. By performing manual tests
c. By integrating with hardware devices
d. By debugging production issues directly
4. When using ChatGPT for debugging Playwright tests, what is a
common approach?
a. Asking ChatGPT to identify errors in test logs
b. Manually stepping through code without ChatGPT
c. Using ChatGPT to deploy applications
d. Disabling ChatGPT during test execution
5. What is a significant limitation of using ChatGPT in test
automation?
a. It can only write tests in JavaScript
b. It may generate incorrect or suboptimal code
c. It cannot be integrated with Playwright
d. It is too slow for practical use
Answers
1. a.
2. b.
3. a.
4. a.
5. b.
Join our book’s Discord space
Join the book's Discord Workspace for Latest updates, Offers, Tech
happenings around the world, New Release and Sessions with the Authors:
https://discord.bpbonline.com
Index
Symbols
<a> 45
<div> 45
<input> 45
<span> 45
A
Accessibility Testing 248, 249
Accessibility Testing, issues
Duplicate IDs, interactive 250
poor color, contrast 250
UI Controls, missing 250
Accessibility Testing, key arguments
equal access 249
legal, compliance 249
market reach, expanding 249
search engine, optimizing 249
social, responsibility 249
user experience, improving 249
AI/ML-based Testing 9
AI/ML-based Testing, architecture 10
AI/ML-based Testing, cons
bias, susceptibility 10
complexity 10
data, requirements 10
upfront cost 10
AI/ML-based Testing, pros
costs, reducing 9
efficiency, increasing 9
flexibility 10
scalability 10
AI Prompt 313
AI Prompt, scenarios 313
Allure Report 153
Allure Report, frameworks
screenshots, attachments 154
HTML/XML, formats 154
Allure Report, reasons
ease use 153
rich/interactive 153
Allure Report, setting up 154-156
Annotations 163
Annotations, functions
group tests 164
tags tests 164
test.fail() 163
test.fixme() 163
test.skip() 163
test.slow() 164
API, applications
map 224
payment 224
social media 224
weather 224
API, components
Endpoints 222
protocols 222
request 222
responses 222
API, protocols
HTTP 222
REST 222
API, steps 219, 220
API Test, authenticating 231-233
API Test, benefits
cost-effectiveness, maintainability 223
efficiency, improving 223
quality/reliability, enhancing 223
reusability, integrating 223
API, testing 220
API Test, key reasons
Direct API, interacting 224
end-to-end test, integrating 224
flexibility, enhancing 224
rich assertion, library 225
scalability, flexibility 225
test suite, managing 224
API Test, setup 225
Application Programming Interface (API) 218
Applitools 11
Applitools, key features
accessibility, testing 12
AI-powered visual, validating 12
cross-browser, testing 12
eyes dashboards 12
ultafast grid 12
visual, testing 12
Assertions 157
Assertions, characteristics 158
Assertions, reasons
bug, detecting 157
control flow, executing 157
debugging 157
performance, monitoring 157
runtime, checking 157
selient failures, avoiding 157
Assertions, types
Auto-retrying 159-161
Soft 162, 163
Authentication 233
Authentication, implementing 235-240
Authentication, ways 234
Automate Visual Testing, benefits
cross-browser, compatibility 270
parallel, execution 270
regression, detection 270
visual regression, testing 270
visual, validating 270
Automation Testing 2, 3
Automation Testing, benefits
comprehensive test, coverage 16
cost, saving 16
cross-platform 16
data-driven, testing 16
early detection 16
efficiency 15
faster, feedback 16
repeatability 15
Automation Testing, history 3
Axe-core 253
Axe-core, scenarios
elements, excluding 260
scan rules, disabling 261, 262
specific element, scanning 258
WCAG Violation 259
B
BDD 192
BDD, advantages
business value 194
collaboration, communicating 194
tools, supporting 194
user-centric focus 194
BDD, lifecycle 193, 194
BDD, syntax
Given 192
Then 193
When 193
before() 233
browser context, handling 110, 111
Built-In Locators, elements
AltText 67
by label 66
by role 64
by text 65
by title 68
placeholder 66
Test-ID 68, 69
C
CBT, reasons
accessibility, compliance 50
browser market, sharing 49
bug, detecting 50
compatibility 49
user experience, consistency 49
wider audience, reach 49
CBT With Desktop, configuring 50, 51
CBT With Mobile, optimizing 51-53
chain selectors 70
chain selectors, breakdown 71
ChatGPT 321
ChatGPT, limitations 326, 327
ChatGPT Script, debugging 321, 322
ChatGPT, ways 321
Checkboxes 57
Checkboxes, types
Multi-Checkboxes 78, 79
Single Checkbox 76, 77
CI/CD 280
CI/CD, advantages
collaboration, enhancing 282
cost, efficiency 282
frequency, reliability 281
productivity, improving 281
software quality, improving 281
software, releases 281
CI/CD GitHub Action, Executing 303-305
CI/CD, parts
Continuous Delivery (CD) 280, 281
Continuous Integration (CI) 280
CI/CD, tools
Azure DevOps 282
Circle CI 282
GitHub Actions 282
GitLab 282
Jenkins 282
Travis CI 282
CI, key practices
automate, building 280
automate, testing 280
frequent commits 280
Codegen 128
Codegen, key features
assertion, generating 128
code, customizing 128
element, selecting 128
program language, supporting 128
Codegen Record Test, steps 130-133
Codegen, scenarios 129, 130
Continuous Delivery (CD) 280
Continuous Integration (CI) 280
Cross Browser Testing 290
Cross Browser Testing, benefits
auto-waiting 291
browser, supporting 291
consistency 291
parallel, testing 291
rich, reports 291
Cross-Browser Testing (CBT) 49
Cross Browser Testing, scenarios 291-294
Cross Browser Testing, steps 290
CSS Selectors 58
CSS Selectors, types
Attribute 60
Class 59
combine 61
ID 59
Cucumber 195
Cucumber Background 201, 202
Cucumber, breakdown
feature 196
scenarios 196
Cucumber Data Table 202
Cucumber Hooks 198
Cucumber Hooks, purpose 198
Cucumber Hooks, types
After Hook 198
Before Hook 198
Cucumber HTML Reporter 209
Cucumber HTML Reporter, use case 209-214
Cucumber, process
execution, reporting 196
features files 195
step, defining 195
Cucumber Tag 198
Cucumber Tag, benefits
CI/CD, integrating 199
parallel, executing 199
scenarios, grouping 198
selective, executing 199
Cucumber With Playwright, integrating 204-209
D
Data-Driven Test 186
Data-Driven Test, advantages
data storage, centralizing 186
parameterize, testing 187
scalability 187
test data, separation 186
test logic, reusing 186
Data-Driven Test, steps 187, 188
Debug Scripts 134
Debug Scripts, ways 134-136
DELETE Request 231
download file, handling 92
download file, methods
multiple file 94-96
single file 93, 94
drag/drop, handling 84, 85
E
E2E Script 322
E2E Script, scenarios 322, 323
F
Feature 197
filter locators 71
filter locators, breakdown 71
framework based test 7
framework based test, advantages 8
framework based test, disadvantages 8
framework based test, tools
Cucumber 8
Jest 9
Pytest 9
Robot Framework 9
RSpec 9
TestNG 8
Functionize 13
Functionize, key features
AI-powered, testing 13
cross-platform, testing 14
data-driven, testing 14
NLP 13
self-healing, test 14
G
Generative AI 311, 312
Generative AI, capabilities
error, handling 313
test case, generating 313
test script, maintenance 313
Generative AI, key types
Generative Adversarial Network 312
Transformer Models 312
Variational Autoencoders (VAEs) 312
GET Request Test 225
GitHub Actions 282
GitHub Actions, key features
Artifacts, caching 283
Hosted, runners 283
marketplace, actions 283
Matrix, building 283
Parallel Job, processing 283
Powerful YAML, syntax 283
secrets, managing 283
self-hosted, runners 283
third-party, integrating 283
workflow, automation 282
GitHub Actions, setting up 283-285
H
Hooks 165
Hooks, functions
test.afterAll() 165
test.afterEach() 165
test.beforeAll() 165
test.beforeEach() 165
I
iframe 97
iframe, benefits
control 97
convenience 97
flexibility 97
iframe, steps 97-101
input boxes, handling 75
input buttons, handling 75, 76
J
JavaScript Alerts 102
JavaScript Alerts, types
confirm alert 103-105
prompt alert 106
standard alert 102
L
Locators 56
Locators, architectures 56
Locators, best practices
CSS Selectors, using 74
data attributes 74
hardcoded, avoiding 74
position-based, selectors 74
prioritize, attributes 74
semantic selectors, using 73
test locators 74
test logic 74
wait, strategies 74
XPath, avoiding 74
Locators, elements 58
Locators, types
Class Name 57
CSS 57
ID 57
link text/partial link 57
Name 57
tag name 57
XPath 57
Login Page 174
M
Mabl 12
Mabl, key features
AI-powered test, automating 12
API, testing 13
codeless test, automating 13
cross-browser, testing 13
data-driven, testing 13
performance, testing 13
self-healing, tests 13
visual, testing 13
Manual Testing 3
Manual Testing, benefits
adaptable, changing 4
assessment, usability 4
complex, scenarios 4
early, detecting 4
exploratory, testing 4
Manual Testing, limitations
human, errors 4
limit, scalability 4
resource-intensive 4
time-consuming 4
Manual Testing, process
defect, reporting 3
defect, verifying 3
regression, testing 4
requirement, analyzing 3
test case, designing 3
test environment, setup 3
test, execution 3
test, planning 3
Mock API Requests 241
Mock API Requests, breakdown 242, 243
Mock APIs 240
Mock APIs, benefits
environment, controlling 241
independent, developing 241
independent, development 241
isolation 240
resource usage, reducing 241
Modify API Responses 243
Modify API Responses, breakdown 244, 245
multi-select dropdowns, handling 81, 82
N
nested iframe 99
P
page.locator() 64
Page Object Model (POM) 170
Parallel Test Execution 295
Parallel Test Execution, scenarios 295-299
playback tool 120
playback tool, advantages
beginners, friendly 120
cross language, supporting 121
easy use 121
quick script, creating 121
time/effort, reducing 121
playback tool, disadvantages
brittle/rigid 121
code, reusability 121
error, handling 122
limit, coverage 121
maintenance, challenges 121
Playwright 17
Playwright, architectures
ease of use 36
execution, speed 36
features, supporting 36
installation, setup 36
language, bindings 36
Playwright Automation, reasons
asynchronous, nature 43
automation 43
cross-platform, compatibility 43
DOM, manipulation 43
industry, adoption 43
Playwright/Cypress, comparing 21
Playwright/Cypress Feature, comparing 21
Playwright, environment
Mac, installing 26-29
VS Code, installing 34, 35
Window, installing 30-33
Playwright Inspector 136
Playwright Inspector, parameters 136
Playwright, key components
client code 37
Execution, process 38
iterative, process 38
JSON Format 38
response, transmission 38
server 38
WebSocket, connecting 37
Playwright, key features
APIs, interaction 40
automatic, waiting 41
browser, contexts 40
built-in, reporters 41
CI/CD, integrating 41
emulation 40
file, uploading 41
headless/headful, modes 40
multi-browser, supporting 40
multiple language, supporting 40
network, interception 40
parallel, execution 40
screenshots, videos 41
selector, engine 40
test framework, integrating 41
trace, recording 41
Playwright, navigation
pick locator 126
record at cursor 125, 126
record the test 123-125
Playwright, purpose
e2e 42
.gitignore 42
node-modules 42
package.json 42
package-lock.json 42
playwright.config.js 42
test-examples 42
tests 42
Playwright, reasons
browser contexts 18
community, supporting 18
cross-browser, compatibility 17
device emulation 18
fast, reliable 18
headless browser, testing 18
integration, testing 18
multiple language, supporting 18
network intercept, mocking 18
page context, isolating 18
parallel, execution 18
Powerful API 17
technology, evolving 18
video, recording 18
Playwright/Selenium, comparing 19
Playwright/Selenium Features, comparing 19
POM, benefits
code, maintainability 172
components, reusable 172
easy, debugging 172
modularity/reusability 172
readability 172
test script, maintenance 172
test stability, enhancing 172
UI, abstraction 172
POM, breakdown
active community, ecosystem 172
async, operations 171
cross-browser, consistency 171
maintainability 171
rich feature 171
Unified API 171
POM, components
Assertions 171
constructor 171
page classes 170
page methods/actions 171
web elements 171
POM, implications 173-176
POM, steps
dependencies, importing 180
home page content, checking 180
LoginPage Object, creating 180
logout 180
perform, login 180
POM, use cases 180-185
POST Request Test 227, 228
prompt alert, breakdown
assertion 107
button, click 107
dialog, handling 107
event listener 107
navigation 107
PUT Request Test 230
Q
QA Automation 308
QA Automation, benefits
cost, saving 309
efficiency, increasing 309
higher, accuracy 309
scalability 309
self-healing 309
test data, managing 309
QA Automation, key points
defect, prediction 308
self-heal, tests 309
test case, generating 308
test, optimizing 308
visual, testing 309
QA Automation, tools
Applitools 309
Functionize 310
KaneAI 311
Katalon Studio 310
Mabl 310
Sauce Labs 310
Testim 310
TestRigor 311
R
radio buttons, handling 82, 83
record-and-playback 4
record-and-playback, benefits 4
record-and-playback, limitations 5
record-and-playback, tools
Cypress 5
Katalon Studio 5
Playwright 5
Selenium IDE 5
TestComplete 5
Reporter 144
Reporter, benefits
CI/CD 145
debugging 144
historical, analyzing 144
issue, identifying 144
performance, monitoring 145
test execution, visibility 144
user-friendly, interface 144
Reporter, types
Dot 147
GitHub Actions, annotations 153
HTML 148, 149
JSON 149, 150
JUnit 151, 152
Line 146
List 145, 146
Running Playwright Tests 285
Running Playwright Tests, steps 285, 286
S
Scenario 197
script-based automation 6
script-based automation, advantages 6
script-based automation, disadvantages 6
script-based automation, tools
Appium 7
NightwatchJS 7
Playwright 7
Selenium 7
TestCafe 7
WebDriverIO 7
setInputFiles() 88
Shadow DOM 113, 114
Shadow DOM, elements
boundary 114
host 114
root 114
tree 114
Sharding Tests 300
Sharding Tests, benefits 300
Sharding Tests, workflow 300
Sharding With GitHub, configuring 301-303
single-select dropdowns, handling 79, 80
standard alert, step
alert, accepting 103
alert message, validating 103
event listener, setup 103
navigation 103
test description 102
T
tabs, breakdown 109
tabs, handling 108, 109
test() 226
test automation pyramid 220
test automation pyramid, layers
business 222
database 222
presentation 221
test automation pyramid, levels
API Test 220
GUI Tests 221
unit tests 220
TestCraft 14
TestCraft, key features
AI-powered element 15
codeless, automation 15
collaboration, sharing 15
integration 15
visual test, building 15
test generator 127
test generator, benefits
code, reusing 128
code write, reducing 127
cross language, compatibility 128
fast test case, creating 128
resilient, locators 128
Testim 11
Testim, key features
AI-driven Test 11
cross-browser, testing 11
parameterization 11
self-maintenance 11
smart locators 11
test execution, analyzing 11
visual, testing 11
Testim, tools
Applitools 11
Functionize 13
Mabl 12
TestCraft 14
testRigor 14
testRigor 14
testRigor, key features
Generative AI 14
low-code 14
no code/codeless 14
plain english test, creating 14
veriety, supporting 14
testscript 43, 44
testscript, ways 45-48
Trace Viewer 139
Trace Viewer, ways
Trace CI 140, 141
Trace Locally 139, 140
U
UI Mode 137
UI Mode, purpose
actions 138
attachment 139
Call 138
console 138
errors 138
locator 138
log 138
Metadata 138
network 139
Source 138
upload file, handling 88
upload file, method
multiple file 90-92
single file 88-90
V
Visual Testing 266
Visual Testing, benefits
bugs, detecting 269
cross-browser, improving 269
efficiency, increasing 269
risk regression, reducing 270
User Experience, improving 269
Visual Testing, key aspects
automation 266
baseline, creating 266
comparison 266
cross-browser, testing 266
regression, testing 266
report, debugging 267
visual, validation 266
Visual Testing, steps
baseline, establishment 268
report, analyzing 268
screenshot, capturing 267
tools, comparing 268
Visual Testing, tools
Applitools 268
Cypress 269
Katalon 268
Percy 268
Playwright 269
Sauce Lab 269
Visual Testing, use cases
element/object, navigating 273, 274
full page 272
maxDiffPixels, indicating 275, 276
single page 270, 271
Visual Testing, ways
Automated 267
Manual 267
W
WCAG, levels 251
WCAG, principles
Operable 250
perceivable 250
Robust 251
understandable 250
WCAG, tools
axe DevTools 253
Lighthouse 253
WAVE 253
Web Content Accessibility Guidelines (WCAG) 250
window pop up model, handling 112
X
XPath 61
XPath, functions
contain() 62
starts-with() 63
text() 62
XPath, reasons
brittleness 64
performance 64
readability/maintainability 64
Y
YAML, breakdown 287, 288
YAML, test cases 288, 289