<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: Jaydeep Karale</title>
    <description>The latest articles on DEV Community by Jaydeep Karale (@_jaydeepkarale).</description>
    <link>https://dev.to/_jaydeepkarale</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F1219979%2Faf40697a-b4fa-4ce3-a62b-0e7e222b9c5d.png</url>
      <title>DEV Community: Jaydeep Karale</title>
      <link>https://dev.to/_jaydeepkarale</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/_jaydeepkarale"/>
    <language>en</language>
    <item>
      <title>How to Scroll to an Element in Playwright</title>
      <dc:creator>Jaydeep Karale</dc:creator>
      <pubDate>Sat, 09 Mar 2024 20:09:56 +0000</pubDate>
      <link>https://dev.to/_jaydeepkarale/how-to-scroll-to-an-element-in-playwright-1imh</link>
      <guid>https://dev.to/_jaydeepkarale/how-to-scroll-to-an-element-in-playwright-1imh</guid>
      <description>&lt;p&gt;Creating visually stunning and interactive websites has become a norm in the dynamic landscape of web development. However, this has led to the proliferation of content-rich pages extending beyond the viewport’s confines. As a result, users often need to scroll to an element to access all the information available.&lt;/p&gt;

&lt;p&gt;Scrolling isn’t just a technical necessity; it’s a critical aspect impacting user experience and site visits. When paired with lazy loading, efficient scrolling enhances engagement, encourages content discovery, and positively affects search engine rankings, leading to longer site visits and a more successful online presence.&lt;/p&gt;

&lt;p&gt;In this &lt;a href="https://www.lambdatest.com/playwright?utm_source=devto&amp;amp;utm_medium=organic&amp;amp;utm_campaign=feb_26&amp;amp;utm_term=bw&amp;amp;utm_content=webpage" rel="noopener noreferrer"&gt;Playwright&lt;/a&gt; tutorial, we’ll learn how to scroll to an element in Playwright. We’ll explore scenarios where scrolling is essential, understand the challenges it addresses, and provide practical insights into implementing effective scrolling strategies. So, let’s dive in and uncover the techniques that ensure your automated tests are as comprehensive and reliable as possible.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Need to Scroll to an Element
&lt;/h2&gt;

&lt;p&gt;For automation testers, replicating user interactions accurately is crucial to ensure the functionality and performance of these web applications.&lt;/p&gt;

&lt;p&gt;This is where the need to scroll to an element arises in &lt;a href="https://www.lambdatest.com/automation-testing?utm_source=devto&amp;amp;utm_medium=organic&amp;amp;utm_campaign=feb_26&amp;amp;utm_term=bw&amp;amp;utm_content=webpage" rel="noopener noreferrer"&gt;automation testing&lt;/a&gt;. Automation testers can utilize tools and frameworks to simulate user behavior and interactions on web pages. Scrolling, an essential aspect of user experience, becomes necessary when testing web applications. Scrolling allows testers to interact with hidden, dynamic content or lazy-loaded content, verify content, and validate the responsiveness of a website. It’s about reaching elements beyond the current viewport and ensuring the application behaves seamlessly during user navigation.&lt;/p&gt;

&lt;p&gt;In &lt;a href="https://www.lambdatest.com/learning-hub/browser-automation?utm_source=devto&amp;amp;utm_medium=organic&amp;amp;utm_campaign=feb_26&amp;amp;utm_term=bw&amp;amp;utm_content=learning_hub" rel="noopener noreferrer"&gt;browser automation&lt;/a&gt;, Playwright has emerged as a powerful &lt;a href="https://www.lambdatest.com/blog/automation-testing-frameworks/?utm_source=devto&amp;amp;utm_medium=organic&amp;amp;utm_campaign=feb_26&amp;amp;utm_term=bw&amp;amp;utm_content=blog" rel="noopener noreferrer"&gt;automation testing framework&lt;/a&gt; for automating interactions with web pages. In automation testing, mastering the art of scrolling with tools like Playwright becomes pivotal for ensuring web applications’ functionality and performance. With its auto-wait feature for elements to be actionable, &lt;a href="https://www.lambdatest.com/learning-hub/cross-browser-compatibility?utm_source=devto&amp;amp;utm_medium=organic&amp;amp;utm_campaign=feb_26&amp;amp;utm_term=bw&amp;amp;utm_content=learning_hub" rel="noopener noreferrer"&gt;cross-browser compatibility&lt;/a&gt;, and support for multiple programming languages, Playwright empowers testers to mimic user actions and assess a website’s functionality efficiently.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Discover how to elevate your &lt;a href="https://www.lambdatest.com/selenium?utm_source=devto&amp;amp;utm_medium=organic&amp;amp;utm_campaign=feb_26&amp;amp;utm_term=bw&amp;amp;utm_content=webpage" rel="noopener noreferrer"&gt;automation testing using Selenium&lt;/a&gt; for robust web applications.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2&gt;
  
  
  How to Setup Playwright?
&lt;/h2&gt;

&lt;p&gt;Playwright is an open-source, &lt;a href="https://www.lambdatest.com/learning-hub/end-to-end-testing?utm_source=devto&amp;amp;utm_medium=organic&amp;amp;utm_campaign=feb_26&amp;amp;utm_term=bw&amp;amp;utm_content=learning_hub" rel="noopener noreferrer"&gt;end-to-end testing&lt;/a&gt; and browser automation framework released by Microsoft. For this blog on how to scroll to an element in Playwright, we will use the Python API of Playwright.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Step 1: Create a dedicated virtual environment&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Creating a dedicated virtual environment is a good practice when working with Python. It helps create an isolation between dependencies for various projects.&lt;/p&gt;

&lt;p&gt;For this project, we will create a virtual environment called &lt;em&gt;playwrightscroll&lt;/em&gt; using Python 3.10.&lt;/p&gt;

&lt;p&gt;conda create -n playwrightscroll python=3.10&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fcdn-images-1.medium.com%2Fmax%2F2000%2F0%2Aacz5r8LTaEMgL2a3.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fcdn-images-1.medium.com%2Fmax%2F2000%2F0%2Aacz5r8LTaEMgL2a3.png"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The creation of a virtual environment can also be done using the built-in &lt;em&gt;venv&lt;/em&gt; module.&lt;/p&gt;

&lt;p&gt;python -m venv playwrightscroll&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fcdn-images-1.medium.com%2Fmax%2F2000%2F0%2AAomzWvC4wOHYbcRr.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fcdn-images-1.medium.com%2Fmax%2F2000%2F0%2AAomzWvC4wOHYbcRr.png"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Step 2: Activating the virtual environment and installing Playwright&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Next, we activate the newly created virtual environment using the conda.&lt;/p&gt;

&lt;p&gt;conda activate playwrightscroll&lt;/p&gt;

&lt;p&gt;If not using conda, the command to activate it using the activate script provided within the &lt;em&gt;playwrightscroll&lt;/em&gt; virtual environment.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fcdn-images-1.medium.com%2Fmax%2F2000%2F0%2AMHN7dY18nx8dhGbD.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fcdn-images-1.medium.com%2Fmax%2F2000%2F0%2AMHN7dY18nx8dhGbD.png"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Once activated, the Python API of Playwright can be installed using &lt;em&gt;pip install&lt;/em&gt; &lt;em&gt;pytest-playwright&lt;/em&gt;.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fcdn-images-1.medium.com%2Fmax%2F2000%2F0%2A3f3fwiwJiPtWspUY.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fcdn-images-1.medium.com%2Fmax%2F2000%2F0%2A3f3fwiwJiPtWspUY.png"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Step 3: Installing browsers (Required only when running tests locally)&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;The default Playwright installation does not come with any browser. We need to install the browsers using the &lt;em&gt;playwright install&lt;/em&gt; command.&lt;/p&gt;

&lt;p&gt;This will install the latest versions of Chromium, Firefox, and Webkit. This step is optional and unnecessary if we use Playwright on cloud testing platforms like LambdaTest.&lt;/p&gt;

&lt;p&gt;LambdaTest is an AI-based test orchestration and execution platform that allows you to run Playwright tests on a scalable and reliable cloud grid. With LambdaTest, you can perform &lt;a href="https://www.lambdatest.com/playwright-testing?utm_source=devto&amp;amp;utm_medium=organic&amp;amp;utm_campaign=feb_26&amp;amp;utm_term=bw&amp;amp;utm_content=webpage" rel="noopener noreferrer"&gt;Playwright testing&lt;/a&gt; on over 3000 unique combinations of browsers, operating systems, and devices.&lt;br&gt;
&lt;iframe width="710" height="399" src="https://www.youtube.com/embed/SqQ8SugRDos"&gt;
&lt;/iframe&gt;
&lt;br&gt;
Catch up on the latest tutorial around the &lt;a href="https://www.lambdatest.com/learning-hub/playwright-visual-regression-testing?utm_source=devto&amp;amp;utm_medium=organic&amp;amp;utm_campaign=feb_26&amp;amp;utm_term=bw&amp;amp;utm_content=learning_hub" rel="noopener noreferrer"&gt;Playwright visual regression testing&lt;/a&gt;, &lt;a href="https://www.lambdatest.com/learning-hub/automation-testing?utm_source=devto&amp;amp;utm_medium=organic&amp;amp;utm_campaign=feb_26&amp;amp;utm_term=bw&amp;amp;utm_content=learning_hub" rel="noopener noreferrer"&gt;automated testing&lt;/a&gt;, and more. Subscribe to the LambdaTest YouTube Channel for quick updates.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fcdn-images-1.medium.com%2Fmax%2F2500%2F0%2AHz5TqdbWKhqOGmS-.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fcdn-images-1.medium.com%2Fmax%2F2500%2F0%2AHz5TqdbWKhqOGmS-.png"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Learn the secrets of &lt;a href="https://www.lambdatest.com/learning-hub/jenkins?utm_source=devto&amp;amp;utm_medium=organic&amp;amp;utm_campaign=feb_26&amp;amp;utm_term=bw&amp;amp;utm_content=learning_hub" rel="noopener noreferrer"&gt;Jenkins for automation testing&lt;/a&gt; to streamline your CI/CD pipelines.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;This completes the setup required.&lt;/p&gt;

&lt;p&gt;Let us do a quick version check so that replicating the setup when referring to this blog will be easy. As shown, the scenarios demonstrated in this blog hereafter are fully compatible with&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;Python 3.10.12&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Playwright 1.37.0&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;pytest 7.4.0&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fcdn-images-1.medium.com%2Fmax%2F2000%2F0%2ATSETKCTwHiTtwGDh.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fcdn-images-1.medium.com%2Fmax%2F2000%2F0%2ATSETKCTwHiTtwGDh.png"&gt;&lt;/a&gt;&lt;/p&gt;
&lt;h2&gt;
  
  
  Scrolling to an Element in Playwright
&lt;/h2&gt;

&lt;p&gt;Vertical scrolling is available on most websites, from those using simple HTML and CSS to Single-Page Applications (SPA) developed using complex modern web technologies like React and Angular and so on.&lt;/p&gt;

&lt;p&gt;Horizontal scrolling is considered bad UX and should generally be avoided (or minimized) if necessary. Don’t take my word for it; check out this usability guideline from the world leaders in Research-Based user experience.&lt;/p&gt;

&lt;p&gt;There are three ways to scroll to an element in Playwright. Let us briefly understand each of them before moving on to the demo of each method.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Master &lt;a href="https://www.lambdatest.com/appium?utm_source=devto&amp;amp;utm_medium=organic&amp;amp;utm_campaign=feb_26&amp;amp;utm_term=bw&amp;amp;utm_content=webpage" rel="noopener noreferrer"&gt;mobile app automation testing using Appium&lt;/a&gt; for flawless mobile apps.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h2&gt;
  
  
  scroll_into_view_if_needed() method
&lt;/h2&gt;

&lt;p&gt;This method is called after locating an element using the &lt;a href="https://www.lambdatest.com/blog/playwright-locators/?utm_source=devto&amp;amp;utm_medium=organic&amp;amp;utm_campaign=feb_26&amp;amp;utm_term=bw&amp;amp;utm_content=blog" rel="noopener noreferrer"&gt;Playwright Locator&lt;/a&gt;. If the element passes the actionability checks of being Stable and Attached, this method will try to scroll to an element in Playwright.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Usage&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;locator.scroll_into_view_if_needed(**kwargs)&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Arguments&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;em&gt;timeout&lt;/em&gt; float (optional): Maximum time in milliseconds; default is 3000&lt;/p&gt;
&lt;h2&gt;
  
  
  Mouse API wheel() method
&lt;/h2&gt;

&lt;p&gt;The &lt;em&gt;mouse&lt;/em&gt; class helps simulate the movement of the mouse, with the top left corner of the viewport considered the starting point. Each Playwright page has access to its instance of the &lt;em&gt;mouse&lt;/em&gt; class available &lt;em&gt;via page.mouse()&lt;/em&gt;.&lt;/p&gt;

&lt;p&gt;In the &lt;em&gt;mouse&lt;/em&gt; class, we can call the &lt;em&gt;page.mouse.wheel()&lt;/em&gt; method to scroll to an element in Playwright either in the upward or downward direction.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Usage&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;mouse.wheel(delta_x, delta_y)&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Arguments&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;em&gt;delta_x&lt;/em&gt; float: Pixels to scroll horizontally&lt;/p&gt;

&lt;p&gt;&lt;em&gt;delta_y&lt;/em&gt; float: Pixels to scroll vertically&lt;/p&gt;
&lt;h2&gt;
  
  
  Keyboard API press() method
&lt;/h2&gt;

&lt;p&gt;The keyboard class provides an API for managing a virtual keyboard. Using this API’s &lt;em&gt;press()&lt;/em&gt; method and supplying the arguments &lt;em&gt;PageDown, PageUp, ArrowUp, ArrowDown, ArrowLeft,&lt;/em&gt; or &lt;em&gt;ArrowRight,&lt;/em&gt; we can simulate scrolling in the required direction.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Usage&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;page.keyboard.press()&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Arguments&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;em&gt;key str:&lt;/em&gt; Name of the key to press or a character to generate, such as ArrowLeft or a.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;delay&lt;/em&gt; float (optional): Time to wait between keydown and keyup in milliseconds. Defaults to 0.&lt;/p&gt;

&lt;p&gt;Now that we know the various methods available to scroll to an element in Playwright, let us study each of them in detail using various scenarios and their implementation, starting with the &lt;em&gt;scroll_into_view_if_needed()&lt;/em&gt; method.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Unlock the power of &lt;a href="https://www.lambdatest.com/blog/python-automation-testing/?utm_source=devto&amp;amp;utm_medium=organic&amp;amp;utm_campaign=feb_26&amp;amp;utm_term=bw&amp;amp;utm_content=blog" rel="noopener noreferrer"&gt;automation testing in Python&lt;/a&gt; for more efficient coding and testing.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h2&gt;
  
  
  Demonstration: Using the scroll_into_view_if_needed() Method
&lt;/h2&gt;

&lt;p&gt;In this section, you will learn how to scroll to an element in Playwright using the &lt;em&gt;scroll_into_view_if_needed()&lt;/em&gt; method.&lt;/p&gt;

&lt;p&gt;The &lt;em&gt;scroll_into_view_if_needed()&lt;/em&gt; method brings an element located using a Playwright Locator into the viewport. If the located element is already in the viewport, the method takes no action.&lt;/p&gt;

&lt;p&gt;To understand this, let us consider a practical scenario where an element is absent in the viewport.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Test Scenario: Element not visible in the current viewport&lt;/strong&gt;&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;&lt;p&gt;Navigate to the &lt;a href="https://www.lambdatest.com/selenium-playground/?utm_source=devto&amp;amp;utm_medium=organic&amp;amp;utm_campaign=feb_26&amp;amp;utm_term=bw&amp;amp;utm_content=webpage" rel="noopener noreferrer"&gt;LambdaTest Selenium Playground&lt;/a&gt; website.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Locate the ‘Contact Us’ element.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Use the &lt;em&gt;scroll_into_view_if_needed()&lt;/em&gt; method to scroll down to the ‘Contact Us’ element.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Viewport should be scrolled down and show the ‘Contact Us’ element.&lt;/p&gt;&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;&lt;strong&gt;Implementation&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;The complete test code is as follows.&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;def test_scroll_into_view_if_needed(page: Page):
    """
    Test verifies if the contact us section is visible and scrolls to it
    """    
    try:
        page.goto('https://www.lambdatest.com/selenium-playground/')
        base_locator = page.get_by_role('link', name="Contact Us")        
        expect(base_locator).to_be_visible()
        page.pause()
        base_locator.scroll_into_view_if_needed()
        page.pause()
        set_test_status(page, 'Passed','Blog exists')        
        page.close()
    except Exception as ex:
        set_test_status(page, 'Failed',str(ex))  
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;
&lt;p&gt;&lt;strong&gt;Code Walkthrough&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Let us now go through the test steps step-by-step.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Step 1&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;The default viewport of the website is shown below. The ‘Contact Us’ section is not visible.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fcdn-images-1.medium.com%2Fmax%2F3200%2F0%2A5jNVbNdtIZyLdRBx.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fcdn-images-1.medium.com%2Fmax%2F3200%2F0%2A5jNVbNdtIZyLdRBx.png"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Scrolling down further reveals the ‘Contact Us’ section.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fcdn-images-1.medium.com%2Fmax%2F3200%2F0%2ART-ateMfzDDTZy95.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fcdn-images-1.medium.com%2Fmax%2F3200%2F0%2ART-ateMfzDDTZy95.png"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Step 2&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fcdn-images-1.medium.com%2Fmax%2F2336%2F0%2AnsWzDM7TwDT_0iaW.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fcdn-images-1.medium.com%2Fmax%2F2336%2F0%2AnsWzDM7TwDT_0iaW.png"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Navigate to the website under test using the &lt;em&gt;goto()&lt;/em&gt; method.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Step 3&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fcdn-images-1.medium.com%2Fmax%2F3200%2F0%2ADh7lq0Wglci7v0gC.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fcdn-images-1.medium.com%2Fmax%2F3200%2F0%2ADh7lq0Wglci7v0gC.png"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fcdn-images-1.medium.com%2Fmax%2F2520%2F0%2AoMwCcsbM-Wtxe0II.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fcdn-images-1.medium.com%2Fmax%2F2520%2F0%2AoMwCcsbM-Wtxe0II.png"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The “Contact Us” section is a link, so we use the &lt;em&gt;get_by_role()&lt;/em&gt; Playwright Locator to locate the element.&lt;/p&gt;

&lt;p&gt;The located element is stored in the variable &lt;em&gt;base_locator&lt;/em&gt;.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Step 4&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fcdn-images-1.medium.com%2Fmax%2F2000%2F0%2AGt4x1A1vyhcI5TZk.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fcdn-images-1.medium.com%2Fmax%2F2000%2F0%2AGt4x1A1vyhcI5TZk.png"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;We use the Playwright &lt;em&gt;expect()&lt;/em&gt; assertion to ensure the element ‘Contact Us’ &lt;em&gt;to_be_visible()&lt;/em&gt;.&lt;/p&gt;

&lt;p&gt;The &lt;em&gt;page.pause()&lt;/em&gt; will pause the test execution so that we can view that before calling the &lt;em&gt;scroll_into_view_if_needed()&lt;/em&gt; method, the element not in the viewport.&lt;/p&gt;

&lt;p&gt;An intermediate screenshot from the execution is shown below.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Enhance your skills in &lt;a href="https://www.lambdatest.com/blog/selenium-with-java/?utm_source=devto&amp;amp;utm_medium=organic&amp;amp;utm_campaign=feb_26&amp;amp;utm_term=bw&amp;amp;utm_content=blog" rel="noopener noreferrer"&gt;automation testing using Java&lt;/a&gt; with our expert insights and tips.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fcdn-images-1.medium.com%2Fmax%2F3200%2F0%2ABR5d45ou6ufMTS8k.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fcdn-images-1.medium.com%2Fmax%2F3200%2F0%2ABR5d45ou6ufMTS8k.png"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Step 5&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fcdn-images-1.medium.com%2Fmax%2F2000%2F0%2Ao5utxq86Oj6y940W.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fcdn-images-1.medium.com%2Fmax%2F2000%2F0%2Ao5utxq86Oj6y940W.png"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;On the &lt;em&gt;base_locator&lt;/em&gt;, we call the &lt;em&gt;scroll_into_view_if_needed()&lt;/em&gt; method to bring the element ‘Contact Us’ into the viewport.&lt;/p&gt;

&lt;p&gt;The &lt;em&gt;page.pause()&lt;/em&gt; is used again to pause the test execution and ensure the browser does not close until we manually unpause it.&lt;/p&gt;

&lt;p&gt;Here is a screenshot of the resumed test execution.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fcdn-images-1.medium.com%2Fmax%2F3200%2F0%2AHfFezwbCwaXvKEin.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fcdn-images-1.medium.com%2Fmax%2F3200%2F0%2AHfFezwbCwaXvKEin.png"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The actionability logs highlighted at the bottom right clearly show the successful scroll.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Step 6&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fcdn-images-1.medium.com%2Fmax%2F2212%2F0%2A8edQFs_Zvx5vWGpy.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fcdn-images-1.medium.com%2Fmax%2F2212%2F0%2A8edQFs_Zvx5vWGpy.png"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The &lt;em&gt;page.close()&lt;/em&gt; closes the session and completes the test.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Execution&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;The test can be executed using the following command.&lt;/p&gt;

&lt;p&gt;pytest -v&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fcdn-images-1.medium.com%2Fmax%2F2330%2F0%2A-GM4vt049Yzt8pZf.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fcdn-images-1.medium.com%2Fmax%2F2330%2F0%2A-GM4vt049Yzt8pZf.png"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Before execution of the &lt;em&gt;scroll_into_view_if_needed()&lt;/em&gt; method.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fcdn-images-1.medium.com%2Fmax%2F3200%2F0%2AGc9lsjR2HhmVixLJ.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fcdn-images-1.medium.com%2Fmax%2F3200%2F0%2AGc9lsjR2HhmVixLJ.png"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;After execution of the &lt;em&gt;scroll_into_view_if_needed()&lt;/em&gt; method.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fcdn-images-1.medium.com%2Fmax%2F3200%2F0%2AntpEP-OXUcbF4dte.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fcdn-images-1.medium.com%2Fmax%2F3200%2F0%2AntpEP-OXUcbF4dte.png"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Since we also have the &lt;em&gt;expect()&lt;/em&gt; assertion, the test passes as the ‘Contact Us’ element is visible.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fcdn-images-1.medium.com%2Fmax%2F2330%2F0%2Ak5JR7cTjV7fzDi4z.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fcdn-images-1.medium.com%2Fmax%2F2330%2F0%2Ak5JR7cTjV7fzDi4z.png"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Scenario: Element visible in the current viewport&lt;/strong&gt;&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;&lt;p&gt;Navigate to the &lt;a href="https://www.lambdatest.com/selenium-playground/?utm_source=devto&amp;amp;utm_medium=organic&amp;amp;utm_campaign=feb_26&amp;amp;utm_term=bw&amp;amp;utm_content=webpage" rel="noopener noreferrer"&gt;LambdaTest Selenium Playground&lt;/a&gt; website.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Locate the ‘Context Menu’ element.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Use the &lt;em&gt;scroll_into_view_if_needed()&lt;/em&gt; method on the located Context Menu element.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;No efect should be seen on the scroll since the element is already within the viewport.&lt;/p&gt;&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;&lt;strong&gt;Implementation&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Here is the complete implementation of the test.&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;def test_scroll_into_view_if_needed_element_already_in_viewport(page: Page):
    """
    Test verifies if the Context Menu section is visible and no scroll happens
    """    
    try:
        page.goto('https://www.lambdatest.com/selenium-playground/')
        base_locator = page.get_by_role('link', name=" Context Menu")
        expect(base_locator).to_be_visible()
        page.pause()
        base_locator.scroll_into_view_if_needed()
        page.pause()
        set_test_status(page, 'Passed','Element exists')        
        page.close()
    except Exception as ex:
        set_test_status(page, 'Failed',str(ex))    
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;
&lt;p&gt;&lt;strong&gt;Code Walkthrough&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;The code for this scenario is identical to the scenario we saw before, except for the element that we are locating, which has changed to ‘Context Menu’. Let us do a walkthrough of the same.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Step 1&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fcdn-images-1.medium.com%2Fmax%2F2336%2F0%2Aq2Fk38eZq89hdKON.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fcdn-images-1.medium.com%2Fmax%2F2336%2F0%2Aq2Fk38eZq89hdKON.png"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Navigate to the test website using the &lt;em&gt;page.goto()&lt;/em&gt; method.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Step 2&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fcdn-images-1.medium.com%2Fmax%2F2364%2F0%2A0YXTqzvEACQYODfk.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fcdn-images-1.medium.com%2Fmax%2F2364%2F0%2A0YXTqzvEACQYODfk.png"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;This step changes from scenario #1 we saw above, and we look for the ‘Context Menu’ element, which is already in the viewport.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Step 3&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fcdn-images-1.medium.com%2Fmax%2F2000%2F0%2AOfO8daY6YLPOukuO.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fcdn-images-1.medium.com%2Fmax%2F2000%2F0%2AOfO8daY6YLPOukuO.png"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;We use the Playwright &lt;em&gt;expect()&lt;/em&gt; assertion to make the element ‘Context Menu’ visible. The &lt;em&gt;page.pause()&lt;/em&gt; will pause the test execution.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Step 4&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fcdn-images-1.medium.com%2Fmax%2F2000%2F0%2A1bxFukn4DazpqLsQ.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fcdn-images-1.medium.com%2Fmax%2F2000%2F0%2A1bxFukn4DazpqLsQ.png"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;On the &lt;em&gt;base_locator&lt;/em&gt;, we call the &lt;em&gt;scroll_into_view_if_needed()&lt;/em&gt; method. The &lt;em&gt;page.pause()&lt;/em&gt; is used again to pause the test execution and ensure the browser does not close.&lt;/p&gt;

&lt;p&gt;As we can see, the ‘Context Menu’ is already presented in the viewport. So, the &lt;em&gt;scroll_into_view_if_needed()&lt;/em&gt; will be ineffective.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fcdn-images-1.medium.com%2Fmax%2F3200%2F0%2Al01Wd_EsYdwFePbR.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fcdn-images-1.medium.com%2Fmax%2F3200%2F0%2Al01Wd_EsYdwFePbR.png"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Execution&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;A single test uses the below command.&lt;/p&gt;

&lt;p&gt;pytest -k test_scroll_into_view_if_needed_element_already_in_viewport&lt;/p&gt;

&lt;p&gt;As we can see, the element has been located, but scrolling was not required since the ’Context Menu’ element is present in the viewport itself.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fcdn-images-1.medium.com%2Fmax%2F3200%2F0%2A7sMURyQmC81LJVA-.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fcdn-images-1.medium.com%2Fmax%2F3200%2F0%2A7sMURyQmC81LJVA-.png"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Here are the test completion logs from the VS Code terminal.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fcdn-images-1.medium.com%2Fmax%2F2314%2F0%2ACX6AfbwZAPuh0-xt.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fcdn-images-1.medium.com%2Fmax%2F2314%2F0%2ACX6AfbwZAPuh0-xt.png"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;This marks the completion of the explanation of the &lt;em&gt;scroll_into_view_if_needed()&lt;/em&gt; method. The important takeaway for this method is that the scroll to an element in Playwright happens only if the located element is not present within the viewport. If it is already present, the method takes no action.&lt;/p&gt;

&lt;p&gt;Let us move on to the second method, which can help perform the scroll action, which is the &lt;em&gt;mouse.wheel()&lt;/em&gt;.&lt;/p&gt;
&lt;h2&gt;
  
  
  Demonstration: Using the mouse.wheel() Method
&lt;/h2&gt;

&lt;p&gt;Using the Mouse wheel to scroll to an element in Playwright in either the downward (or upward) direction is the most preferred way users navigate the web page. Playwright provides a &lt;em&gt;mouse&lt;/em&gt; class that helps users simulate the actions that can be performed using a mouse.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fcdn-images-1.medium.com%2Fmax%2F3200%2F0%2AESDC72FA6o304TYN.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fcdn-images-1.medium.com%2Fmax%2F3200%2F0%2AESDC72FA6o304TYN.png"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The &lt;em&gt;mouse&lt;/em&gt; class operates relative to the top left corner of the viewport, i.e., the top left of the web page is considered the starting point or the 0,0 position.&lt;/p&gt;

&lt;p&gt;While the mouse class API is very comprehensive and lets users perform all the actions allowed by an actual Mouse, the purpose of this blog is limited to exploring the &lt;em&gt;wheel()&lt;/em&gt; method.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Test Scenario: Using the mouse.wheel()&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://www.lambdatest.com/blog/how-to-lazy-load-images-javascript/?utm_source=devto&amp;amp;utm_medium=organic&amp;amp;utm_campaign=feb_26&amp;amp;utm_term=bw&amp;amp;utm_content=blog" rel="noopener noreferrer"&gt;Lazy-loading&lt;/a&gt; gained prominence with the emergence of the HTML5 specification and the proliferation of mobile devices.&lt;/p&gt;

&lt;p&gt;It is a web development technique that aims to improve websites’ performance and user experience by deferring the loading of certain elements, such as images, until they are needed. By implementing lazy-loading, developers can optimize page loading times, reduce bandwidth usage, and enhance a website’s overall speed and responsiveness.&lt;/p&gt;

&lt;p&gt;Consider this LambdaTest Demo eCommerce website, which uses a lazy-load tag to improve page load times by loading images outside the viewport only when the user scrolls down to them.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fcdn-images-1.medium.com%2Fmax%2F3200%2F0%2A6ifxN96jMNx4r96f.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fcdn-images-1.medium.com%2Fmax%2F3200%2F0%2A6ifxN96jMNx4r96f.png"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Now that we understand what lazy-loading is and what the benefits are, let us understand how to use the &lt;em&gt;mouse.wheel()&lt;/em&gt; method to test websites that employ lazy loading.&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;&lt;p&gt;Navigate to LambdaTest eCommerce playground.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Locate the ‘Shop By Category’ menu and click it.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Within the ‘Shop By Category’, select the ‘Software’ menu to search for software-related products.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Ensure all product images employing lazy-loading are correctly rendered after scrolling to bring them into the viewport.&lt;/p&gt;&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;&lt;strong&gt;Implementation&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;The complete test code is as below.&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;def test_scroll_using_mouse_wheel(page: Page):
    page.goto("https://ecommerce-playground.lambdatest.io/")
    page.get_by_role("button", name="Shop by Category").click()
    page.get_by_role("link", name="Software").click()
    page_to_be_scrolled = page.get_by_role("combobox", name="Show:").select_option("https://ecommerce-playground.lambdatest.io/index.php?route=product/category&amp;amp;path=17&amp;amp;limit=75")
    page.goto(page_to_be_scrolled[0])          

    # Since image are lazy-loaded scroll to bottom of page
    # the range is dynamically decided based on the number of items i.e. we take the range from limit
    for i in range(int(page_to_be_scrolled[0].split("=")[-1])):
        page.mouse.wheel(0, 100)
        i += 1
        time.sleep(0.1)
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;
&lt;p&gt;&lt;strong&gt;Code Walkthrough&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Step 1&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fcdn-images-1.medium.com%2Fmax%2F2212%2F0%2AnxUmbfKzYwoQjpfs.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fcdn-images-1.medium.com%2Fmax%2F2212%2F0%2AnxUmbfKzYwoQjpfs.png"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;We first navigate to the website under test using the &lt;em&gt;page.goto()&lt;/em&gt; method.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Step 2&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fcdn-images-1.medium.com%2Fmax%2F3200%2F0%2ABR76Pf_JZSl3GIw4.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fcdn-images-1.medium.com%2Fmax%2F3200%2F0%2ABR76Pf_JZSl3GIw4.png"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;As we can see, the ‘&lt;em&gt;Shop by Category&lt;/em&gt;’ element is a button and we use the Playwright Locator &lt;em&gt;get_by_role()&lt;/em&gt; and perform the &lt;em&gt;click()&lt;/em&gt; action on the located element.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fcdn-images-1.medium.com%2Fmax%2F2304%2F0%2Ae9e7Vvh9-60lL2M9.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fcdn-images-1.medium.com%2Fmax%2F2304%2F0%2Ae9e7Vvh9-60lL2M9.png"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Step 3&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fcdn-images-1.medium.com%2Fmax%2F3200%2F0%2AyYuM0bha76FN0eHh.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fcdn-images-1.medium.com%2Fmax%2F3200%2F0%2AyYuM0bha76FN0eHh.png"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fcdn-images-1.medium.com%2Fmax%2F2000%2F0%2AOmKe4HkaAhOZCLmD.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fcdn-images-1.medium.com%2Fmax%2F2000%2F0%2AOmKe4HkaAhOZCLmD.png"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The ‘&lt;em&gt;Software&lt;/em&gt;’ sub-menu is a link and can be accessed using the &lt;em&gt;get_by_role()&lt;/em&gt; locator using the ‘&lt;em&gt;link&lt;/em&gt;’ role &amp;amp; &lt;em&gt;name=’Software’&lt;/em&gt;.&lt;/p&gt;

&lt;p&gt;On the located element, we perform the &lt;em&gt;click()&lt;/em&gt; action.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Step 4&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fcdn-images-1.medium.com%2Fmax%2F3200%2F0%2Ax_PNt89YxBavp6nk.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fcdn-images-1.medium.com%2Fmax%2F3200%2F0%2Ax_PNt89YxBavp6nk.png"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The subsequent page that opens by default displays 15 items and is present within a &lt;em&gt;combobox&lt;/em&gt; element.&lt;/p&gt;

&lt;p&gt;We located the combobox and increased the display option to 75.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fcdn-images-1.medium.com%2Fmax%2F3200%2F0%2A3CIgRCfkIDa7qllb.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fcdn-images-1.medium.com%2Fmax%2F3200%2F0%2A3CIgRCfkIDa7qllb.png"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Step 5&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fcdn-images-1.medium.com%2Fmax%2F2304%2F0%2ARG2HHi70j6RwWLjO.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fcdn-images-1.medium.com%2Fmax%2F2304%2F0%2ARG2HHi70j6RwWLjO.png"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;em&gt;page_to_be_scrolled[0].split(“=”)[-1]&lt;/em&gt; extracts the value 75 from the page URL, and we use this value to decide the vertical distance of the scroll dynamically.&lt;/p&gt;

&lt;p&gt;The &lt;em&gt;page.mouse.wheel(0,100)&lt;/em&gt; indicates a scroll of 0 pixels horizontally and 100 pixels vertically. We can either increase or decrease this value for faster or slower scrolling.&lt;/p&gt;

&lt;p&gt;We do this for 75 iterations based on the total number of objects on the page.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Execution&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Let’s execute the single test using the following command.&lt;/p&gt;

&lt;p&gt;pytest -k “test_scroll_using_mouse_wheel”&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fcdn-images-1.medium.com%2Fmax%2F3200%2F0%2AmnqqU0VZUMjVghIs.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fcdn-images-1.medium.com%2Fmax%2F3200%2F0%2AmnqqU0VZUMjVghIs.png"&gt;&lt;/a&gt;&lt;br&gt;
&lt;iframe width="710" height="399" src="https://www.youtube.com/embed/Dc9rzkYPHJg"&gt;
&lt;/iframe&gt;
&lt;/p&gt;

&lt;p&gt;As we can see, the scrolling is seamless and loads the image media as we scroll to an element in Playwright downwards.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Experience seamless testing with our &lt;a href="https://www.lambdatest.com/android-emulator-online?utm_source=devto&amp;amp;utm_medium=organic&amp;amp;utm_campaign=feb_26&amp;amp;utm_term=bw&amp;amp;utm_content=webpage" rel="noopener noreferrer"&gt;Android device emulator online&lt;/a&gt; for perfect app launches.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2&gt;
  
  
  Demonstration: Using the keyboard.press() Method
&lt;/h2&gt;

&lt;p&gt;The Playwright keyboard class provides an API for managing a virtual keyboard. The high-level API is &lt;em&gt;keyboard.type()&lt;/em&gt;, which takes raw characters and generates proper keydown, keypress/input, and keyup events on your page.&lt;/p&gt;

&lt;p&gt;We can use the &lt;em&gt;keyboard.down()&lt;/em&gt;, &lt;em&gt;keyboard.up()&lt;/em&gt;, and &lt;em&gt;keyboard.insert_text()&lt;/em&gt; to simulate actions as if they were generated from a real keyboard by a user.&lt;/p&gt;

&lt;p&gt;For this blog, we are most interested in the keyboard.press() method that takes keys such as &lt;em&gt;PageDown, PageUp, ArrowUp, ArrowDown,&lt;/em&gt; etc, as inputs and performs the relevant action.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Test Scenario: Using the keyboard.press()&lt;/strong&gt;&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;&lt;p&gt;Navigate to the Playwright Keyboard API page.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Use the &lt;em&gt;keyboard.press()&lt;/em&gt; to scroll in the upward and downward directions on the page.&lt;/p&gt;&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fcdn-images-1.medium.com%2Fmax%2F3200%2F0%2A2CObTzm6Z3fCSkZf.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fcdn-images-1.medium.com%2Fmax%2F3200%2F0%2A2CObTzm6Z3fCSkZf.png"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;As can be seen, the keyboard API page has a few sections that extend beyond the current viewport and is a good candidate to test out the ability of the API itself to simulate scrolling.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Implementation&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Let us look at the implementation of the code first.&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;def test_scroll_using_keyboard_press(page: Page):  
    page.goto("https://playwright.dev/python/docs/api/class-keyboard")


    # delay is used to control time between keypress and key release    
    for _ in range(5):
        page.keyboard.press("PageDown", delay=1000)


    for _ in range(5):
        page.keyboard.press("PageUp", delay=1000)


    for _ in range(30):
        page.keyboard.press("ArrowDown", delay=100)

    for _ in range(30):
        page.keyboard.press("ArrowUp", delay=100)
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;&lt;strong&gt;Code Walkthrough&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Let us now break down the implementation and understand it step-by-step.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Step 1&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fcdn-images-1.medium.com%2Fmax%2F2520%2F0%2Adhn0fKZUNKaFpROM.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fcdn-images-1.medium.com%2Fmax%2F2520%2F0%2Adhn0fKZUNKaFpROM.png"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;We navigate to the Playwright keyboard API page using the &lt;em&gt;page.goto()&lt;/em&gt; method.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Step 2&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fcdn-images-1.medium.com%2Fmax%2F2580%2F0%2AJwvQZIZ801CdmrOP.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fcdn-images-1.medium.com%2Fmax%2F2580%2F0%2AJwvQZIZ801CdmrOP.png"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;On the page we call the &lt;em&gt;keyboard.press()&lt;/em&gt; and perform the ‘&lt;em&gt;PageDown&lt;/em&gt;’ action. The delay ensures we do not scroll to an element in Playwright too fast by introducing a 1000ms delay between the PageDown key press and release.&lt;/p&gt;

&lt;p&gt;The for loop repeats the &lt;em&gt;PageDown&lt;/em&gt; press five times to demo the scrolling.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Step 3&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fcdn-images-1.medium.com%2Fmax%2F2000%2F0%2AYUdy_zVkk_1L_NhP.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fcdn-images-1.medium.com%2Fmax%2F2000%2F0%2AYUdy_zVkk_1L_NhP.png"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;This step is similar to step 1, except we scroll to an element in Playwright in the upward direction using &lt;em&gt;PageUp&lt;/em&gt;.&lt;/p&gt;

&lt;p&gt;Again, the delay ensures we don’t scroll too fast and can see the scrolling in action.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Step 4&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fcdn-images-1.medium.com%2Fmax%2F2000%2F0%2AEX2fbWnJMqt8ggMe.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fcdn-images-1.medium.com%2Fmax%2F2000%2F0%2AEX2fbWnJMqt8ggMe.png"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;In the next step, we simulate the scroll to an element in Playwright in a downward direction, but this time using the down arrow key &lt;em&gt;ArrowDown&lt;/em&gt; and back in the upward direction using &lt;em&gt;ArrowUp&lt;/em&gt;.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Execution&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Now, let’s execute the single test and check if the scrolling works as expected.&lt;/p&gt;

&lt;p&gt;pytest -v “test_scroll_using_keyboard_press”&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fcdn-images-1.medium.com%2Fmax%2F3200%2F0%2AKSSyRDRPHpE8l39b.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fcdn-images-1.medium.com%2Fmax%2F3200%2F0%2AKSSyRDRPHpE8l39b.png"&gt;&lt;/a&gt;&lt;br&gt;
&lt;iframe width="710" height="399" src="https://www.youtube.com/embed/D1ekIlYwZ8s"&gt;
&lt;/iframe&gt;
&lt;/p&gt;

&lt;h2&gt;
  
  
  Implementing Horizontal Scrolling in Playwright
&lt;/h2&gt;

&lt;p&gt;From a usability perspective, horizontal scrolling is generally considered a poor design choice. But there still may be scenarios where horizontal scrolling is necessary; hence, it’s important to test scrolling in the horizontal direction.&lt;/p&gt;

&lt;p&gt;To demonstrate how we can achieve horizontal scrolling in Playwright, we will use this demo codepen website, which implements an image gallery.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fcdn-images-1.medium.com%2Fmax%2F3200%2F0%2AP9G6SgP_D5MZNkQL.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fcdn-images-1.medium.com%2Fmax%2F3200%2F0%2AP9G6SgP_D5MZNkQL.png"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Test Scenario: Implement Horizontal Scrolling&lt;/strong&gt;&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;&lt;p&gt;Navigate to the demo codepen website.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Use the &lt;em&gt;scroll_into_view_if_needed()&lt;/em&gt; method to scroll to an element in Playwright in the horizontal direction on the page.&lt;/p&gt;&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;&lt;strong&gt;Implementation&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;The complete test code is as below.&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;def test_horizontal_scroll(page: Page):
    """
    Test verifies if the contact us section is visible and scrolls to it
    """      
    page.goto('https://codepen.io/ReGGae/full/QZxdVX/')
    for _ in range(50):        
        base_locator = page.frame_locator("#result").locator(f"div:nth-child(3) &amp;gt; article:nth-child({random.choice([1,2,3,4,5])}) &amp;gt; .slide__inner &amp;gt; .slide__img")            
        base_locator.scroll_into_view_if_needed()  
        time.sleep(0.1)        
    page.close()
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;&lt;strong&gt;Code Walkthrough&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Let us now break down the implementation and understand it step by step.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Step 1&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;The demo website renders the image gallery within a &amp;lt; iframe &amp;gt; tag with the name result under a bunch of &amp;lt; article &amp;gt; tags.&lt;/p&gt;

&lt;p&gt;We can use the Playwright Codegen tool to obtain the exact locator for the image gallery. It reveals that all the images are nested under div:nth-child(3) and article numbers 1,2,3,4,5.&lt;/p&gt;

&lt;p&gt;So, the first image can be located using&lt;/p&gt;

&lt;p&gt;div:nth-child(3) &amp;gt; article:nth-child(1) &amp;gt; .slide_&lt;em&gt;inner &amp;gt; .slide&lt;/em&gt;_img"&lt;/p&gt;

&lt;p&gt;The second image can be located using&lt;/p&gt;

&lt;p&gt;div:nth-child(2) &amp;gt; article:nth-child(1) &amp;gt; .slide_&lt;em&gt;inner &amp;gt; .slide&lt;/em&gt;_img"&lt;/p&gt;

&lt;p&gt;and so on till article:nth-child(5) for the last image&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fcdn-images-1.medium.com%2Fmax%2F3200%2F0%2AuDCbLgSWbqsu4dvJ.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fcdn-images-1.medium.com%2Fmax%2F3200%2F0%2AuDCbLgSWbqsu4dvJ.png"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Step 2&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fcdn-images-1.medium.com%2Fmax%2F3200%2F0%2AzFKvetFktFl7Imxe.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fcdn-images-1.medium.com%2Fmax%2F3200%2F0%2AzFKvetFktFl7Imxe.png"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;We use a Python for loop and combine it with the &lt;em&gt;random.choice()&lt;/em&gt; method to randomly locate one of the images within the gallery. Then, we scroll to the located element.&lt;/p&gt;

&lt;p&gt;The number 50 in the for loop has been chosen randomly and only serves the purpose of scrolling 50 times. It can be any arbitrary number.&lt;/p&gt;

&lt;p&gt;The sleep is added so that we can see the scrolling in action.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Execution&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Let’s execute the single test using the following command:&lt;/p&gt;

&lt;p&gt;pytest -k “test_horizontal_scrolll”&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fcdn-images-1.medium.com%2Fmax%2F2752%2F0%2ABcnwNhKN7hqXYE4g.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fcdn-images-1.medium.com%2Fmax%2F2752%2F0%2ABcnwNhKN7hqXYE4g.png"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;iframe width="710" height="399" src="https://www.youtube.com/embed/6JpQnxBhDF4"&gt;
&lt;/iframe&gt;
&lt;/p&gt;

&lt;p&gt;Are you eager to showcase your skills as a &lt;a href="https://www.lambdatest.com/playwright?utm_source=devto&amp;amp;utm_medium=organic&amp;amp;utm_campaign=feb_26&amp;amp;utm_term=bw&amp;amp;utm_content=webpage" rel="noopener noreferrer"&gt;Playwright automation&lt;/a&gt; tester? Look no further than LambdaTest’s Playwright 101 certification program! This program is designed for developers who want to demonstrate their expertise in utilizing Playwright to comprehensively test modern web applications.&lt;/p&gt;

&lt;h2&gt;
  
  
  Conclusion
&lt;/h2&gt;

&lt;p&gt;In conclusion, scrolling stands as a fundamental bridge between users and the wealth of content that modern websites offer. It is not just a navigational tool; it’s a linchpin for user engagement, content accessibility, and, ultimately, a website’s success.&lt;/p&gt;

&lt;p&gt;Playwright, a versatile automation tool, equips testers with a powerful arsenal to handle scrolling. Methods like &lt;em&gt;scroll_into_view_if_needed()&lt;/em&gt;, mouse.wheel(), and &lt;em&gt;keyboard.press()&lt;/em&gt; enables testers to mimic user scrolling actions accurately.&lt;/p&gt;

&lt;p&gt;Whether verifying lazy-loading functionality or assessing the rendering of off-screen elements, Playwright empowers testers to conduct comprehensive and reliable automation testing of scrolling, ensuring that web applications perform optimally and deliver exceptional user experiences.&lt;/p&gt;

&lt;p&gt;In an era where scrolling plays a pivotal role in user interaction, Playwright is an invaluable ally in the pursuit of flawless web development and testing.&lt;/p&gt;

</description>
      <category>playwright</category>
      <category>testing</category>
      <category>automation</category>
      <category>programming</category>
    </item>
    <item>
      <title>How to Handle iFrames in Playwright</title>
      <dc:creator>Jaydeep Karale</dc:creator>
      <pubDate>Mon, 22 Jan 2024 13:14:50 +0000</pubDate>
      <link>https://dev.to/_jaydeepkarale/how-to-handle-iframes-in-playwright-1m2b</link>
      <guid>https://dev.to/_jaydeepkarale/how-to-handle-iframes-in-playwright-1m2b</guid>
      <description>&lt;p&gt;OVERVIEW&lt;/p&gt;

&lt;p&gt;An &lt;em&gt;&amp;lt; iframe &amp;gt;&lt;/em&gt; (short for inline frame) is an HTML element that allows you to embed another HTML document within the current page. Introduced in 1998, an iframe is one of the oldest HTML tags. They are commonly used to display external content such as ads, maps, or videos or to create layered user interfaces by loading multiple web pages within a single page.&lt;/p&gt;

&lt;p&gt;Modern web development libraries and frameworks like Angular and ReactJS also support iframes. Angular’s built-in security features, such as &lt;em&gt;DomSanitizer&lt;/em&gt;, can be used to ensure that content loaded within an iframe is safe and secure.&lt;/p&gt;

&lt;p&gt;In addition, to support the native HTML iframe tag, ReactJS also provides a built-in &lt;em&gt;Reactiframe&lt;/em&gt; component, which is a wrapper around the HTML &lt;em&gt;&lt;/em&gt; tag.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;&lt;/em&gt; offer several benefits, such as the ability to embed external content like YouTube videos and tweets. Iframes can also be used to isolate content as the JavaScript and CSS of the parent page do not apply to iframe. Another excellent use case for iframes is for displaying advertisements.&lt;/p&gt;

&lt;p&gt;With so many use cases for iframes, they are used in websites, and it remains important to be able to test web pages that contain iframes reliably. Not only simple single iframes but web pages that have nested iframes as well.&lt;/p&gt;

&lt;p&gt;In this tutorial, we’ll explore how to handle iframes in &lt;a href="https://www.lambdatest.com/playwright?utm_source=devto&amp;amp;utm_medium=organic&amp;amp;utm_campaign=jan_04&amp;amp;utm_term=bw&amp;amp;utm_content=webpage" rel="noopener noreferrer"&gt;Playwright&lt;/a&gt;, a popular browser automation and end-to-end web testing library. You will learn how to locate an iframe using &lt;a href="https://www.lambdatest.com/blog/playwright-locators/?utm_source=devto&amp;amp;utm_medium=organic&amp;amp;utm_campaign=jan_04&amp;amp;utm_term=bw&amp;amp;utm_content=blog" rel="noopener noreferrer"&gt;Playwright locators&lt;/a&gt; and how Playwright can handle nested iframes.&lt;/p&gt;

&lt;p&gt;In addition to running all the tests to handle iframes in Playwright locally, you will also see how they can be run on a Playwright cloud grid provider.&lt;/p&gt;

&lt;h2&gt;
  
  
  What is Playwright?
&lt;/h2&gt;

&lt;p&gt;Playwright is an open-source browser automation and &lt;a href="https://www.lambdatest.com/learning-hub/end-to-end-testing?utm_source=devto&amp;amp;utm_medium=organic&amp;amp;utm_campaign=jan_04&amp;amp;utm_term=bw&amp;amp;utm_content=learning_hub" rel="noopener noreferrer"&gt;end-to-end testing&lt;/a&gt; framework released by Microsoft in 2020. Playwright API is available for programming languages such as JavaScript, TypeScript, Python, Java, and .Net.&lt;/p&gt;

&lt;p&gt;It also supports all modern browsers, such as Chrome, Firefox, Microsoft Edge, and Safari, making &lt;a href="https://www.lambdatest.com/?utm_source=devto&amp;amp;utm_medium=organic&amp;amp;utm_campaign=jan_04&amp;amp;utm_term=bw&amp;amp;utm_content=webpage" rel="noopener noreferrer"&gt;cross browser testing&lt;/a&gt; feasible. In addition to being cross-browser, Playwright is cross-platform, which means that tests can be run on Windows, macOS, and Linux either in headless or headed mode.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Calculate SHA1 hashes easily with our &lt;a href="https://www.lambdatest.com/free-online-tools/sha1-hash-calculator?utm_source=devto&amp;amp;utm_medium=organic&amp;amp;utm_campaign=jan_04&amp;amp;utm_term=bw&amp;amp;utm_content=free_online_tools" rel="noopener noreferrer"&gt;SHA1 hash calculator&lt;/a&gt; tool&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2&gt;
  
  
  Some key features of Playwright
&lt;/h2&gt;

&lt;p&gt;Playwright provides several key features that set it apart from other &lt;a href="https://www.lambdatest.com/blog/automation-testing-frameworks/?utm_source=devto&amp;amp;utm_medium=organic&amp;amp;utm_campaign=jan_04&amp;amp;utm_term=bw&amp;amp;utm_content=blog" rel="noopener noreferrer"&gt;automation testing frameworks&lt;/a&gt;. Here are some of the key features of Playwright:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Browser Context&lt;/strong&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Playwright creates a browser context for each test. Browser context is equivalent to a brand-new browser profile. This delivers full test isolation with zero overhead. Creating a new browser context only takes a handful of milliseconds.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Log in once&lt;/strong&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Playwright helps save the authentication state of the context and reuse it in all the tests. This bypasses repetitive log-in operations in each test yet delivers full isolation of independent tests.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Powerful Tooling&lt;/strong&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Provides tools such as codegen, which generates tests by recording actions. These tests can be generated in any language. Traceviewer helps capture all test execution information to investigate and debug test failures.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Auto-Wait &amp;amp; Retry&lt;/strong&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Playwright waits for elements to be actionable before performing actions. It also has a rich set of introspection events. Combining these two eliminates the need for setting artificial timeouts and reduces the flakiness of tests.&lt;/p&gt;

&lt;p&gt;All these combined features make Playwright a comprehensive, feature-packed browser automation and end-to-end testing framework.&lt;/p&gt;

&lt;h2&gt;
  
  
  What are iFrames and how to use them?
&lt;/h2&gt;

&lt;p&gt;Iframes provide a convenient way to embed content from other websites on our website. An iframe can be created using the HTML &lt;em&gt;&lt;/em&gt; tag in the body of the page. Let’s see how to embed a YouTube video as an iframe using CodePen.&lt;/p&gt;

&lt;p&gt;YouTube provides the code to embed videos into iframe directly.&lt;/p&gt;

&lt;p&gt;Head over to the YouTube page of the video you wish to embed into your web page as an iframe and click the Share button.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--6UNCwxn5--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://cdn-images-1.medium.com/max/3200/1%2ANW-yrlih1sWKKYpMZrGUrQ.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--6UNCwxn5--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://cdn-images-1.medium.com/max/3200/1%2ANW-yrlih1sWKKYpMZrGUrQ.png" width="800" height="479"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;On the menu that appears, click the “Embed” button.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--WQ0P5gC7--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://cdn-images-1.medium.com/max/2000/1%2AcLB7lq4Qh_yihbQPaBQOAw.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--WQ0P5gC7--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://cdn-images-1.medium.com/max/2000/1%2AcLB7lq4Qh_yihbQPaBQOAw.png" width="601" height="410"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The &lt;em&gt;&lt;/em&gt; code snippet is available to copy &amp;amp; paste into your webpage. Like most websites that allow embedding, YouTube also allows customization of the embedding link, and based on the selection, the link to embed the video will be altered.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--EIBt2z4I--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://cdn-images-1.medium.com/max/2604/1%2AkHUVe5RZ7bbbiFqRt-__4A.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--EIBt2z4I--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://cdn-images-1.medium.com/max/2604/1%2AkHUVe5RZ7bbbiFqRt-__4A.png" width="800" height="313"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;For example, if you click the ‘Start at’ checkbox and give a custom time, the “src” in the &lt;em&gt;&lt;/em&gt; tag will be amended with the start time.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--fQleILqn--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://cdn-images-1.medium.com/max/2566/1%2AwOi0J4Q0ZYCGjP7RYJCqfQ.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--fQleILqn--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://cdn-images-1.medium.com/max/2566/1%2AwOi0J4Q0ZYCGjP7RYJCqfQ.png" width="800" height="301"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Copy and paste the link generated by YouTube into the CodePen, and the resulting webpage will look like the one below.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--pJNO4Fe9--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://cdn-images-1.medium.com/max/3200/1%2AkZiMTVplSeahGr0pLrrifw.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--pJNO4Fe9--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://cdn-images-1.medium.com/max/3200/1%2AkZiMTVplSeahGr0pLrrifw.png" width="800" height="412"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;This is only a high-level overview of the &lt;em&gt;&lt;/em&gt; tag, and a lot more customization is available when using it, but it remains out of the scope of this tutorial to handle iframes in Playwright as we will concentrate on handling web pages that contain iframes with Playwright.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Need to calculate SHA256 hashes? Try our &lt;a href="https://www.lambdatest.com/free-online-tools/sha256-hash-calculator?utm_source=devto&amp;amp;utm_medium=organic&amp;amp;utm_campaign=jan_04&amp;amp;utm_term=bw&amp;amp;utm_content=free_online_tools" rel="noopener noreferrer"&gt;SHA256 hash calculator&lt;/a&gt; now!&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2&gt;
  
  
  What is the difference between Frames and iFrames?
&lt;/h2&gt;

&lt;p&gt;The &lt;em&gt;&lt;/em&gt; and &lt;em&gt;&lt;/em&gt; tags represent a way to divide a web page into parts. The major difference between the two is that the frame tag is no longer supported in HTML 5, and iframe is the preferred way to embed content from one website to another.&lt;/p&gt;

&lt;p&gt;When you use the &lt;em&gt;&lt;/em&gt; tag, then the content of a web page consists of frames that are created by using &lt;em&gt;&lt;/em&gt; and &lt;em&gt;&lt;/em&gt; tags only (and &lt;em&gt;&lt;/em&gt; tag is not used).&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;&amp;lt;html&amp;gt;
&amp;lt;head&amp;gt;
&amp;lt;title&amp;gt;HTML Frames&amp;lt;/title&amp;gt;
&amp;lt;/head&amp;gt;
&amp;lt;frameset rows="20%,70%,10%"&amp;gt;
   &amp;lt;frame name="top" src="/html/top.html" /&amp;gt;
   &amp;lt;frame name="main" src="/html/main.html" /&amp;gt;
   &amp;lt;frame name="bottom" src="/html/bottom.html" /&amp;gt;
&amp;lt;/frameset&amp;gt;
&amp;lt;/html&amp;gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;And when you use &lt;em&gt;&lt;/em&gt;, then the content of web page doesn’t contain frames and content of web page is created by using &lt;em&gt;&lt;/em&gt; tag (and &lt;em&gt;&lt;/em&gt; and &lt;em&gt;&lt;/em&gt; tags are not used) as shown below:&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;&amp;lt;html&amp;gt;
&amp;lt;head&amp;gt;
&amp;lt;title&amp;gt;HTML Iframes&amp;lt;/title&amp;gt;
&amp;lt;/head&amp;gt;
&amp;lt;body&amp;gt;
&amp;lt;p&amp;gt;See the video&amp;lt;/p&amp;gt;
 &amp;lt;iframe width="854" height="480" src="https://www.youtube.com/embed/2eabXBvw4oI"
    frameborder="0"    allowfullscreen&amp;gt;
  &amp;lt;/iframe&amp;gt;
&amp;lt;/body&amp;gt;
&amp;lt;/html&amp;gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;So &lt;em&gt;&lt;/em&gt; just brings some other source’s document to a web page. Frameset tags were used to create frames with the tag frame, whereas iframe fulfills the functions of both frame and frameset tags. Unlike frame tags, iframe tags can also be placed inside the body tags.&lt;/p&gt;

&lt;p&gt;Placement of &lt;em&gt;&lt;/em&gt; is easy, as a developer can easily put the iframe tag among the other webpage tags and add several iframe tags. On the other hand, placing frame tags in a frameset can be a bit more challenging.&lt;br&gt;
&lt;iframe width="710" height="399" src="https://www.youtube.com/embed/JN16nilL8Wg"&gt;
&lt;/iframe&gt;
&lt;br&gt;
You can also Subscribe to the LambdaTest YouTube Channel and stay updated with the latest tutorials around &lt;a href="https://www.lambdatest.com/selenium-automation?utm_source=devto&amp;amp;utm_medium=organic&amp;amp;utm_campaign=jan_04&amp;amp;utm_term=bw&amp;amp;utm_content=webpage" rel="noopener noreferrer"&gt;Selenium testing&lt;/a&gt;, &lt;a href="https://www.lambdatest.com/blog/playwright-framework?utm_source=devto&amp;amp;utm_medium=organic&amp;amp;utm_campaign=jan_04&amp;amp;utm_term=bw&amp;amp;utm_content=blog" rel="noopener noreferrer"&gt;Playwright browser testing&lt;/a&gt;, CI/CD, and more.&lt;/p&gt;

&lt;h2&gt;
  
  
  Playwright Frame and FrameLocator
&lt;/h2&gt;

&lt;p&gt;Playwright’s Page class API provides two methods to &lt;a href="https://www.lambdatest.com/learning-hub/handling-frames-and-windows-in-playwright?utm_source=devto&amp;amp;utm_medium=organic&amp;amp;utm_campaign=jan_04&amp;amp;utm_term=bw&amp;amp;utm_content=learning_hub" rel="noopener noreferrer"&gt;handle frames in Playwright&lt;/a&gt;. These are &lt;em&gt;page.frame() and page.frame_locator()&lt;/em&gt;.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;page.frame()&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Returns a frame matching the specified criteria.&lt;/p&gt;

&lt;p&gt;To locate the frame using the &lt;em&gt;frame()&lt;/em&gt; method, the name or URL of the frame must be supplied to this method. In the example below, you can locate the CodePen &lt;em&gt;&lt;/em&gt; name or the page URL.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--vkxFm33O--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://cdn-images-1.medium.com/max/3200/1%2AYgRiwedudCPI0S-cAoIg8A.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--vkxFm33O--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://cdn-images-1.medium.com/max/3200/1%2AYgRiwedudCPI0S-cAoIg8A.png" width="800" height="416"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--PebO_nNm--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://cdn-images-1.medium.com/max/2736/1%2AQ-rmuOmQYCV5InG1uog8fg.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--PebO_nNm--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://cdn-images-1.medium.com/max/2736/1%2AQ-rmuOmQYCV5InG1uog8fg.png" width="800" height="215"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;page.frame_locator()&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;When working with iframes in Playwright, you can create a frame locator that will enter the iframe and allow selecting of elements in that iframe.&lt;/p&gt;

&lt;p&gt;The below code snippet will select the frame with id=’iframe-window’ and search for the text element ‘LAMBDATEST BLOG’.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--6Oi_Qgol--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://cdn-images-1.medium.com/max/3200/1%2AsQiMeU5TjH9SEAz7WxssAg.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--6Oi_Qgol--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://cdn-images-1.medium.com/max/3200/1%2AsQiMeU5TjH9SEAz7WxssAg.png" width="800" height="416"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--K0jxRH7u--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://cdn-images-1.medium.com/max/2548/1%2AqG588PcoU9h7ngzrp1RJAA.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--K0jxRH7u--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://cdn-images-1.medium.com/max/2548/1%2AqG588PcoU9h7ngzrp1RJAA.png" width="800" height="207"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;🛎️The &lt;em&gt;frame and frame_locator()&lt;/em&gt; methods are available in all language APIs, but their naming convention might differ slightly. For example, in TypeScript API, &lt;em&gt;frame_locator()&lt;/em&gt; is called &lt;em&gt;frameLocator()&lt;/em&gt; by the language naming convention. But the functionality and use case remains the same.&lt;/p&gt;

&lt;p&gt;Now that you have learned the nuances of frames and iframes in Playwright let’s dive deep into handling iframes in Playwright using Python and TypeScript. The demo will consist of four tests, two using the Python API of Playwright and two using the TypeScript API of Playwright.&lt;/p&gt;

&lt;p&gt;You will also see how to run the tests on the Playwright cloud grid provided by LambdaTest.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Looking for a quick &lt;a href="https://www.lambdatest.com/free-online-tools/difference-checker?utm_source=devto&amp;amp;utm_medium=organic&amp;amp;utm_campaign=jan_04&amp;amp;utm_term=bw&amp;amp;utm_content=free_online_tools" rel="noopener noreferrer"&gt;difference checker&lt;/a&gt;? Check out our tool!&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2&gt;
  
  
  How to set up Playwright with Python?
&lt;/h2&gt;

&lt;p&gt;Before we begin, let us look at how to set up &lt;a href="https://www.lambdatest.com/blog/playwright-python-tutorial/?utm_source=devto&amp;amp;utm_medium=organic&amp;amp;utm_campaign=jan_04&amp;amp;utm_term=bw&amp;amp;utm_content=blog" rel="noopener noreferrer"&gt;Playwright Python&lt;/a&gt;. The first thing you need for any Python project is a dedicated virtual environment to isolate the required dependencies. For this project, let’s create and activate an environment named &lt;em&gt;playwrightiframes&lt;/em&gt;.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--hspAshlY--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://cdn-images-1.medium.com/max/2000/1%2AYc6mKJxnsTFj3J3b8dxNGw.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--hspAshlY--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://cdn-images-1.medium.com/max/2000/1%2AYc6mKJxnsTFj3J3b8dxNGw.png" width="800" height="196"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Next, let’s install Playwright. The packages that need to be installed are shown below and available in the requirements.txt file on the GitHub repo of this code.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--d-rQkixc--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://cdn-images-1.medium.com/max/2000/1%2AVTddthS2Yyy-zH0ybXKVig.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--d-rQkixc--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://cdn-images-1.medium.com/max/2000/1%2AVTddthS2Yyy-zH0ybXKVig.png" width="800" height="277"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;And then download all browsers. You can also download specific browsers like Chromium or Firefox instead of downloading them. This step is unnecessary when using a cloud grid like LambdaTest, as LambdaTest will provide the browsers.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--hfs3LeL2--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://cdn-images-1.medium.com/max/2550/1%2AJoJ3fVFoQT8M9tF0H-oOrw.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--hfs3LeL2--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://cdn-images-1.medium.com/max/2550/1%2AJoJ3fVFoQT8M9tF0H-oOrw.png" width="800" height="133"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;All the test scenarios used for the demo in this tutorial on handling iframes in Playwright are compatible with Playwright version 1.33, pytest version 7.3.1, and Python version 3.10.1.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--sjGFQTqB--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://cdn-images-1.medium.com/max/2000/1%2AHherZm7QCQGEbiTI4O_eVA.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--sjGFQTqB--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://cdn-images-1.medium.com/max/2000/1%2AHherZm7QCQGEbiTI4O_eVA.png" width="783" height="218"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Before moving to the testing scenario, let’s understand the project structure and some important configuration files necessary to run the tests. The project structure looks as below.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--aTsta36k--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://cdn-images-1.medium.com/max/2000/1%2A5Uxb49yQsY8v0qDLJLnSKA.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--aTsta36k--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://cdn-images-1.medium.com/max/2000/1%2A5Uxb49yQsY8v0qDLJLnSKA.png" width="199" height="339"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Let us now look at the content of each file.&lt;/p&gt;

&lt;p&gt;The first file you can see is &lt;em&gt;secrets.env&lt;/em&gt;, which is used to store the environment variables that hold the username and API key to access the cloud grid.&lt;/p&gt;

&lt;p&gt;The variable &lt;em&gt;RUN_ON&lt;/em&gt; controls whether the tests run locally or on a cloud grid.&lt;/p&gt;

&lt;p&gt;The variables &lt;em&gt;BROWSER&lt;/em&gt; and &lt;em&gt;PLATFORM&lt;/em&gt; control the web browser and operating system used when running tests on the cloud grid.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--ikRdIiNL--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://cdn-images-1.medium.com/max/2000/1%2Av6ZJ_iuRKdWnedwkw5m6Ew.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--ikRdIiNL--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://cdn-images-1.medium.com/max/2000/1%2Av6ZJ_iuRKdWnedwkw5m6Ew.png" width="614" height="562"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Here is what the complete &lt;em&gt;utilities.py&lt;/em&gt; file looks like.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;FileName — utitlies.py&lt;/em&gt;&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;from playwright.sync_api import Page
from dotenv import load_dotenv
import os

load_dotenv('secrets.env')
platform = os.getenv('PLATFORM')
browser = os.getenv('BROWSER')
run_on = os.getenv("RUN_ON") or 'local'

def set_test_status(page: Page, status: str, remark: str):
    """
    Function to set stats, title and description of test run on cloud grid
    :param page: instance of Playwright browser page
    :param status: status of test e.g Pass or Failed
    :param remark: additional remark about test status
    """
    page.evaluate(
        "_ =&amp;gt; {}",
        "lambdatest_action: {\"action\":\"setTestStatus\", \"\": \status\":\"" + status + "\", \"remark\": \"" + remark + "\"}}"
    )  
capabilities = {
    'browserName': browser,
    'browserVersion': 'latest',
    'LT:Options': {
        'platform': platform,
        'build': 'Testing iFrames using Playwright',
        'name': f'Testing iFrames For {platform} &amp;amp; {browser}',
        'user': os.getenv('LT_USERNAME'),
        'accessKey': os.getenv('LT_ACCESS_KEY'),
        'network': True,
        'video': True,
        'visual': True,
        'console': True,
        'tunnel': False,   # Add tunnel configuration if testing locally hosted webpage
        'tunnelName': '',  # Optional
        'geoLocation': '', # country code can be fetched from https://www.lambdatest.com/capabilities-generator/
    }
} 
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;The &lt;em&gt;utilities.py&lt;/em&gt; file is used to store reusable functions. The first line is to import the Page class from the &lt;em&gt;playwright.sync_api&lt;/em&gt;. The &lt;em&gt;page&lt;/em&gt; class provides methods to interact with the single tab within the browser.&lt;/p&gt;

&lt;p&gt;The &lt;em&gt;run_on&lt;/em&gt; variable determines if the tests run on a cloud grid or locally. It has been defaulted to run tests locally.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--XNTTKC3C--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://cdn-images-1.medium.com/max/2000/1%2AHiyDbNgZ0yO-3yVrZ0E2fA.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--XNTTKC3C--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://cdn-images-1.medium.com/max/2000/1%2AHiyDbNgZ0yO-3yVrZ0E2fA.png" width="800" height="565"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;For reading the environment variables, you can use the Python library &lt;em&gt;load_dotenv()&lt;/em&gt; and supply to it the ‘&lt;em&gt;secrets.env&lt;/em&gt;’ file, which holds all the environment variables.&lt;/p&gt;

&lt;p&gt;The &lt;em&gt;set_test_status()&lt;/em&gt; function is used to set the final status of the test when running on the cloud grid. It makes use of the &lt;em&gt;evaluate()&lt;/em&gt; method of the &lt;em&gt;Page&lt;/em&gt; class.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--9DNZz8Hy--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://cdn-images-1.medium.com/max/3200/1%2AjS34N0GInFXGoUj45A3EjQ.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--9DNZz8Hy--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://cdn-images-1.medium.com/max/3200/1%2AjS34N0GInFXGoUj45A3EjQ.png" width="800" height="237"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The next reusable bit is the &lt;em&gt;capabilities&lt;/em&gt; dictionary, which configures the behavior of the cloud grid such as browser, operating system, name of the build, whether video of &lt;a href="https://www.lambdatest.com/learning-hub/test-execution?utm_source=devto&amp;amp;utm_medium=organic&amp;amp;utm_campaign=jan_04&amp;amp;utm_term=bw&amp;amp;utm_content=learning_hub" rel="noopener noreferrer"&gt;test execution&lt;/a&gt; will be completed, and screenshots will be captured.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--VOhmwgEW--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://cdn-images-1.medium.com/max/3200/1%2Auf_t6Z83IuQmAxwRV1rY9Q.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--VOhmwgEW--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://cdn-images-1.medium.com/max/3200/1%2Auf_t6Z83IuQmAxwRV1rY9Q.png" width="800" height="397"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The &lt;em&gt;conftest.py&lt;/em&gt; is a special pytest file that contains fixtures that can be globally reused in any pytest file. &lt;a href="https://www.lambdatest.com/blog/end-to-end-tutorial-for-pytest-fixtures-with-examples/?utm_source=devto&amp;amp;utm_medium=organic&amp;amp;utm_campaign=jan_04&amp;amp;utm_term=bw&amp;amp;utm_content=blog" rel="noopener noreferrer"&gt;pytest fixtures&lt;/a&gt; can be used to write setup and tear down code necessary to run tests.&lt;/p&gt;

&lt;p&gt;We have written two fixtures; the first is named &lt;em&gt;playwright_browser()&lt;/em&gt;. It initializes a web browser instance to run locally or establishes a connection to the cloud grid depending on the &lt;em&gt;run_on&lt;/em&gt; variable configured in the &lt;em&gt;secrets.env&lt;/em&gt;.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--PSa6HLPq--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://cdn-images-1.medium.com/max/3200/1%2Am2vMyTXxpU7nzZ_5cDpMoA.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--PSa6HLPq--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://cdn-images-1.medium.com/max/3200/1%2Am2vMyTXxpU7nzZ_5cDpMoA.png" width="800" height="423"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The second fixture is named &lt;em&gt;page&lt;/em&gt;, and it uses the browser fixture to initialize a web page within the browser to run tests.&lt;/p&gt;

&lt;p&gt;By default, the fixture gets the same name as the function’s name, but you can give a different name to the fixture in &lt;em&gt;@pytest.fixture(name=)&lt;/em&gt;.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--P3X-Fk-0--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://cdn-images-1.medium.com/max/2056/1%2AVFSGnr48ccQN2aViNrRU7w.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--P3X-Fk-0--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://cdn-images-1.medium.com/max/2056/1%2AVFSGnr48ccQN2aViNrRU7w.png" width="800" height="375"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Here is what the complete &lt;em&gt;conftest.py&lt;/em&gt; file looks like.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;FileName — conftest.py&lt;/em&gt;&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;import pytest
from playwright.sync_api import sync_playwright
from utilities.utilities import capabilities, run_on
import subprocess
import urllib
import json

@pytest.fixture(name="browser")
def playwright_browser():    
    if run_on == 'local':
        with sync_playwright() as playwright:
            browser = playwright.chromium.launch(headless=False)
            yield browser

    if run_on == 'cloud':
        with sync_playwright() as playwright:
            playwrightVersion = str(subprocess.getoutput('playwright --version')).strip().split(" ")[1]
            capabilities['LT:Options']['playwrightClientVersion'] = playwrightVersion        
            lt_cdp_url = 'wss://cdp.lambdatest.com/playwright?capabilities=' + urllib.parse.quote(json.dumps(capabilities))
            browser = playwright.chromium.connect(lt_cdp_url)
            yield browser  

@pytest.fixture(name="page")
def page(browser):
    page = browser.new_context().new_page()        
    yield page
    browser.close()
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;blockquote&gt;
&lt;p&gt;Want to add some fun to your text? Try our &lt;a href="https://www.lambdatest.com/free-online-tools/shuffle-letters?utm_source=devto&amp;amp;utm_medium=organic&amp;amp;utm_campaign=jan_04&amp;amp;utm_term=bw&amp;amp;utm_content=free_online_tools" rel="noopener noreferrer"&gt;shuffle letters&lt;/a&gt; tool!&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2&gt;
  
  
  Test Scenario: How to handle nested iFrames in Playwright?
&lt;/h2&gt;

&lt;p&gt;In this scenario, we will check in detail how to handle nested iframes in Playwright.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--rs-vzM8H--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://cdn-images-1.medium.com/max/2000/1%2ATvTa_u4y7eaF7oJXKACxVQ.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--rs-vzM8H--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://cdn-images-1.medium.com/max/2000/1%2ATvTa_u4y7eaF7oJXKACxVQ.png" width="800" height="266"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Implementation&lt;/p&gt;

&lt;p&gt;The complete test code is as below.&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;from playwright.sync_api import expect, Keyboard
from utilities.utilities import set_test_status
import re

def test_nested_iframe_can_be_loaded_correctly(page):
    """
    Test nested iframes can be rendered correctly with the lambdatest blog website.
    Search for blog 'How To Use Playwright For Web Scraping with Python' by author Jaydeep Karale.
    Verify that the blog How To Use Playwright For Web Scraping with Python exists
    Also verify that author profile contains correct link
    """    
    try:
        page.goto('https://codepen.io/jaydeepkarale/pen/dygvXbm')
        base_frame_locator = page.frame_locator("iframe[name=\"CodePen\"]").frame_locator("#frame1")
        base_frame_locator.get_by_placeholder("Enter a url").fill("https://www.lambdatest.com/blog")
        base_frame_locator.get_by_role("button", name="Render iframe").click()
        base_frame_locator.frame_locator('#iframe-window').get_by_placeholder("Search …").fill('How To Use Playwright For Web Scraping with Python')
        page.keyboard.press('Enter')    
        blog_link_locator = base_frame_locator.frame_locator('#iframe-window').get_by_role('link', name='How To Use Playwright For Web Scraping with Python').first
        blog_author_locator = base_frame_locator.frame_locator('#iframe-window').get_by_role('link', name='Jaydeep Karale').first
        expect(blog_link_locator).to_have_attribute('href',re.compile('/blog/playwright-for-web-scraping/'))
        expect(blog_author_locator).to_have_attribute('href','https://www.lambdatest.com/blog/author/jaydkarale/')
        set_test_status(page, 'Passed','Blog exists')
        page.pause()
        page.close()
    except Exception as ex:
        set_test_status(page, 'Failed',str(ex))
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;&lt;strong&gt;Code Walkthrough&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Let us now go through the test steps step-by-step.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Step 1&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--5BAUEI3D--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://cdn-images-1.medium.com/max/3200/1%2ARG1jsevEzHNbC2-impjV-g.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--5BAUEI3D--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://cdn-images-1.medium.com/max/3200/1%2ARG1jsevEzHNbC2-impjV-g.png" width="800" height="406"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--luvBGMqH--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://cdn-images-1.medium.com/max/2240/1%2ACeoAUCapvQ9taS4OgNmWaA.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--luvBGMqH--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://cdn-images-1.medium.com/max/2240/1%2ACeoAUCapvQ9taS4OgNmWaA.png" width="800" height="236"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;First, navigate to the custom-developed CodePen using the &lt;em&gt;goto()&lt;/em&gt; method available as part of the page API.&lt;/p&gt;

&lt;p&gt;The CodePen contains two iframes; one iframe renders &lt;em&gt;iframetester.com&lt;/em&gt; the second iframe renders the Playwright Python homepage.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Step 2&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--UBcdnhKk--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://cdn-images-1.medium.com/max/3200/1%2ArjMXtK27KbJ9Dtza5Fp9PQ.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--UBcdnhKk--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://cdn-images-1.medium.com/max/3200/1%2ArjMXtK27KbJ9Dtza5Fp9PQ.png" width="800" height="418"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--XGayEiIB--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://cdn-images-1.medium.com/max/3200/1%2AqQif8Zxo9ahrg9iRWyVRlg.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--XGayEiIB--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://cdn-images-1.medium.com/max/3200/1%2AqQif8Zxo9ahrg9iRWyVRlg.png" width="800" height="159"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Next, use the &lt;em&gt;frame_locator()&lt;/em&gt; method and supply the id of the frame, in this case, frame1, to form the base locator. All the subsequent elements will be located using this base locator as a reference.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Step 3&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;In this step, use the &lt;em&gt;base_frame_locator&lt;/em&gt; to locate the input field on iframetester using the Playwright locator &lt;em&gt;get_by_placeholder()&lt;/em&gt; since the field has a placeholder text ‘Enter a url’ assigned to it.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--KA_BFjdW--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://cdn-images-1.medium.com/max/3200/1%2AvoRxLcM33Qqc77MftzfhRw.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--KA_BFjdW--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://cdn-images-1.medium.com/max/3200/1%2AvoRxLcM33Qqc77MftzfhRw.png" width="800" height="418"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Fill in the URL of &lt;a href="https://www.lambdatest.com/blog," rel="noopener noreferrer"&gt;&lt;em&gt;https://www.lambdatest.com/blog&lt;/em&gt;,&lt;/a&gt; which will redirect to the blog page of LambdaTest. After filling in the URL, you can use the &lt;em&gt;get_by_role() *locator to find the ‘Render iframe’ button and perform a *click()&lt;/em&gt; action on it.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--vhMVwJPJ--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://cdn-images-1.medium.com/max/3200/1%2AzMA7F1-LIPFbDw0mY_Adaw.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--vhMVwJPJ--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://cdn-images-1.medium.com/max/3200/1%2AzMA7F1-LIPFbDw0mY_Adaw.png" width="800" height="196"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The rendered website is shown below.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--XDXPLx-O--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://cdn-images-1.medium.com/max/3200/1%2AnaMIVPEpZ87-MC4hO_-8Vw.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--XDXPLx-O--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://cdn-images-1.medium.com/max/3200/1%2AnaMIVPEpZ87-MC4hO_-8Vw.png" width="800" height="404"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Step 4&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--ga_ya9HW--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://cdn-images-1.medium.com/max/2500/1%2A8XOuCq8t9XNnPSbxvoZ2HQ.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--ga_ya9HW--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://cdn-images-1.medium.com/max/2500/1%2A8XOuCq8t9XNnPSbxvoZ2HQ.png" width="800" height="413"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;On inspection of the CodePen once more, you can see that the iframe rendered by the iframetester website has an &lt;em&gt;id=’iframe-window’&lt;/em&gt;. You can leverage this fact to interact with elements within the iframe.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--CC1c1tQe--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://cdn-images-1.medium.com/max/3200/1%2Aq1XiytOKf5rwMxGoNKDokQ.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--CC1c1tQe--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://cdn-images-1.medium.com/max/3200/1%2Aq1XiytOKf5rwMxGoNKDokQ.png" width="800" height="123"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--VAcSEB9M--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://cdn-images-1.medium.com/max/3200/1%2Amhlv4Z7OfUp4Ltr5i6jt2A.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--VAcSEB9M--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://cdn-images-1.medium.com/max/3200/1%2Amhlv4Z7OfUp4Ltr5i6jt2A.png" width="800" height="417"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The element you will locate is the ‘Search’ input field. It has a placeholder text ‘Search …’ which can be used by the Playwright locator &lt;em&gt;get_by_placeholder()&lt;/em&gt;, and in the located element, search for the blog ‘How To Use Playwright For Web Scraping with Python’.&lt;/p&gt;

&lt;p&gt;The &lt;em&gt;page.keyboard.press(‘Enter’)&lt;/em&gt; will simulate pressing enter on the web page.&lt;/p&gt;

&lt;p&gt;The search results appear and look like the image shown below.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--MpnIJ9Gy--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://cdn-images-1.medium.com/max/3060/1%2Ah9nV5PWpSIYDWWQz3KuhmA.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--MpnIJ9Gy--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://cdn-images-1.medium.com/max/3060/1%2Ah9nV5PWpSIYDWWQz3KuhmA.png" width="800" height="366"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Step 5&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Next, use &lt;em&gt;base_frame_locator&lt;/em&gt; to look for the nested &lt;em&gt; by id=’iframe-window’.&lt;/em&gt; Within the nested iframe, use the &lt;em&gt;get_by_role()&lt;/em&gt; Playwright locator to look for a link to the blog under test.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--PbcAk1at--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://cdn-images-1.medium.com/max/3200/1%2AXXKji2U9ZrdDQFsVA5Y47A.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--PbcAk1at--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://cdn-images-1.medium.com/max/3200/1%2AXXKji2U9ZrdDQFsVA5Y47A.png" width="800" height="418"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--ayQ275nX--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://cdn-images-1.medium.com/max/3200/1%2A1mh_i_uvQ41wNm1D7tcIEQ.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--ayQ275nX--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://cdn-images-1.medium.com/max/3200/1%2A1mh_i_uvQ41wNm1D7tcIEQ.png" width="800" height="101"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Since the link &lt;a href="%E2%80%9D[https://www.lambdatest.com/blog/playwright-for-web-scraping/](https://www.lambdatest.com/blog/playwright-for-web-scraping/?utm_source=devto&amp;amp;utm_medium=organic&amp;amp;utm_campaign=jan_04&amp;amp;utm_term=bw&amp;amp;utm_content=blog)%22"&gt; is used twice, one for the title of the blog and once for the image of the blog, consider the first located link using the first attribute and assign it to &lt;em&gt;blog_link_locator&lt;/em&gt; variable.&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--ciqzw-pk--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://cdn-images-1.medium.com/max/3200/1%2A6RxVkUVU9CnKUW4t2hNYQg.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--ciqzw-pk--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://cdn-images-1.medium.com/max/3200/1%2A6RxVkUVU9CnKUW4t2hNYQg.png" width="800" height="416"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--sGg-dtt9--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://cdn-images-1.medium.com/max/3200/1%2AQ7sDBimB54utrAXUvWAi4Q.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--sGg-dtt9--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://cdn-images-1.medium.com/max/3200/1%2AQ7sDBimB54utrAXUvWAi4Q.png" width="800" height="126"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The blog author profile is also represented by a link. You can use the &lt;em&gt;get_by_role()&lt;/em&gt; Playwright locator and locate the ‘link’ with the text ‘Jaydeep Karale’ and assign it to a variable &lt;em&gt;blog_author_locator&lt;/em&gt;.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Step 6&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--pY7ZDb9P--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://cdn-images-1.medium.com/max/3200/1%2AlyRkpOUrHbqpglWMJGyhFA.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--pY7ZDb9P--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://cdn-images-1.medium.com/max/3200/1%2AlyRkpOUrHbqpglWMJGyhFA.png" width="800" height="173"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;You can use the &lt;em&gt;expect()&lt;/em&gt; Playwright assertion to test if the element located by the &lt;em&gt;blog_link_locator&lt;/em&gt; has an &lt;em&gt;href attribute ‘/blog/playwright-for-web-scraping.’&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;Then, use &lt;em&gt;expect()&lt;/em&gt; assertion on the &lt;em&gt;blog_author_locator()&lt;/em&gt; to test if the author’s profile contains &lt;em&gt;‘jaydkarale’&lt;/em&gt;, the short name of the author.&lt;/p&gt;

&lt;p&gt;The &lt;em&gt;set_test_status()&lt;/em&gt; method discussed in the &lt;em&gt;utilities.py&lt;/em&gt; is used to communicate the test status to the cloud grid if tests run on cloud infra.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Step 7&lt;/strong&gt;&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;page.pause()
page.close()
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;The &lt;em&gt;pause()&lt;/em&gt; method in the page class is helpful when debugging tests and is used to pause the execution at this point.&lt;/p&gt;

&lt;p&gt;The &lt;em&gt;close()&lt;/em&gt; method is called to close the page context.&lt;/p&gt;

&lt;p&gt;This completes the first test scenario where you saw how to handle nested iframes using the Playwright &lt;em&gt;frame&lt;/em&gt; and &lt;em&gt;frame_locator&lt;/em&gt; methods. Let us look at another test scenario to fill in a ‘Book A Demo’ form rendered within an &amp;lt;&lt;em&gt;iframe&lt;/em&gt;&amp;gt;.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Convert your text to &lt;a href="https://www.lambdatest.com/free-online-tools/text-lowercase?utm_source=devto&amp;amp;utm_medium=organic&amp;amp;utm_campaign=jan_04&amp;amp;utm_term=bw&amp;amp;utm_content=free_online_tools" rel="noopener noreferrer"&gt;text lowercase&lt;/a&gt; effortlessly using our online tool.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2&gt;
  
  
  Test Scenario: How to handle Forms within iFrames in Playwright?
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--ycrwG9lE--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://cdn-images-1.medium.com/max/2000/1%2AN7aj4hJmfbykoBLRCEp6ww.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--ycrwG9lE--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://cdn-images-1.medium.com/max/2000/1%2AN7aj4hJmfbykoBLRCEp6ww.png" width="800" height="181"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Implementation&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;The complete test code looks as below.&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;def test_iframe_tester_fill_book_a_demo_form(page):
    """
    Test if iframe renders the lambdatest blog URL correctly.
    Click on Book A Demo and fill the form with company name, firstname, lastname, email and phonenumber    
    """
    try:
        page.goto('https://iframetester.com/')
        page.get_by_placeholder('Enter a url').fill('https://www.lambdatest.com/blog')
        page.keyboard.press('Enter')        
        page.frame_locator('#iframe-window').get_by_role('link', name='Book A Demo', exact=True).click()
        page.frame_locator("internal:text=\"&amp;lt;p&amp;gt;Your browser does not support iframes.&amp;lt;/p&amp;gt;\"i").get_by_text("Allow Cookie").click()
        page.frame_locator("internal:text=\"&amp;lt;p&amp;gt;Your browser does not support iframes.&amp;lt;/p&amp;gt;\"i").get_by_placeholder("Company Name").fill('Test Company')
        page.frame_locator("internal:text=\"&amp;lt;p&amp;gt;Your browser does not support iframes.&amp;lt;/p&amp;gt;\"i").get_by_role("textbox", name="First Name*").fill('Test First Name')
        page.frame_locator("internal:text=\"&amp;lt;p&amp;gt;Your browser does not support iframes.&amp;lt;/p&amp;gt;\"i").get_by_role("textbox", name="Last Name*").fill('Test Last Name')
        page.frame_locator("internal:text=\"&amp;lt;p&amp;gt;Your browser does not support iframes.&amp;lt;/p&amp;gt;\"i").get_by_role("textbox", name="Work Email*").fill('sample@sample.com')
        page.frame_locator("internal:text=\"&amp;lt;p&amp;gt;Your browser does not support iframes.&amp;lt;/p&amp;gt;\"i").get_by_role("textbox", name="Phone Number*").fill('1234567890')
        page.pause()
        set_test_status(page, 'Passed','Form input completed')
        page.close()
    except Exception as ex:        
        set_test_status(page, 'Failed',str(ex))
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;&lt;strong&gt;Code Walk Through&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Let us now go through the test cases step-by-step.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Step 1&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--DUPV0T0L--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://cdn-images-1.medium.com/max/3200/1%2AppraU2BivTYl8mzhlW9YUA.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--DUPV0T0L--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://cdn-images-1.medium.com/max/3200/1%2AppraU2BivTYl8mzhlW9YUA.png" width="800" height="417"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--lkaGnOgm--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://cdn-images-1.medium.com/max/2888/1%2Av6j6kraOQT5ZQfdO4wN0tQ.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--lkaGnOgm--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://cdn-images-1.medium.com/max/2888/1%2Av6j6kraOQT5ZQfdO4wN0tQ.png" width="800" height="225"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Load the iframetester website using the &lt;em&gt;page.goto()&lt;/em&gt; method. The input field to enter the website is rendered as a placeholder text &lt;em&gt;‘Enter a url’&lt;/em&gt;. You can use this with the Playwright locator &lt;em&gt;get_by_placeholder()&lt;/em&gt; to locate the input field and enter the URL of the LambdaTest blog website.&lt;/p&gt;

&lt;p&gt;Then, simulate an enter key press using the &lt;em&gt;keyboard.press()&lt;/em&gt; method to render the website under test.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Step 2&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;The iframe within the iframetester website has the &lt;em&gt;id=’iframe-window’&lt;/em&gt;, which is used to interact with the website rendered within the iframe.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--5VEASbBn--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://cdn-images-1.medium.com/max/3200/1%2AMOmc5j_94iM4tSmzXYvmtg.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--5VEASbBn--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://cdn-images-1.medium.com/max/3200/1%2AMOmc5j_94iM4tSmzXYvmtg.png" width="800" height="450"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--J8AvaSNc--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://cdn-images-1.medium.com/max/3200/1%2AooYRpG84Zu9Q6I6SUjqSAQ.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--J8AvaSNc--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://cdn-images-1.medium.com/max/3200/1%2AooYRpG84Zu9Q6I6SUjqSAQ.png" width="800" height="154"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Within the iframe ‘Book A Demo’ is a link located using the Playwright locator &lt;em&gt;get_by_role()&lt;/em&gt; with link as an attribute and perform &lt;em&gt;click()&lt;/em&gt; action on it.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Step 3&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--08q2zeyq--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://cdn-images-1.medium.com/max/3200/1%2As9Uo6nYpT3WNzQbrKlKZDQ.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--08q2zeyq--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://cdn-images-1.medium.com/max/3200/1%2As9Uo6nYpT3WNzQbrKlKZDQ.png" width="800" height="420"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--62zw_Uj---/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://cdn-images-1.medium.com/max/3200/1%2AfXy4F0kC4tKFIMmDWu8Udg.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--62zw_Uj---/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://cdn-images-1.medium.com/max/3200/1%2AfXy4F0kC4tKFIMmDWu8Udg.png" width="800" height="124"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The &lt;em&gt;‘Allow Cookie’&lt;/em&gt; button is located and clicked, which makes the ‘Book Demo’ form visible.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--0lIjvLHs--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://cdn-images-1.medium.com/max/3200/1%2Ac1lmxebfR9LP0ls4bfLbzg.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--0lIjvLHs--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://cdn-images-1.medium.com/max/3200/1%2Ac1lmxebfR9LP0ls4bfLbzg.png" width="800" height="417"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Step 4&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--QyN7z_MM--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://cdn-images-1.medium.com/max/3200/1%2AX8EN0bUy-7ZAHxl5VYcygw.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--QyN7z_MM--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://cdn-images-1.medium.com/max/3200/1%2AX8EN0bUy-7ZAHxl5VYcygw.png" width="800" height="416"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--JW4sujue--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://cdn-images-1.medium.com/max/3200/1%2AmQnu1-3mA_MSq5PWKK3ByQ.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--JW4sujue--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://cdn-images-1.medium.com/max/3200/1%2AmQnu1-3mA_MSq5PWKK3ByQ.png" width="800" height="147"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The form elements are input fields, and you can locate them using the placeholder text by using the &lt;em&gt;get_by_placeholder()&lt;/em&gt; locator or &lt;em&gt;get_by_role(‘textbox’)&lt;/em&gt;. You can see both of them used to demo each locator.&lt;/p&gt;

&lt;p&gt;On the located elements, call the &lt;em&gt;fill()&lt;/em&gt; method to populate the values.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Execution&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;First execute the tests locally by setting the variable &lt;em&gt;RUN_ON=local&lt;/em&gt; in the &lt;em&gt;secrets.env *file and running the command *pytest -v.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;Both tests pass successfully.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--hK5ajOKq--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://cdn-images-1.medium.com/max/3066/1%2AoPNisCOS3lRBomlEBSNaVg.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--hK5ajOKq--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://cdn-images-1.medium.com/max/3066/1%2AoPNisCOS3lRBomlEBSNaVg.png" width="800" height="135"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;For the second test, you can also see the ‘Book A Demo’ form populated with the dummy values you provided in the test case.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--XtRJ4fst--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://cdn-images-1.medium.com/max/3200/1%2AOIlkBimCo8dvNET3kb7Vvw.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--XtRJ4fst--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://cdn-images-1.medium.com/max/3200/1%2AOIlkBimCo8dvNET3kb7Vvw.png" width="800" height="416"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;To run the tests on a cloud grid, use the cloud grid provided by LambdaTest for &lt;a href="https://www.lambdatest.com/blog/playwright-framework/?utm_source=devto&amp;amp;utm_medium=organic&amp;amp;utm_campaign=jan_04&amp;amp;utm_term=bw&amp;amp;utm_content=blog" rel="noopener noreferrer"&gt;Playwright automated testing&lt;/a&gt;. For that, amend the secrets.env file to set the &lt;em&gt;RUN_ON=cloud *and the *LT_USERNAME *and *LT_ACCESS_KEY&lt;/em&gt;, both of which can be obtained from the &lt;a href="https://accounts.lambdatest.com/security" rel="noopener noreferrer"&gt;LambdaTest Profile Page&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--YDwjAQUX--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://cdn-images-1.medium.com/max/3200/1%2AIBOxmdAbOnEbNOBjjR3bLQ.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--YDwjAQUX--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://cdn-images-1.medium.com/max/3200/1%2AIBOxmdAbOnEbNOBjjR3bLQ.png" width="800" height="415"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Running the tests using &lt;em&gt;pytest -v&lt;/em&gt;, you can see the tests running on the LambdaTest Dashboard. The dashboard clearly shows the status of the tests as configured in the &lt;em&gt;set_test_status()&lt;/em&gt; function.&lt;/p&gt;

&lt;p&gt;Additional configurations made in the capabilities dictionary in &lt;em&gt;utilities.py&lt;/em&gt;, such as the build and name of the tests, are displayed.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--dEwVpn6p--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://cdn-images-1.medium.com/max/3200/1%2A-sN6ZD0Ww99rqPEfaXqpUg.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--dEwVpn6p--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://cdn-images-1.medium.com/max/3200/1%2A-sN6ZD0Ww99rqPEfaXqpUg.png" width="800" height="393"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Additionally, when you open any test, you can see more information such as video of the execution, screenshots, test status, browser, and operating system where the tests are running.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--Q2rninHN--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://cdn-images-1.medium.com/max/3200/1%2AaZZHwc1r-l04LnobjM46Ng.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--Q2rninHN--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://cdn-images-1.medium.com/max/3200/1%2AaZZHwc1r-l04LnobjM46Ng.png" width="800" height="413"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Give your text a twist with our unique &lt;a href="https://www.lambdatest.com/free-online-tools/text-rotater?utm_source=devto&amp;amp;utm_medium=organic&amp;amp;utm_campaign=jan_04&amp;amp;utm_term=bw&amp;amp;utm_content=free_online_tools" rel="noopener noreferrer"&gt;text rotater&lt;/a&gt; tool.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2&gt;
  
  
  How to set up Playwright with TypeScript?
&lt;/h2&gt;

&lt;p&gt;Using Playwright with its Typescript API has its advantages. In terms of installation, there are two ways in which Playwright can be installed, one of them is using the Node.js package manager like npm. The second method is using the Playwright VS Code extension.&lt;/p&gt;

&lt;p&gt;Navigate to the extension manager on VS Code and search for Playwright. Then install the official Playwright Test for VS Code, as shown in the image below.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--j8VEhJ8T--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://cdn-images-1.medium.com/max/3200/1%2A3ayIyNNv1VgKXSqYFy78gg.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--j8VEhJ8T--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://cdn-images-1.medium.com/max/3200/1%2A3ayIyNNv1VgKXSqYFy78gg.png" width="800" height="431"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Once the official extension is installed, use the ‘Install Playwright’ action from the command panel to get started.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--rJPcHDiD--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://cdn-images-1.medium.com/max/3200/1%2AE1W_prE7EiIhmgkmHNhWbQ.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--rJPcHDiD--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://cdn-images-1.medium.com/max/3200/1%2AE1W_prE7EiIhmgkmHNhWbQ.png" width="800" height="432"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--u6t703Gu--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://cdn-images-1.medium.com/max/3200/1%2AMXK1TSKG3UW_jwyrTpYYBg.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--u6t703Gu--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://cdn-images-1.medium.com/max/3200/1%2AMXK1TSKG3UW_jwyrTpYYBg.png" width="800" height="430"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;A successful installation will result in logs that look like the below image.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--2c6VnRyd--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://cdn-images-1.medium.com/max/3166/1%2AdDQWd_XlfNjroLvjiA-d7Q.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--2c6VnRyd--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://cdn-images-1.medium.com/max/3166/1%2AdDQWd_XlfNjroLvjiA-d7Q.png" width="800" height="431"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Successful installation will produce a directory structure that resembles the image shown below.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--uS5RhkUS--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://cdn-images-1.medium.com/max/3200/1%2ASo35ij2MotA9fu_ioAFtyg.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--uS5RhkUS--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://cdn-images-1.medium.com/max/3200/1%2ASo35ij2MotA9fu_ioAFtyg.png" width="800" height="329"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The &lt;em&gt;tests&lt;/em&gt; folder will contain a sample test, and all new tests should be added to this folder only. The &lt;em&gt;package.json&lt;/em&gt; will contain the Playwright version, and a demo test app is also included in the &lt;em&gt;test-examples&lt;/em&gt; folder.&lt;/p&gt;

&lt;p&gt;The &lt;em&gt;playwright.config.ts&lt;/em&gt; is an important file generated as part of the installation, which stores the configuration information of where the tests are located&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;export default defineConfig({
  testDir: './tests',
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;The reporter to be used to generate reports after tests are run:&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;reporter: 'html',
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;Below is the list of browsers and device viewports to run the tests on. We will be later configuring this file to debug tests on mobile viewports. Here are the complete contents of the file.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--pXGS3DOV--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://cdn-images-1.medium.com/max/2300/1%2AuJGWRIUGXZd71x_k1wHJGA.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--pXGS3DOV--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://cdn-images-1.medium.com/max/2300/1%2AuJGWRIUGXZd71x_k1wHJGA.png" width="800" height="1113"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;All the test scenarios used for the demo in this blog are compatible with Playwright version 1.32.2, NodeJS version v18.15.0, and npm version 9.5.0.&lt;/p&gt;

&lt;p&gt;The setup required for performing &lt;a href="https://www.lambdatest.com/blog/playwright-end-to-end-testing/" rel="noopener noreferrer"&gt;Playwright end-to-end testing&lt;/a&gt; with TypeScript locally is completed.&lt;/p&gt;

&lt;p&gt;When running the &lt;a href="https://www.lambdatest.com/blog/playwright-framework?utm_source=devto&amp;amp;utm_medium=organic&amp;amp;utm_campaign=jan_04&amp;amp;utm_term=bw&amp;amp;utm_content=blog" rel="noopener noreferrer"&gt;Playwright automation&lt;/a&gt; on a cloud grid, additional settings are required, but we will discuss them in the execution part of this tutorial on handling iframes in Playwright a bit later.&lt;/p&gt;

&lt;h2&gt;
  
  
  Test Scenario: How to handle Ajax response in iFrames in Playwright?
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--_qGIVp3h--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://cdn-images-1.medium.com/max/2000/1%2ABWjQ3CetmU7n3ED3LwxBFg.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--_qGIVp3h--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://cdn-images-1.medium.com/max/2000/1%2ABWjQ3CetmU7n3ED3LwxBFg.png" width="800" height="294"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Implementation&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;The complete test code is below.&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;test('has success message', async ({ page }, testInfo ) =&amp;gt; {  
  /*
  Test if demo email and demo name can be entered in the codepen form and select Gitlab as platform and click Submit.
  On submitting we should be able to verify the success message is displayed
  */  
  await page.goto('https://codepen.io/getform/pen/jRoexL');
  await page.frameLocator('iframe[name="CodePen"]').getByPlaceholder('Enter email').fill('test@test.com');  
  await page.frameLocator('iframe[name="CodePen"]').getByPlaceholder('Enter your name').fill('Test user');
  await page.frameLocator('iframe[name="CodePen"]').getByRole('combobox', { name: 'Favourite Platform' }).selectOption('Gitlab');
  await page.frameLocator('iframe[name="CodePen"]').getByRole('button', { name: 'Submit' }).click();
  await expect(page.frameLocator('iframe[name="CodePen"]').getByText('We received your submission, thank you!')).toBeVisible();
});
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;&lt;strong&gt;Code Walk Through&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Let us now go over the test code step by step.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Step 1&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;The sample CodePen displaying the contact form is shown below.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--8SZt_zhV--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://cdn-images-1.medium.com/max/3200/1%2AkyafxyyCRF5M7FS6Rj05Yg.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--8SZt_zhV--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://cdn-images-1.medium.com/max/3200/1%2AkyafxyyCRF5M7FS6Rj05Yg.png" width="800" height="417"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--qEssVyJ8--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://cdn-images-1.medium.com/max/2244/1%2AeMmR60beo0I7rApFMwgscA.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--qEssVyJ8--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://cdn-images-1.medium.com/max/2244/1%2AeMmR60beo0I7rApFMwgscA.png" width="800" height="235"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;On the browser page, call the &lt;em&gt;goto()&lt;/em&gt; method to navigate to the codepen URL.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Step 2&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--616cio_F--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://cdn-images-1.medium.com/max/3200/1%2A_lO8L1QIU4NDqBn2N2TV9A.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--616cio_F--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://cdn-images-1.medium.com/max/3200/1%2A_lO8L1QIU4NDqBn2N2TV9A.png" width="800" height="416"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;On inspection, you can see that the form is displayed within an iframe with &lt;em&gt;name=’CodePen’&lt;/em&gt;. You can leverage this to enter the iframe using the &lt;em&gt;frameLocator()&lt;/em&gt; method.&lt;/p&gt;

&lt;p&gt;All other elements within the iframe are then located using the frame as a reference.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Step 3&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--Iv6s4LWe--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://cdn-images-1.medium.com/max/3200/1%2AZaVL1s34ICke9gBM95jznw.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--Iv6s4LWe--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://cdn-images-1.medium.com/max/3200/1%2AZaVL1s34ICke9gBM95jznw.png" width="800" height="415"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--XMKIuXAc--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://cdn-images-1.medium.com/max/3200/1%2AXZg7RKte_j2mLDeOuVezQw.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--XMKIuXAc--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://cdn-images-1.medium.com/max/3200/1%2AXZg7RKte_j2mLDeOuVezQw.png" width="800" height="143"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;In the iframe, the input field to enter email has a &lt;em&gt;placeholder&lt;/em&gt; ‘Enter email’.&lt;/p&gt;

&lt;p&gt;We use the Playwright locator &lt;em&gt;getByPlaceholder()&lt;/em&gt; and supply the placeholder text and call the &lt;em&gt;fill(‘&lt;a href="mailto:test@test.com"&gt;test@test.com&lt;/a&gt;’)&lt;/em&gt; method to enter a dummy email.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Step 4&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--mIrZpJSA--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://cdn-images-1.medium.com/max/2000/1%2Ak9xmj-bocOzyGA2p6EpH6A.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--mIrZpJSA--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://cdn-images-1.medium.com/max/2000/1%2Ak9xmj-bocOzyGA2p6EpH6A.png" width="800" height="587"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Like the email field, the name input field is located using the placeholder text and &lt;em&gt;getByPlaceholder()&lt;/em&gt; Playwright locator.&lt;/p&gt;

&lt;p&gt;The &lt;em&gt;fill()&lt;/em&gt; method is used to populate a dummy name, ‘Test user’.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Step 5&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--8q7YrdsF--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://cdn-images-1.medium.com/max/3200/1%2Ayp5Pp67GYieF_vtTsD-qEA.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--8q7YrdsF--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://cdn-images-1.medium.com/max/3200/1%2Ayp5Pp67GYieF_vtTsD-qEA.png" width="800" height="417"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--h79GXVeW--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://cdn-images-1.medium.com/max/3200/1%2ARCSBzh2FVif_fLj11Fy69A.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--h79GXVeW--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://cdn-images-1.medium.com/max/3200/1%2ARCSBzh2FVif_fLj11Fy69A.png" width="800" height="120"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The dropdown to select the platform is located using &lt;em&gt;getByRole (‘combobox’, {name:’Favourite Platform’ } )&lt;/em&gt;.&lt;/p&gt;

&lt;p&gt;Using the &lt;em&gt;selectOption()&lt;/em&gt; method, select the &lt;em&gt;‘Gitlab’&lt;/em&gt; option.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Step 6&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--nFQaM1X_--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://cdn-images-1.medium.com/max/3200/1%2AIO8666YbPKEleitlSUyAAg.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--nFQaM1X_--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://cdn-images-1.medium.com/max/3200/1%2AIO8666YbPKEleitlSUyAAg.png" width="800" height="151"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;em&gt;‘Submit&lt;/em&gt;’ is a button and the &lt;em&gt;getByRole()&lt;/em&gt; method is used with a role argument as ‘button’ and &lt;em&gt;name: ‘Submit’&lt;/em&gt;.&lt;/p&gt;

&lt;p&gt;The &lt;em&gt;click()&lt;/em&gt; method is called on the located button to submit the form.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Step 7&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--OD7pT4z_--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://cdn-images-1.medium.com/max/3200/1%2AfnjOezXOtwih6hFna2r9ug.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--OD7pT4z_--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://cdn-images-1.medium.com/max/3200/1%2AfnjOezXOtwih6hFna2r9ug.png" width="800" height="418"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--IyLHXYCQ--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://cdn-images-1.medium.com/max/3200/1%2AIzJFCAtxBkoomBxo_Abw2w.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--IyLHXYCQ--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://cdn-images-1.medium.com/max/3200/1%2AIzJFCAtxBkoomBxo_Abw2w.png" width="800" height="122"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The &lt;a href="https://www.lambdatest.com/learning-hub/playwright-assertions?utm_source=devto&amp;amp;utm_medium=organic&amp;amp;utm_campaign=jan_04&amp;amp;utm_term=bw&amp;amp;utm_content=learning_hub" rel="noopener noreferrer"&gt;Playwright assertion&lt;/a&gt; — &lt;em&gt;expect() *is used to assert the response *text toBeVisible()&lt;/em&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Test Scenario: How to handle search within iFrames in Playwright?
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--UvOgE10N--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://cdn-images-1.medium.com/max/2000/1%2A4EtkFdPBUsOLqstWKLfAyQ.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--UvOgE10N--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://cdn-images-1.medium.com/max/2000/1%2A4EtkFdPBUsOLqstWKLfAyQ.png" width="679" height="163"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Implementation&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Here is what the complete test looks like.&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;test('w3 schools has typescript tutorials', async ({ page }) =&amp;gt; {  
  /*
  Test if demo website has a search bar and we are able to search for TypeScript tutorials.
  */
    await page.goto('https://www.w3schools.com/tags/tryit.asp?filename=tryhtml_iframe');
    await page.frameLocator('iframe[name="iframeResult"]').frameLocator('iframe[title="W3Schools Free Online Web Tutorials"]').getByPlaceholder('Search our tutorials, e.g. HTML').fill('typescript');
    await page.frameLocator('iframe[name="iframeResult"]').frameLocator('iframe[title="W3Schools Free Online Web Tutorials"]').getByRole('button', { name: '' }).click();
    await expect(page.frameLocator('iframe[name="iframeResult"]').frameLocator('iframe[title="W3Schools Free Online Web Tutorials"]').getByRole('link', { name: 'Start learning TypeScript now »' })).toBeVisible();
});
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;&lt;strong&gt;Code Walk Through&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Let us now go over the step-by-step walkthrough of the code.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Step 1&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--CrAXAKDE--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://cdn-images-1.medium.com/max/3072/1%2AUQZAweXiARnMAvS2Kuuj3w.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--CrAXAKDE--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://cdn-images-1.medium.com/max/3072/1%2AUQZAweXiARnMAvS2Kuuj3w.png" width="800" height="172"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Navigate to the W3Schools iframe test website by calling the &lt;em&gt;goto()&lt;/em&gt; method on the &lt;em&gt;page&lt;/em&gt; class.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Step 2&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--OcMe0toj--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://cdn-images-1.medium.com/max/3200/1%2A-9tfCjrGYwk7VbwPajQRsA.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--OcMe0toj--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://cdn-images-1.medium.com/max/3200/1%2A-9tfCjrGYwk7VbwPajQRsA.png" width="800" height="416"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--os-0t3Sy--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://cdn-images-1.medium.com/max/3200/1%2ArRl_-yTrTCmhx9D4NcJZJQ.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--os-0t3Sy--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://cdn-images-1.medium.com/max/3200/1%2ArRl_-yTrTCmhx9D4NcJZJQ.png" width="800" height="421"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--wjMvmMGF--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://cdn-images-1.medium.com/max/3200/1%2AiX16GGFYv6ZA1gkgtJuvUA.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--wjMvmMGF--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://cdn-images-1.medium.com/max/3200/1%2AiX16GGFYv6ZA1gkgtJuvUA.png" width="800" height="82"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The outer iframe has &lt;em&gt;name=’iframeResult’&lt;/em&gt;, and the inner iframe has a &lt;em&gt;title=’W3Schools Free Online Web Tutorials’&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;You can use chaining to navigate to the inner iframe by using the &lt;em&gt;frameLocator()&lt;/em&gt; method twice, once on the outer iframe and then on the inner iframe in Playwright.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--YhuxWskx--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://cdn-images-1.medium.com/max/3200/1%2AzRAdkhcHjFCjdK317MyTLQ.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--YhuxWskx--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://cdn-images-1.medium.com/max/3200/1%2AzRAdkhcHjFCjdK317MyTLQ.png" width="800" height="418"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;In the inner iframe, we use the &lt;em&gt;getByPlaceholder()&lt;/em&gt; to locate the search field and fill in the value &lt;em&gt;‘typescript’&lt;/em&gt; to search for all TypeScript content.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Step 3&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--NjSIY5Ln--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://cdn-images-1.medium.com/max/3200/1%2AqKCXLgdHzz5RK370TYi1xg.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--NjSIY5Ln--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://cdn-images-1.medium.com/max/3200/1%2AqKCXLgdHzz5RK370TYi1xg.png" width="800" height="417"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--FdJPZQ4H--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://cdn-images-1.medium.com/max/3200/1%2ARR1F3GUUhMlyv59g3LVLmQ.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--FdJPZQ4H--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://cdn-images-1.medium.com/max/3200/1%2ARR1F3GUUhMlyv59g3LVLmQ.png" width="800" height="94"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The chaining is continued in this step as well to navigate to the inner frame.&lt;/p&gt;

&lt;p&gt;Within the inner frame, the search is located by using &lt;em&gt;getByRole(‘button’)&lt;/em&gt;, and then the &lt;em&gt;click()&lt;/em&gt; action is performed.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Step 4&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--iSCtS0TS--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://cdn-images-1.medium.com/max/3200/1%2AnJGinYLbOFD3s0ctxuFp9g.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--iSCtS0TS--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://cdn-images-1.medium.com/max/3200/1%2AnJGinYLbOFD3s0ctxuFp9g.png" width="800" height="417"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--_S7mqkCB--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://cdn-images-1.medium.com/max/3200/1%2AEe3UYXTjJydTbC2m65U6aw.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--_S7mqkCB--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://cdn-images-1.medium.com/max/3200/1%2AEe3UYXTjJydTbC2m65U6aw.png" width="800" height="77"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The results contain a &lt;em&gt;‘Start learning Typescript now’&lt;/em&gt; link.&lt;/p&gt;

&lt;p&gt;You can use Playwright assertion &lt;em&gt;expect()&lt;/em&gt; to assert this link &lt;em&gt;toBeVisible()&lt;/em&gt;, which is a method to assert the visibility of elements on a webpage.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Execution&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Let us now run the test locally first on Chromium. For that purpose, you need to enable Chromium in &lt;em&gt;playwright.config.ts&lt;/em&gt; file in the project’s array.&lt;/p&gt;

&lt;p&gt;Chromium is configured as a JSON object, as shown below:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--xx3ADDoN--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://cdn-images-1.medium.com/max/2088/1%2AVrJfUhDzVF7H5PtEl0NTuQ.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--xx3ADDoN--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://cdn-images-1.medium.com/max/2088/1%2AVrJfUhDzVF7H5PtEl0NTuQ.png" width="800" height="602"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;You can now run the tests using the command:&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;npx playwright test –project=Chromium
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;Both the tests are passing successfully, and you can generate a detailed execution summary using the command.&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;npx playwright show-report
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--QQEr0I7T--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://cdn-images-1.medium.com/max/2254/1%2AjdG9aQbFJqtxcpM5L4DJUw.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--QQEr0I7T--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://cdn-images-1.medium.com/max/2254/1%2AjdG9aQbFJqtxcpM5L4DJUw.png" width="800" height="171"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The summary view of the report looks like the one shown below and clearly shows the number of tests run, passed, failed, and skipped.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--ebkrYxNX--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://cdn-images-1.medium.com/max/3200/1%2Aq8ZBBmawr6iyzustLZ7LUA.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--ebkrYxNX--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://cdn-images-1.medium.com/max/3200/1%2Aq8ZBBmawr6iyzustLZ7LUA.png" width="800" height="124"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;On clicking an individual test, you can see a more detailed view of the test execution in terms of the test steps and browser engine used.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--YVK5tDz8--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://cdn-images-1.medium.com/max/3200/1%2AhP0HUNwCRnPGfvvTli2NGA.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--YVK5tDz8--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://cdn-images-1.medium.com/max/3200/1%2AhP0HUNwCRnPGfvvTli2NGA.png" width="800" height="416"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Let us now look at the execution of the tests on a cloud grid such as LambdaTest. For that purpose, add two new files to your project.&lt;/p&gt;

&lt;p&gt;The first file is called &lt;em&gt;‘.env’&lt;/em&gt;, which will store the username and access key required to access the cloud grid. The username and access key can be obtained from the &lt;a href="https://accounts.lambdatest.com/security" rel="noopener noreferrer"&gt;password &amp;amp; security page &lt;/a&gt;of LambdaTest.&lt;/p&gt;

&lt;p&gt;Add the username and access key to the &lt;em&gt;.env&lt;/em&gt; file.&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;LT_USERNAME=&amp;lt;LT_USERNAME&amp;gt;
LT_ACCESS_KEY=&amp;lt;LT_ACCESS_KEY&amp;gt;&amp;lt;/LT_ACCESS_KEY&amp;gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;To ensure that env variables are available for our tests, use the &lt;em&gt;dotenv npm&lt;/em&gt; package. It can be installed using the &lt;em&gt;npm install dotenv&lt;/em&gt; command.&lt;/p&gt;

&lt;p&gt;This dotenv package reads the ‘.env’ file and exports the variables in it to be used in our code whenever needed using &lt;em&gt;process.env.LT_USERNAME or process.env.LT_ACCESS_KEY&lt;/em&gt;. The general format remains process.env.&lt;em&gt;&amp;lt; ENV_VARIABLE_NAME &amp;gt;&lt;/em&gt;.&lt;/p&gt;

&lt;p&gt;The second file you need is the &lt;em&gt;lambdatest-setup.ts.&lt;/em&gt; The naming of the file is for reference purposes. It can be named anything as per your preference. Here is what the complete file looks like.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;FileName — lambdatest-setup.ts&lt;/em&gt;&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;/**
 * Add the file in your test suite to run tests on LambdaTest.
 * Import 'test' object from this file in the tests.
 */

import * as base from "@playwright/test";
import path from "path";
import { chromium } from "@playwright/test"
import dotenv from 'dotenv';
dotenv.config();

// LambdaTest capabilities
const capabilities = {
  browserName: "Chrome", // Browsers allowed: 'Chrome', 'MicrosoftEdge', 'pw-chromium', 'pw-firefox' and 'pw-webkit'
  browserVersion: "latest",
  "LT:Options": {
    platform: "Windows 10",
    build: "Playwright TypeScript iframes",
    name: "Playwright TypeScript iframes",
    user: process.env.LT_USERNAME,
    accessKey: process.env.LT_ACCESS_KEY,
    network: true,
    video: true,
    console: true,
    tunnel: false, // Add tunnel configuration if testing locally hosted webpage
    tunnelName: "", // Optional
    geoLocation: "US", // country code can be fetched from https://www.lambdatest.com/capabilities-generator/
  },
};

// Patching the capabilities dynamically according to the project name.
const modifyCapabilities = (configName, testName) =&amp;gt; {
  let config = configName.split("@lambdatest")[0];
  let [browserName, browserVersion, platform] = config.split(":");
  capabilities.browserName = browserName
    ? browserName
    : capabilities.browserName;
  capabilities.browserVersion = browserVersion
    ? browserVersion
    : capabilities.browserVersion;
  capabilities["LT:Options"]["platform"] = platform
    ? platform
    : capabilities["LT:Options"]["platform"];
  capabilities["LT:Options"]["name"] = testName;
};

const getErrorMessage = (obj, keys) =&amp;gt;
  keys.reduce(
    (obj, key) =&amp;gt; (typeof obj == "object" ? obj[key] : undefined),
    obj
  );

const test = base.test.extend({
  page: async ({ page, playwright }, use, testInfo) =&amp;gt; {  
    // Configure LambdaTest platform for cross-browser testing
    let fileName = testInfo.file.split(path.sep).pop();
    if (testInfo.project.name.match(/lambdatest/)) {
      modifyCapabilities(
        testInfo.project.name,
        '${testInfo.title} - ${fileName}'
      );
      const browser = await chromium.connect({
        wsEndpoint: 'wss://cdp.lambdatest.com/playwright?capabilities=${encodeURIComponent(
          JSON.stringify(capabilities)
        )}',
      });

      const ltPage = await browser.newPage(testInfo.project.use);
   await use(ltPage);

      const testStatus = {
        action: "setTestStatus",
        arguments: {
          status: testInfo.status,
          remark: getErrorMessage(testInfo, ["error", "message"]),
        },
      };
      await ltPage.evaluate(() =&amp;gt; {},
      'lambdatest_action: ${JSON.stringify(testStatus)}');
      await ltPage.close();
      // await browser.close();
    } else {
      // Run tests in local in case of local config provided
      await use(page);
    }
  },
});
export default test;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;Let us now understand the contents of the file step by step.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--yqsNjt_C--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://cdn-images-1.medium.com/max/2396/1%2APE74fjggcaf9QGMnix4UYg.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--yqsNjt_C--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://cdn-images-1.medium.com/max/2396/1%2APE74fjggcaf9QGMnix4UYg.png" width="800" height="449"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The first few lines in the files are the imports. The two notable imports are the Chromium browser engine from Playwright to support running the tests. The second is the &lt;em&gt;dotenv&lt;/em&gt; package, which will help us convert the &lt;em&gt;.env&lt;/em&gt; file to environment variables and use them in our code.&lt;/p&gt;

&lt;p&gt;The &lt;em&gt;dotenv.config()&lt;/em&gt; is the line that reads the ‘.env’ and exposes them as environment variables.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--JgnObl43--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://cdn-images-1.medium.com/max/3200/1%2A1886Fi1mERRCQIFGWHSSVg.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--JgnObl43--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://cdn-images-1.medium.com/max/3200/1%2A1886Fi1mERRCQIFGWHSSVg.png" width="800" height="385"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The JSON object &lt;em&gt;capabilities&lt;/em&gt; are used to configure the behavior of the cloud grid and supply the username and access key.&lt;/p&gt;

&lt;p&gt;The configuration we can make is setting the *browserName, browserVersion, platform *(operating system), enable or disable network and console logs and enable or disable execution video capture.&lt;/p&gt;

&lt;p&gt;In the case of locally hosted websites, tunnel to the LambdaTest cloud grid can be also provisioned here.&lt;/p&gt;

&lt;p&gt;The next part is configuring the operating system and the browser, which will be used when running the tests on the cloud grid.&lt;/p&gt;

&lt;p&gt;A variety of options, such as Linux, macOS, and Windows are available in terms of operating system, and all modern browser engines are supported by LambdaTest cloud grid.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--z_0eZqQ4--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://cdn-images-1.medium.com/max/2672/1%2AvX4mZ7qxa8-L8X9-Vg-tYg.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--z_0eZqQ4--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://cdn-images-1.medium.com/max/2672/1%2AvX4mZ7qxa8-L8X9-Vg-tYg.png" width="800" height="516"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The final part is to override the base &lt;em&gt;‘Playwright/test’&lt;/em&gt; method to make it compatible with connecting to the cloud grid and running tests. The complete method implementation is as below.&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;const test = base.test.extend({
  page: async ({ page, playwright }, use, testInfo) =&amp;gt; {  
    // Configure LambdaTest platform for cross-browser testing
    let fileName = testInfo.file.split(path.sep).pop();
    if (testInfo.project.name.match(/lambdatest/)) {
      modifyCapabilities(
        testInfo.project.name,
        '${testInfo.title} - ${fileName}'
      );
      const browser = await chromium.connect({
        wsEndpoint: 'wss://cdp.lambdatest.com/playwright?capabilities=${encodeURIComponent(
          JSON.stringify(capabilities)
        )}',
      });

      const ltPage = await browser.newPage(testInfo.project.use);
      await use(ltPage);

      const testStatus = {
        action: "setTestStatus",
        arguments: {
          status: testInfo.status,
          remark: getErrorMessage(testInfo, ["error", "message"]),
        },
      };
      await ltPage.evaluate(() =&amp;gt; {},
      'lambdatest_action: ${JSON.stringify(testStatus)}');
      await ltPage.close();
      // await browser.close();
    } else {
      // Run tests in local in case of local config provided
      await use(page);
    }
  },
});
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;Let us understand the method in detail by doing a step-by-step walkthrough.&lt;/p&gt;

&lt;p&gt;The testInfo is a special Playwright variable that contains information about the currently running test. Extract the name of the TypeScript file that contains the tests.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--pDXehOjs--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://cdn-images-1.medium.com/max/2056/1%2AulgY4NoiT3UAJQkmWsSXHA.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--pDXehOjs--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://cdn-images-1.medium.com/max/2056/1%2AulgY4NoiT3UAJQkmWsSXHA.png" width="800" height="257"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Next, use the &lt;em&gt;testInfo&lt;/em&gt; to determine the name of the project, which is configured in the &lt;em&gt;playwright.config.ts&lt;/em&gt; projects array. If the name contains the word &lt;em&gt;‘lambdatest’&lt;/em&gt;, then run the tests on the cloud grid else locally.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--1q24vS-8--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://cdn-images-1.medium.com/max/2000/1%2AsbcZ-r_T_tpgw2bETJVU8A.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--1q24vS-8--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://cdn-images-1.medium.com/max/2000/1%2AsbcZ-r_T_tpgw2bETJVU8A.png" width="800" height="269"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The snapshot below shows how the project’s array looks to run the tests on a cloud grid. We have configured the cloud grid to provide Windows 10 &amp;amp; Chrome latest version as the running environment.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--xkT_aoKu--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://cdn-images-1.medium.com/max/2000/1%2Atdswm0hLMxmfmyXZaKophA.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--xkT_aoKu--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://cdn-images-1.medium.com/max/2000/1%2Atdswm0hLMxmfmyXZaKophA.png" width="800" height="516"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The &lt;em&gt;connect&lt;/em&gt; method is used to supply the URL to connect to the cloud grid, and also provided to it is the capabilities object configured to run the tests and contains the username and access key. This returns a browser object.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--KTYAmbKH--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://cdn-images-1.medium.com/max/3200/1%2ALnJEtvlzYmFSSI8IsrZIlA.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--KTYAmbKH--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://cdn-images-1.medium.com/max/3200/1%2ALnJEtvlzYmFSSI8IsrZIlA.png" width="800" height="235"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Using the browser object, you can create a web page.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--QBsfQ-6b--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://cdn-images-1.medium.com/max/2304/1%2AklqIb4uXw_TufF-2MP9a1A.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--QBsfQ-6b--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://cdn-images-1.medium.com/max/2304/1%2AklqIb4uXw_TufF-2MP9a1A.png" width="800" height="256"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The &lt;em&gt;testStatus&lt;/em&gt; method is used to set the test status i.e., either success or fail on the cloud grid.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--1ihKmGb3--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://cdn-images-1.medium.com/max/2520/1%2A6IZBbbXeDfY4nto-tSjLvQ.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--1ihKmGb3--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://cdn-images-1.medium.com/max/2520/1%2A6IZBbbXeDfY4nto-tSjLvQ.png" width="800" height="403"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;In the file containing the test, we wrote &lt;em&gt;testiframes.spec.ts&lt;/em&gt;. You can now import the test method. This is how the complete file looks now.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;FileName — testiframes.spec.ts&lt;/em&gt;&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;import { expect } from '@playwright/test';
import test from "../lambdatest-setup";

test('has success message', async ({ page }, testInfo ) =&amp;gt; {  
  /*
  Test if demo email and demo name can be entered in the codepen form and select Gitlab as platform and click Submit.
  On submitting we should be able to verify the success message is displayed
  */  
  await page.goto('https://codepen.io/getform/pen/jRoexL');
  await page.frameLocator('iframe[name="CodePen"]').getByPlaceholder('Enter email').fill('test@test.com');  
  await page.frameLocator('iframe[name="CodePen"]').getByPlaceholder('Enter your name').fill('Test user');
  await page.frameLocator('iframe[name="CodePen"]').getByRole('combobox', { name: 'Favourite Platform' }).selectOption('Gitlab');
  await page.frameLocator('iframe[name="CodePen"]').getByRole('button', { name: 'Submit' }).click();
  await expect(page.frameLocator('iframe[name="CodePen"]').getByText('We received your submission, thank you!')).toBeVisible();
});

test('w3 schools has typescript tutorials', async ({ page }) =&amp;gt; {  
  /*
  Test if demo website has a search bar and we are able to search for TypeScript tutorials.
  */
    await page.goto('https://www.w3schools.com/tags/tryit.asp?filename=tryhtml_iframe');
    await page.frameLocator('iframe[name="iframeResult"]').frameLocator('iframe[title="W3Schools Free Online Web Tutorials"]').getByPlaceholder('Search our tutorials, e.g. HTML').fill('typescript');
    await page.frameLocator('iframe[name="iframeResult"]').frameLocator('iframe[title="W3Schools Free Online Web Tutorials"]').getByRole('button', { name: '' }).click();
    await expect(page.frameLocator('iframe[name="iframeResult"]').frameLocator('iframe[title="W3Schools Free Online Web Tutorials"]').getByRole('link', { name: 'Start learning TypeScript now »' })).toBeVisible();
});
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;Since you have seen the execution of both tests locally and on the cloud grid, we will demo a single running of a single test, &lt;em&gt;‘w3 schools has typescript tutorials’&lt;/em&gt;.&lt;/p&gt;

&lt;p&gt;Leveraging cloud grids provided by LambdaTest, an AI-powered test orchestration and execution platform, for running &lt;a href="https://www.lambdatest.com/playwright-testing?utm_source=devto&amp;amp;utm_medium=organic&amp;amp;utm_campaign=jan_04&amp;amp;utm_term=bw&amp;amp;utm_content=webpage" rel="noopener noreferrer"&gt;Playwright tests&lt;/a&gt; provide advantages such as scalability and availability of multiple operating systems and browsers to understand the test behavior on a wide range of test setups.&lt;/p&gt;

&lt;p&gt;Run the command &lt;em&gt;npx playwright test -g “w3 schools has typescript tutorials”.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--yVWz9Cgh--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://cdn-images-1.medium.com/max/2000/1%2AZVtdRoIcPGWJRXcNzQjFTw.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--yVWz9Cgh--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://cdn-images-1.medium.com/max/2000/1%2AZVtdRoIcPGWJRXcNzQjFTw.png" width="782" height="180"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Here is how a successfully run test will look on the LambdaTest Dashboard.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--nsclR0t6--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://cdn-images-1.medium.com/max/3200/1%2AJhpVNg3I2BhaJIpGyn1uxw.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--nsclR0t6--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://cdn-images-1.medium.com/max/3200/1%2AJhpVNg3I2BhaJIpGyn1uxw.png" width="800" height="415"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;On clicking the test, we can also see a detailed view of the test, such as the execution video, the actionability logs, the operating system, and the browsers used.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--F5Jij160--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://cdn-images-1.medium.com/max/3200/1%2AEdTdpVZYB8vkJHSZEPaKSw.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--F5Jij160--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://cdn-images-1.medium.com/max/3200/1%2AEdTdpVZYB8vkJHSZEPaKSw.png" width="800" height="415"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The &lt;em&gt;npx playwright show-report&lt;/em&gt; command also shows the details of the test execution as before.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--cE1bOCK0--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://cdn-images-1.medium.com/max/3200/1%2Aa5-RHz10DeNpi4jUvvExmA.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--cE1bOCK0--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://cdn-images-1.medium.com/max/3200/1%2Aa5-RHz10DeNpi4jUvvExmA.png" width="800" height="416"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;This completes the execution of the test on the cloud grid.&lt;/p&gt;

&lt;p&gt;Playwright automation is straightforward to grasp, requiring only fundamental knowledge of Python and TypeScript. If you’re interested in mastering test execution through Playwright automation, LambdaTest offers a complimentary &lt;a href="https://www.lambdatest.com/certifications/playwright-101?utm_source=devto&amp;amp;utm_medium=organic&amp;amp;utm_campaign=jan_04&amp;amp;utm_term=bw&amp;amp;utm_content=certifications" rel="noopener noreferrer"&gt;Playwright 101 certification&lt;/a&gt; to enhance your automation testing abilities.&lt;/p&gt;

&lt;h2&gt;
  
  
  Conclusion
&lt;/h2&gt;

&lt;p&gt;In this tutorial on handling iframes in Playwright, you have seen in detail how you can use Playwright to handle frames using its Python and TypeScript API. You have also had a look at what the use case is for frames and what is the distinction between the HTML tags frame and iframe.&lt;/p&gt;

&lt;p&gt;Since HTML 5 iframe is the preferred way of embedding content from other websites into our own, you also learned how to embed a YouTube video within a custom-developed iframe.&lt;/p&gt;

&lt;p&gt;Iframes in Playwright provide a convenient way of embedding content from other sources such as YouTube, Twitter, and Google, and even placing advertisements, their usage can provide better modularity to our website. Hence, reliably &lt;a href="https://www.lambdatest.com/blog/website-testing/?utm_source=devto&amp;amp;utm_medium=organic&amp;amp;utm_campaign=jan_04&amp;amp;utm_term=bw&amp;amp;utm_content=blog" rel="noopener noreferrer"&gt;testing websites&lt;/a&gt; containing iframes is key to delivering a better user experience.&lt;/p&gt;

&lt;p&gt;You also learned scenarios of nested iframes in Playwright, and you can test them using Python and TypeScript.&lt;/p&gt;

&lt;p&gt;The execution of the tests was covered locally as well as on a cloud grid. The advantage of using a cloud grid for &lt;a href="https://www.lambdatest.com/automation-testing?utm_source=devto&amp;amp;utm_medium=organic&amp;amp;utm_campaign=jan_04&amp;amp;utm_term=bw&amp;amp;utm_content=webpage" rel="noopener noreferrer"&gt;automation testing&lt;/a&gt; is that time and cost can be saved on setting up various cross platform and cross browser environments. All that is needed is a simple configuration setup, and the desired operating system and browser configuration are available for running the tests.&lt;/p&gt;

&lt;p&gt;This provides more confidence to QA and Automation teams by ensuring that the user experience remains the same across different browsers and operating systems.&lt;/p&gt;

</description>
      <category>playwright</category>
      <category>iframes</category>
      <category>testing</category>
      <category>programming</category>
    </item>
    <item>
      <title>Playwright Docker Tutorial: Detailed Guide With Example</title>
      <dc:creator>Jaydeep Karale</dc:creator>
      <pubDate>Mon, 22 Jan 2024 10:33:42 +0000</pubDate>
      <link>https://dev.to/_jaydeepkarale/playwright-docker-tutorial-detailed-guide-with-example-gkp</link>
      <guid>https://dev.to/_jaydeepkarale/playwright-docker-tutorial-detailed-guide-with-example-gkp</guid>
      <description>&lt;p&gt;OVERVIEW&lt;/p&gt;

&lt;p&gt;As software development advanced, software became better, but so did the demands. The tech industry has gone from deploying to production once in 6 months to deploying 6 times daily. With &lt;a href="https://www.lambdatest.com/learning-hub/agile-development?utm_source=devto&amp;amp;utm_medium=organic&amp;amp;utm_campaign=jan_03&amp;amp;utm_term=bw&amp;amp;utm_content=learning_hub" rel="noopener noreferrer"&gt;Agile development&lt;/a&gt; methodologies leading the way, software development has advanced at lightning speeds, but there’s always a catch.&lt;/p&gt;

&lt;p&gt;“It works on my machine” is a common problem when it comes to software development and testing. Simply put, code and tests written on the developers’ local machines sometimes fail to execute on the machines of testers, fellow developers, or even in production, hampering the software development and testing.&lt;/p&gt;

&lt;p&gt;As businesses demanded shipping features faster and reduced time to market, tech teams needed now more than ever to find solutions to the problem of running code consistently across the team and environments.&lt;/p&gt;

&lt;p&gt;For context, companies like Amazon, Google, and Netflix deploy to production 1000s of time per day (aggregated over the 100s of services that comprise their production environment). While most companies don’t need to deploy at the rate of these tech giants, deploying once or twice per week is now the new normal. And to achieve this deployment rate, there is less and less scope for team members to say, “It doesn’t work on my machine.”&lt;/p&gt;

&lt;p&gt;Docker is a platform that allows developers to create and manage applications in containers, which are lightweight and portable environments. With Docker, we can quickly set up and test applications in isolated environments without worrying about compatibility issues. These containers can be easily shared with others and deployed to different environments, making it an efficient way to manage applications at scale.&lt;/p&gt;

&lt;p&gt;Not only does Docker solve the puzzle of running software consistently and at scale, but it also provides the necessary means to set up at scale, high quality, high speed, and resilient &lt;a href="https://www.lambdatest.com/automation-testing?utm_source=devto&amp;amp;utm_medium=organic&amp;amp;utm_campaign=jan_03&amp;amp;utm_term=bw&amp;amp;utm_content=webpage" rel="noopener noreferrer"&gt;automated testing&lt;/a&gt;. Since the testing team no longer has to worry about setting up code to run locally, the time saved can be invested in writing better end-to-end automation tests, which ensures high-quality software delivery by catching defects early.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://www.lambdatest.com/playwright?utm_source=devto&amp;amp;utm_medium=organic&amp;amp;utm_campaign=jan_03&amp;amp;utm_term=bw&amp;amp;utm_content=webpage" rel="noopener noreferrer"&gt;Playwright&lt;/a&gt;, a library for automated end-to-end browser testing, and Docker, a platform for running applications in containers, provide a powerful solution for developers looking to streamline their testing process. By combining Playwright and Docker, developers can confidently create, test, and deploy their applications, knowing they will run consistently on any machine.&lt;/p&gt;

&lt;p&gt;In this Playwright Docker tutorial, we will dive deep into how Playwright and Docker can be used together. When paired together, it can simplify &lt;a href="https://www.lambdatest.com/online-browser-testing?utm_source=devto&amp;amp;utm_medium=organic&amp;amp;utm_campaign=jan_03&amp;amp;utm_term=bw&amp;amp;utm_content=webpage" rel="noopener noreferrer"&gt;browser testing&lt;/a&gt; and improve the reliability and speed of test automation. It can also reduce the time and resources required for testing complex web applications.&lt;/p&gt;

&lt;h2&gt;
  
  
  Introduction To Docker
&lt;/h2&gt;

&lt;p&gt;&lt;em&gt;Fun Fact !!!&lt;/em&gt; The story of Docker can be traced back to the shipping industry and the development of standardized shipping containers in the mid-20th century. Before standardized containers, the cargo was loaded and unloaded by hand, making shipping slow and inefficient.&lt;/p&gt;

&lt;p&gt;But with the introduction of standardized containers, it became possible to load and unload cargo using cranes and other machinery, reducing shipping times and costs. This revolutionized the shipping industry, making it possible to move goods more quickly and efficiently than ever before.&lt;/p&gt;

&lt;p&gt;On similar lines, Docker revolutionized the world of software development by introducing a standardized, container-based approach to packaging, distributing, and running applications.&lt;/p&gt;

&lt;p&gt;Like shipping containers, Docker containers enable developers to move applications quickly and efficiently between different environments, from development to testing to production, making it easier to develop and deploy software at scale.&lt;/p&gt;

&lt;h2&gt;
  
  
  What Problem Does Docker Solve?
&lt;/h2&gt;

&lt;p&gt;Before implementing &lt;a href="https://www.lambdatest.com/learning-hub/end-to-end-testing?utm_source=devto&amp;amp;utm_medium=organic&amp;amp;utm_campaign=jan_03&amp;amp;utm_term=bw&amp;amp;utm_content=learning_hub" rel="noopener noreferrer"&gt;end-to-end testing&lt;/a&gt; using Playwright Docker, it’s important to understand what problem Docker solves. Let’s now understand them.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Isolated Execution Environments&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;With software development becoming accessible to anyone with a laptop and an Internet connection, the differentiating factor now is a shorter time to market and faster feature shipping. And to achieve this, it’s important to provide isolated yet consistent execution for teams.&lt;/p&gt;

&lt;p&gt;This is where Docker comes in. Docker is a powerful containerization platform that allows developers to package their applications and dependencies into a portable container that can be easily deployed across different environments. It eliminates the challenges of testing across different environments, ensuring that code runs consistently across all machines.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Streamlining Testing&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Docker has become an essential tool for &lt;a href="https://www.lambdatest.com/learning-hub/software-testing?utm_source=devto&amp;amp;utm_medium=organic&amp;amp;utm_campaign=jan_03&amp;amp;utm_term=bw&amp;amp;utm_content=learning_hub" rel="noopener noreferrer"&gt;software testing&lt;/a&gt;. It provides a way to streamline the application testing process, eliminating the need for complex installation procedures and compatibility issues.&lt;/p&gt;

&lt;p&gt;It also enables developers to easily spin up multiple instances of an application in different environments, making it easier to identify and fix bugs before deployment.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Scaling With Reliability And Consistency&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Not only does Docker solve consistency issues, but it solves them at scale. Docker orchestration is the process of managing, deploying, and scaling Docker containers. With orchestration tools like Kubernetes, Docker Swarm, and Amazon ECS, we can easily manage many containers and automatically scale them up or down, depending on demand.&lt;/p&gt;

&lt;p&gt;This solves the scaling issue that arises when we need to handle a sudden increase in traffic or workload. With Docker orchestration, we can ensure that the containers are always running at optimal capacity, providing a seamless and efficient user experience.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Faster Feature Shipping&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;In the competitive world of software development, time-to-market and faster feature shipping can be the differentiating factor. Docker’s ability to package applications and dependencies into portable containers enables developers to ship features faster.&lt;/p&gt;

&lt;p&gt;By providing consistent and isolated code execution environments. This leads to increased efficiency and faster time-to-market.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Convert your data effortlessly with our &lt;a href="https://www.lambdatest.com/free-online-tools/csv-to-xml?utm_source=devto&amp;amp;utm_medium=organic&amp;amp;utm_campaign=jan_03&amp;amp;utm_term=bw&amp;amp;utm_content=free_online_tools" rel="noopener noreferrer"&gt;CSV to XML&lt;/a&gt; tool! Try it now&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2&gt;
  
  
  Docker Architecture
&lt;/h2&gt;

&lt;p&gt;Docker consists of a client-server architecture, where the Docker client communicates with the Docker daemon to build, run, and manage containers.&lt;/p&gt;

&lt;p&gt;Before we proceed with the installation and writing of tests, let’s first understand the Docker Architecture.&lt;/p&gt;

&lt;h2&gt;
  
  
  Docker Client
&lt;/h2&gt;

&lt;p&gt;The Docker client (docker) is the primary way many Docker users interact with Docker. When we use commands such as docker run, the client sends these commands to dockerd, which carries them out.&lt;/p&gt;

&lt;p&gt;The Docker command uses the Docker API. The Docker client can communicate with more than one daemon.&lt;/p&gt;

&lt;h2&gt;
  
  
  Docker Host
&lt;/h2&gt;

&lt;p&gt;Docker Host refers to a physical or virtual machine running the Docker engine. The Docker host provides the infrastructure needed to run Docker containers, such as the operating system, storage, networking, and other resources.&lt;/p&gt;

&lt;p&gt;The Docker daemon runs on the host machine and manages container images, networks, and storage volumes.&lt;/p&gt;

&lt;h2&gt;
  
  
  Docker Registry
&lt;/h2&gt;

&lt;p&gt;Docker Registry is a central storage for Docker images. Docker Hub is the preferred registry for Docker, but it’s entirely possible to have your private registry.&lt;/p&gt;

&lt;p&gt;Whenever we fire docker pull or push commands, the images are retrieved or stored from the Docker registry.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fcdn-images-1.medium.com%2Fmax%2F2000%2F1%2ALliWU-dRq-vHkxCFO4VHQw.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fcdn-images-1.medium.com%2Fmax%2F2000%2F1%2ALliWU-dRq-vHkxCFO4VHQw.png"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Docker Objects
&lt;/h2&gt;

&lt;p&gt;Working with Docker involves creating and using images, containers, networks, and volumes. It’s essential to understand these before we proceed.&lt;/p&gt;

&lt;h2&gt;
  
  
  Docker Images
&lt;/h2&gt;

&lt;p&gt;Docker images are read-only templates that contain everything needed to run an application, including the application code, runtime environment, libraries, and dependencies. Images are built using a &lt;em&gt;Dockerfile&lt;/em&gt;, which defines the components and configuration of the application.&lt;/p&gt;

&lt;p&gt;Images are stored in a registry and can be easily shared and reused across different environments. Pre-built images can also be downloaded from Docker Hub using the &lt;em&gt;docker pull&lt;/em&gt; command. A sample screenshot showing the download of the Python Docker image is below.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fcdn-images-1.medium.com%2Fmax%2F2000%2F1%2AMX_x3-NbendyghV_SNt4bA.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fcdn-images-1.medium.com%2Fmax%2F2000%2F1%2AMX_x3-NbendyghV_SNt4bA.png"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Docker Containers
&lt;/h2&gt;

&lt;p&gt;Docker containers are lightweight, standalone, and executable packages containing an application and all its dependencies (e.g., binaries, packages, libraries, etc.). Containers are created from images and run in a loosely isolated environment, providing isolation and security for the application.&lt;/p&gt;

&lt;p&gt;Containers can be easily started, stopped, and managed. They are also designed to be ephemeral, with no persistent state.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Transform &lt;a href="https://www.lambdatest.com/free-online-tools/yaml-to-json?utm_source=devto&amp;amp;utm_medium=organic&amp;amp;utm_campaign=jan_03&amp;amp;utm_term=bw&amp;amp;utm_content=free_online_tools" rel="noopener noreferrer"&gt;YAML to JSON&lt;/a&gt; seamlessly using our online converter! Get started today!&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2&gt;
  
  
  Docker Networking
&lt;/h2&gt;

&lt;p&gt;Docker networking enables containers to communicate with each other and the outside world, providing connectivity and flexibility for containerized applications. Containers can be connected to different networks, including default, custom, and overlay networks, depending on applications.&lt;/p&gt;

&lt;p&gt;Docker also provides various networking tools and plugins, including DNS, load balancing, and software-defined networking (SDN).&lt;/p&gt;

&lt;h2&gt;
  
  
  Docker Storage
&lt;/h2&gt;

&lt;p&gt;Docker storage offers a range of options for managing and persisting data in containers, including volumes, bind mounts, and &lt;em&gt;tmpfs&lt;/em&gt; mounts. Volumes are the preferred way to manage data in Docker, providing persistent storage that can be shared between containers and preserved even when a container is deleted.&lt;/p&gt;

&lt;p&gt;Docker also offers a range of storage plugins and drivers, including support for storage systems like Network File System(NFS), Amazon Elastic Block Store(EBS), and more.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;A Quick Word About Open Container Initiative (OCI)&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;As containerization gained popularity and its adoption started to increase, in 2015, a group of industry leaders, including Docker, CoreOS, and Red Hat, came together to form the OCI, or the Open Container Initiative.&lt;/p&gt;

&lt;p&gt;At the time, the container ecosystem was fragmented, with different vendors and platforms offering their proprietary container formats and runtimes. This made it difficult for developers to move containerized applications between different environments and added complexity to the containerization process.&lt;/p&gt;

&lt;p&gt;The goal of OCI is to standardize how containers are built and run across different platforms, making it easier to move containerized applications between different environments.&lt;/p&gt;

&lt;h2&gt;
  
  
  How To Install Docker On Windows?
&lt;/h2&gt;

&lt;p&gt;For this Playwright Docker tutorial, we are using a Windows machine for the demonstration. Some of the below-mentioned steps might not be applicable when using Docker on macOS.&lt;/p&gt;

&lt;p&gt;Here are the quick steps for &lt;a href="https://www.lambdatest.com/blog/run-selenium-tests-in-docker/#Howtoinstall?utm_source=devto&amp;amp;utm_medium=organic&amp;amp;utm_campaign=jan_03&amp;amp;utm_term=bw&amp;amp;utm_content=blog" rel="noopener noreferrer"&gt;installing Docker on Windows&lt;/a&gt;:&lt;/p&gt;

&lt;h2&gt;
  
  
  Enabling WSL2, Hyper-V, and Virtualization
&lt;/h2&gt;

&lt;p&gt;Based on the OS, Docker allows us the choice of the virtualization backend, i.e., we can either choose WSL2 (Windows Subsystem For Linux) or Hyper-V. Docker is designed to run on Linux-based machines. Hence, it runs without any additional macOS installation but needs the WSL installation on Windows.&lt;/p&gt;

&lt;p&gt;Hyper-V is a full-fledged virtualization technology that allows us to create and manage virtual machines on a Windows system, while WSL2 is a lightweight virtualization technology that enables running a Linux distribution natively on a Windows system, primarily for developers who want to use Linux tools and applications on a Windows system.&lt;/p&gt;

&lt;p&gt;With that out of the way, let’s see the Windows version we are running on. To check the version, navigate to Windows Explorer, right-click on PC, and click Properties. We are running a Windows 10 Home Edition as seen from the image below:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fcdn-images-1.medium.com%2Fmax%2F2000%2F0%2A9xS8UEzLXDKiT7ZH.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fcdn-images-1.medium.com%2Fmax%2F2000%2F0%2A9xS8UEzLXDKiT7ZH.png"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Based on the Windows version below is the choice of the virtualization backends.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;WLS2 Backend&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;Windows 11 64-bit: Home or Pro version 21H2 or higher, or Enterprise or Education version 21H2 or higher.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Windows 10 64-bit: Home or Pro 21H1 (build 19043) or higher, or Enterprise or Education 20H2 (build 19042) or higher.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Hyper-V Backend&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;Windows 11 64-bit: Pro version 21H2 or higher, or Enterprise or Education version 21H2 or higher.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Windows 10 64-bit: Pro 21H1 (build 19043) or higher, or Enterprise or Education 20H2 (build 19042) or higher.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The process to enable WSL2, Hyper-V, and Virtualization is similar. Open the Run (Windows + r) and type optionalfeatures.exe&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fcdn-images-1.medium.com%2Fmax%2F2000%2F0%2AAY1Yyl_8gRFMoEPk.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fcdn-images-1.medium.com%2Fmax%2F2000%2F0%2AAY1Yyl_8gRFMoEPk.png"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;On the subsequent screen that opens, based on the OS version, enable either Virtual Machine Platform and WSL2 or Hyper-V&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fcdn-images-1.medium.com%2Fmax%2F2000%2F0%2AxV596jhRqJv3Zxbo.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fcdn-images-1.medium.com%2Fmax%2F2000%2F0%2AxV596jhRqJv3Zxbo.png"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fcdn-images-1.medium.com%2Fmax%2F2000%2F0%2AZQIgNyR-aFAD7q72.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fcdn-images-1.medium.com%2Fmax%2F2000%2F0%2AZQIgNyR-aFAD7q72.png"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;This completes the prerequisites of WSL2 and the Hyper-V setup needed to install Docker. In the next step of this Playwright Docker tutorial, we proceed to install Docker Desktop, the application necessary for creating and managing Docker containers.&lt;/p&gt;

&lt;h2&gt;
  
  
  Installing Docker Desktop
&lt;/h2&gt;

&lt;p&gt;Docker Desktop is an application for creating and managing Docker containers on the computer. It provides a graphical user interface and command-line interface for working with containers. Let’s proceed with downloading Docker for windows Docker for windows. After downloading, follow the step-by-step installation guide.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;The downloaded installation package will be verified first.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fcdn-images-1.medium.com%2Fmax%2F2000%2F0%2Aduw9m4nWPLyc6D-n.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fcdn-images-1.medium.com%2Fmax%2F2000%2F0%2Aduw9m4nWPLyc6D-n.png"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Once the package verification is completed unpacking and installation will start.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fcdn-images-1.medium.com%2Fmax%2F2000%2F0%2AfHyI_6Bn2wtFxEzD.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fcdn-images-1.medium.com%2Fmax%2F2000%2F0%2AfHyI_6Bn2wtFxEzD.png"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;If everything goes well, we should see the following &lt;em&gt;Installation succeeded prompt&lt;/em&gt;.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fcdn-images-1.medium.com%2Fmax%2F2000%2F0%2AmzEWkpmZJicqWv2Y.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fcdn-images-1.medium.com%2Fmax%2F2000%2F0%2AmzEWkpmZJicqWv2Y.png"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;To use the Docker Desktop, we must sign up on Docker Hub and create an account. After signing up, start the Docker Desktop from the Windows start menu. Upon starting it, we need to log in using the account we created on Docker Hub.&lt;/p&gt;

&lt;p&gt;Once logged in, Docker will attempt to start, and we should see a screen as shown below:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fcdn-images-1.medium.com%2Fmax%2F3190%2F0%2AS0fCD_gK1Xr4xlm-.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fcdn-images-1.medium.com%2Fmax%2F3190%2F0%2AS0fCD_gK1Xr4xlm-.png"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Need to convert &lt;a href="https://www.lambdatest.com/free-online-tools/html-to-csv?utm_source=devto&amp;amp;utm_medium=organic&amp;amp;utm_campaign=jan_03&amp;amp;utm_term=bw&amp;amp;utm_content=free_online_tools" rel="noopener noreferrer"&gt;HTML to CSV&lt;/a&gt;? Our free tool makes it a breeze! Check it out now!&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2&gt;
  
  
  Troubleshooting Tip 💡 WSL1 To WSL2 Upgrade
&lt;/h2&gt;

&lt;p&gt;If the screen freezes at this point, i.e., Docker Desktop starting… prompt doesn’t go away, it’s most probably because the machine does not have the WSL 2. We need to upgrade from WSL1 to WSL2.&lt;/p&gt;

&lt;p&gt;Run the below set of commands in the Windows PowerShell command prompt or the newly launched Windows Terminal as per preference.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;wsl -l -v&lt;/em&gt; to view the WSL version. We can see WSL version 1 exists, but we need version 2.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fcdn-images-1.medium.com%2Fmax%2F2000%2F0%2AyQk5aafZnQ4HKkfv.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fcdn-images-1.medium.com%2Fmax%2F2000%2F0%2AyQk5aafZnQ4HKkfv.png"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;*wsl — update *(to update to version 2).&lt;/p&gt;

&lt;p&gt;&lt;em&gt;wsl –set-default-version 2&lt;/em&gt; (to set 2 as the default version).&lt;/p&gt;

&lt;p&gt;Once done, run the &lt;em&gt;wsl -l -v&lt;/em&gt; command again and restart the Docker desktop.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fcdn-images-1.medium.com%2Fmax%2F2168%2F0%2A8c9gCmYFGJCHLWFS.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fcdn-images-1.medium.com%2Fmax%2F2168%2F0%2A8c9gCmYFGJCHLWFS.png"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;After completing all the steps mentioned till this point, a successfully running Docker installation will look as shown in the below image.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fcdn-images-1.medium.com%2Fmax%2F3200%2F0%2AbNNKG6dxpcsYXcrq.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fcdn-images-1.medium.com%2Fmax%2F3200%2F0%2AbNNKG6dxpcsYXcrq.png"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Check the settings, and based on the Windows version, there will be options to select the backend. It defaults to WSL 2 as we are running Windows 10 Home version.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fcdn-images-1.medium.com%2Fmax%2F3200%2F0%2AXj_qUCawe4mHWcy_.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fcdn-images-1.medium.com%2Fmax%2F3200%2F0%2AXj_qUCawe4mHWcy_.png"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;em&gt;“Streamline your software development and integration process with the LambdaTest &lt;a href="https://www.lambdatest.com/docker-integration?utm_source=devto&amp;amp;utm_medium=organic&amp;amp;utm_campaign=jan_03&amp;amp;utm_term=bw&amp;amp;utm_content=webpage" rel="noopener noreferrer"&gt;Docker integration&lt;/a&gt;. Seamlessly incorporate software applications into your current workflows”&lt;/em&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Brief Introduction To Playwright
&lt;/h2&gt;

&lt;p&gt;Playwright is an end-to-end &lt;a href="https://www.lambdatest.com/web-testing?utm_source=devto&amp;amp;utm_medium=organic&amp;amp;utm_campaign=jan_03&amp;amp;utm_term=bw&amp;amp;utm_content=webpage" rel="noopener noreferrer"&gt;web testing&lt;/a&gt; and browser automation framework. It is immensely helpful for writing test cases for both modern, i.e., dynamic and static web apps.&lt;/p&gt;

&lt;p&gt;At the time of writing this Playwright Docker tutorial, the latest stable version of Playwright is 1.28.0, and Playwright is now consistently hitting the &amp;gt; 20K download per day mark, as seen from PyPi Stats.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fcdn-images-1.medium.com%2Fmax%2F2904%2F0%2AKaH3UnXhIvrBkJx5.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fcdn-images-1.medium.com%2Fmax%2F2904%2F0%2AKaH3UnXhIvrBkJx5.png"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Below are the download trends of Playwright compared to a popular alternative, Selenium, taken from &lt;a href="https://piptrends.com/compare/playwright-vs-selenium" rel="noopener noreferrer"&gt;Pip Trends&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fcdn-images-1.medium.com%2Fmax%2F3200%2F0%2Aq_ZwhL79oatxd46e.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fcdn-images-1.medium.com%2Fmax%2F3200%2F0%2Aq_ZwhL79oatxd46e.png"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;A key consideration to make when using any language, tool, or framework is the ease of its use.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Key features of Playwright&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;Playwright supports all major browsers, including Chromium, Firefox, Microsoft Edge, and WebKit&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Playwright’s APIs are available in various programming languages, such as Python, Java, JavaScript, TypeScript, and .NET&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Playwright is a cross-platform tool that works seamlessly on Windows, macOS, and Linux in both headless and non-headless modes&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Playwright’s auto-wait feature eliminates the need for artificial timeouts, making tests more robust and less prone to errors.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Playwright includes a codegen tool that helps locate elements with ease.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;a href="https://www.lambdatest.com/blog/playwright-locators/?utm_source=devto&amp;amp;utm_medium=organic&amp;amp;utm_campaign=jan_03&amp;amp;utm_term=bw&amp;amp;utm_content=blog" rel="noopener noreferrer"&gt;Playwright Locators&lt;/a&gt; provide easy APIs for finding elements on websites.&lt;br&gt;
&lt;iframe width="710" height="399" src="https://www.youtube.com/embed/wawbt1cATsk"&gt;
&lt;/iframe&gt;
&lt;br&gt;
In the next section of this Playwright Docker tutorial, we will learn how to set up Playwright for Python.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Setting Up Playwright For Python
&lt;/h2&gt;

&lt;p&gt;Playwright is a powerful tool that allows you to automate browser tasks such as filling out forms, clicking buttons, and scraping data from web pages. It supports multiple programming languages, including Python. In this Playwright Docker tutorial, we will go through the steps to set up Playwright for Python so you can start automating your browser tasks.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;First, create a dedicated folder for the project called &lt;em&gt;Docker Playwright&lt;/em&gt; (This step is not mandatory but is good practice).&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Inside the folder we just created, use the built-in &lt;em&gt;venv&lt;/em&gt; module to create a virtual environment named &lt;em&gt;docker playwright&lt;/em&gt;.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fcdn-images-1.medium.com%2Fmax%2F2000%2F0%2AzYJ6DnU9aaMznYGu.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fcdn-images-1.medium.com%2Fmax%2F2000%2F0%2AzYJ6DnU9aaMznYGu.png"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;Activate the virtual environment by calling the activate script.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Install the Playwright module using &lt;em&gt;pip install pytest-playwright&lt;/em&gt;.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Lastly, install the required browsers using &lt;em&gt;playwright install&lt;/em&gt;.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fcdn-images-1.medium.com%2Fmax%2F2242%2F0%2AlIX403_WxlIISELD.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fcdn-images-1.medium.com%2Fmax%2F2242%2F0%2AlIX403_WxlIISELD.png"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;All the tests in this Playwright Docker tutorial have been thoroughly tested on the following versions.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;Docker 20.10.22, build 3a2c30b&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Python 3.9.12&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Pytest 7.2.1&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Playwright 1.30.0&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fcdn-images-1.medium.com%2Fmax%2F2014%2F0%2A4YRN6Ld4jp_K8hl3.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fcdn-images-1.medium.com%2Fmax%2F2014%2F0%2A4YRN6Ld4jp_K8hl3.png"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Easily convert &lt;a href="https://www.lambdatest.com/free-online-tools/ascii-to-binary?utm_source=devto&amp;amp;utm_medium=organic&amp;amp;utm_campaign=jan_03&amp;amp;utm_term=bw&amp;amp;utm_content=free_online_tools" rel="noopener noreferrer"&gt;ASCII to Binary&lt;/a&gt; with our online tool! Give it a go!&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2&gt;
  
  
  Project Structure
&lt;/h2&gt;

&lt;p&gt;We will be using the &lt;a href="https://ecommerce-playground.lambdatest.io/" rel="noopener noreferrer"&gt;LambdaTest eCommerce Playground&lt;/a&gt; for writing the tests. Before moving to dockerizing them, let’s check each in detail and execute them locally without Docker. We will write a total of 8 tests designed to test various functions across 3 different pages on the test website.&lt;/p&gt;

&lt;p&gt;To make the &lt;a href="https://www.lambdatest.com/learning-hub/test-suite?utm_source=devto&amp;amp;utm_medium=organic&amp;amp;utm_campaign=jan_03&amp;amp;utm_term=bw&amp;amp;utm_content=learning_hub" rel="noopener noreferrer"&gt;test suite&lt;/a&gt; structured, easy to extend, and maintain, we will use the &lt;a href="https://www.lambdatest.com/learning-hub/playwright-page-object-model?utm_source=devto&amp;amp;utm_medium=organic&amp;amp;utm_campaign=jan_03&amp;amp;utm_term=bw&amp;amp;utm_content=learning_hub" rel="noopener noreferrer"&gt;Page Object Model (POM)&lt;/a&gt; design pattern to arrange the tests. Here is what the project directory structure looks like.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fcdn-images-1.medium.com%2Fmax%2F2000%2F0%2AgKZAFESaHE7VtfBo.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fcdn-images-1.medium.com%2Fmax%2F2000%2F0%2AgKZAFESaHE7VtfBo.png"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Let’s understand the structure in brief first&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;The &lt;em&gt;pages&lt;/em&gt; folder contains python files for each page. Each file contains a class we want to abstract This structure allows the code to remain organized and increases reusability.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;The &lt;em&gt;tests&lt;/em&gt; folder contains the test cases which we want to run. These are again segregated based on the page that they will be testing. The name of this folder is in accordance with the pytest requirement.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;The &lt;em&gt;utilities&lt;/em&gt; folder contains a single &lt;em&gt;utilities.py&lt;/em&gt; where we can add all the reusable supporting code.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;The &lt;em&gt;docker-compose.yaml&lt;/em&gt; contains the code to help build the Docker container effectively in case of a multi-container setup.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;The &lt;em&gt;Dockerfile&lt;/em&gt; contains the build of the Docker image from the code.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;The &lt;em&gt;requirements.txt&lt;/em&gt; contains all the Python packages we will need to run the code.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;em&gt;secrets.env&lt;/em&gt; is where we abstract the environment variables to control the test runs, browsers, operating systems used, etc.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Implementation (Fixtures &amp;amp; Configuration)
&lt;/h2&gt;

&lt;p&gt;Let us now look at the implementation of the environment file, configuration file, and fixtures file. The environment variables control the &lt;a href="https://www.lambdatest.com/learning-hub/test-execution?utm_source=devto&amp;amp;utm_medium=organic&amp;amp;utm_campaign=jan_03&amp;amp;utm_term=bw&amp;amp;utm_content=learning_hub" rel="noopener noreferrer"&gt;test execution&lt;/a&gt; environment.&lt;/p&gt;

&lt;p&gt;The configuration file contains reusable utilities and the special conftest.py file that allows us to define and share fixtures, hooks, and plugins across multiple test files.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;secrets.env&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;The first supporting file we need to run the code is the secrets.env. This file controls the environment we run the code on, i.e., local or cloud. Username and Access Key of the cloud grid provider. It also controls the Operating system and Browser where the tests will run.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fcdn-images-1.medium.com%2Fmax%2F2000%2F0%2A5bstBmhbJNBdOMdP.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fcdn-images-1.medium.com%2Fmax%2F2000%2F0%2A5bstBmhbJNBdOMdP.png"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;utilities.py&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;All the reusable code which supports the running of the tests and is called from multiple places within the main script is put in the &lt;em&gt;utilities.py&lt;/em&gt; file. The dotenv Python package reads key-value pairs from the .env file and sets them as an environment variable. This is done by calling &lt;em&gt;load_dotenv(‘secrets.env’)&lt;/em&gt;.&lt;/p&gt;

&lt;p&gt;The three lines after that are used to read the environment variables values, i.e., operating system, browser, and the environment, i.e., cloud or local. The tests will be defaulted to run locally.&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;from playwright.sync_api import Page
from dotenv import load_dotenv
import os


load_dotenv('secrets.env')
platform = os.getenv('PLATFORM')
browser = os.getenv('BROWSER')
run_on = os.getenv("RUN_ON") or 'local'
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;The &lt;em&gt;set_test_status&lt;/em&gt; function is used to set the results of the test execution when running the code on a cloud grid. When running locally, this function does not do anything.&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;def set_test_status(page: Page, status: str, remark: str):
    """
    Function to set stats, title and description of test run on cloud grid
    :param page: instance of Playwright browser page
    :param status: status of test e.g Pass or Failed
    :param remark: additional remark about test status
    """
    page.evaluate(
        "_ =&amp;gt; {}",
        "lambdatest_action: {"action": "setTestStatus", "arguments": {"status":"" + status + "", "remark": "" + remark + ""}}"
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;The capabilities dictionary is used to configure the &lt;a href="https://www.lambdatest.com/blog/what-is-test-environment/?utm_source=devto&amp;amp;utm_medium=organic&amp;amp;utm_campaign=jan_03&amp;amp;utm_term=bw&amp;amp;utm_content=blog" rel="noopener noreferrer"&gt;test environment&lt;/a&gt; when running on a cloud grid. The most notable configurations are the Platform, i.e., the Operating system and the browser. Both these are controlled using environment variables defined in secrets.env.&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;capabilities = {
    'browserName': browser,
    'browserVersion': 'latest',
    'LT:Options': {
        'platform': platform,
        'build': 'Playwright Docker Demo Build',
        'name': f'Playwright Docker Test For {platform} &amp;amp; {browser}',
        'user': os.getenv('LT_USERNAME'),
        'accessKey': os.getenv('LT_ACCESS_KEY'),
        'network': True,
        'video': True,
        'visual': True,
        'console': True,
        'tunnel': False,   # Add tunnel configuration if testing locally hosted webpage
        'tunnelName': '',  # Optional
        'geoLocation': '', # country code can be fetched from https://www.lambdatest.com/capabilities-generator/
    }
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;&lt;strong&gt;conftest.py&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Pytest fixtures are a powerful feature of the &lt;a href="https://www.lambdatest.com/pytest-testing?utm_source=devto&amp;amp;utm_medium=organic&amp;amp;utm_campaign=jan_03&amp;amp;utm_term=bw&amp;amp;utm_content=webpage" rel="noopener noreferrer"&gt;pytest testing&lt;/a&gt; framework that allows us to organize and manage the test code in a more efficient and reusable way. Essentially, &lt;a href="https://www.lambdatest.com/blog/end-to-end-tutorial-for-pytest-fixtures-with-examples/?utm_source=devto&amp;amp;utm_medium=organic&amp;amp;utm_campaign=jan_03&amp;amp;utm_term=bw&amp;amp;utm_content=blog" rel="noopener noreferrer"&gt;pytest fixtures&lt;/a&gt; are reusable pieces of code that provide data or resources to the test functions.&lt;/p&gt;

&lt;p&gt;This can include pre-populating a database with test data, creating and configuring objects that the tests depend on, or mocking external APIs to simulate certain behaviors.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;conftest.py&lt;/em&gt; is a special file in pytest that allows us to define fixtures and other configuration options that can be shared across multiple tests. This naming convention is mandatory; otherwise, pytest will not be able to locate the fixtures.&lt;/p&gt;

&lt;p&gt;By placing conftest.py in the tests directory, we can make the fixtures and configuration options defined in it available to all test functions and modules in that directory and its subdirectories.&lt;/p&gt;

&lt;p&gt;Fixtures are defined using the same syntax as a normal function and decorating the function with the &lt;em&gt;@pytest.fixture decorator&lt;/em&gt;. Optionally we can also give the fixture a name. In this case, we have given it the name browser because we are using it to return a Playwright browser instance.&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;import pytest
import os
from playwright.sync_api import sync_playwright
from utilities.utilities import capabilities, run_on
from pages import homepage, accountpage, productpage
import subprocess
import urllib
import json




@pytest.fixture(name="browser")
def playwright_browser():
    print(run_on)
    if run_on == 'local':
        with sync_playwright() as playwright:
            browser = playwright.chromium.launch(headless=True)
            yield browser

    if run_on == 'cloud':
        with sync_playwright() as playwright:
            playwrightVersion = str(subprocess.getoutput('playwright --version')).strip().split(" ")[1]
            capabilities['LT:Options']['playwrightClientVersion'] = playwrightVersion        
            lt_cdp_url = 'wss://cdp.lambdatest.com/playwright?capabilities=' + urllib.parse.quote(json.dumps(capabilities))
            browser = playwright.chromium.connect(lt_cdp_url)
            yield browser      
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;The next three fixtures we have defined are for providing a page instance. All three fixtures take in the &lt;em&gt;fixture&lt;/em&gt; defined above as input and create a Playwright page instance. Using this Page instance, we initialize the respective Page Object and yield it.&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;@pytest.fixture
def account_page(browser):
    page = browser.new_context().new_page()    
    account_page = accountpage.AccountPage(page)
    yield account_page


@pytest.fixture
def home_page(browser):
    page = browser.new_context().new_page()    
    home_page = homepage.HomePage(page)
    yield home_page


@pytest.fixture
def product_page(browser):
    page = browser.new_context().new_page()    
    product_page = productpage.ProductPage(page)
    yield product_page
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;This completes the setup code walkthrough, and we are now ready to see the actual tests.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Want to convert &lt;a href="https://www.lambdatest.com/free-online-tools/ascii-to-hex?utm_source=devto&amp;amp;utm_medium=organic&amp;amp;utm_campaign=jan_03&amp;amp;utm_term=bw&amp;amp;utm_content=free_online_tools" rel="noopener noreferrer"&gt;ASCII to Hex&lt;/a&gt;? Try our simple online converter now&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2&gt;
  
  
  Implementation (POM &amp;amp; Tests)
&lt;/h2&gt;

&lt;p&gt;The test suite is going to consist of 8 tests in total which test functionality of 3 different pages:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;Home page&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;My account page&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Product page&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;As shown in the project structure, we will create a .py file under pages directory, which represents the class object for each page under test. So, homepage.py will define the code corresponding to the Home page, accountpage.py will define the code corresponding to the My account page, and productpage.py will define the code corresponding to the Product page.&lt;/p&gt;

&lt;p&gt;This approach ensures that the code remains organized, is easy to maintain, and also easy to extend for adding more tests in the future.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Test Scenarios (Homepage)&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;We will write two test scenarios for the Home page, defined below.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;The Home page should contain the ‘SEARCH’ button and be visible.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;The Home page should contain the ‘My account’ link and URL to navigate to the Account section.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Code Walkthrough (pages/homepage.py)&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;On inspecting the Home page, we can see that the ‘SEARCH’ has a &lt;strong&gt;button&lt;/strong&gt; role.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fcdn-images-1.medium.com%2Fmax%2F3200%2F1%2A_Bt8U9SvP3KtlyaYnbMRrw.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fcdn-images-1.medium.com%2Fmax%2F3200%2F1%2A_Bt8U9SvP3KtlyaYnbMRrw.png"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;And that ‘My account’ is a &lt;em&gt;link&lt;/em&gt; that contains the &lt;em&gt;href&lt;/em&gt; attribute.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fcdn-images-1.medium.com%2Fmax%2F3200%2F1%2AONWaemyQ1oAGtIfGBI2uzg.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fcdn-images-1.medium.com%2Fmax%2F3200%2F1%2AONWaemyQ1oAGtIfGBI2uzg.png"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;from playwright.sync_api import expect
from utilities.utilities import set_test_status


class HomePage:  
   def __init__(self, page):
        self.page = page
        self.search_button_locator = page.get_by_role(role="button", name="Search")
        self.myaccount_link_locator = page.get_by_role(role="link", name="My Account")
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;&lt;strong&gt;Step 1:&lt;/strong&gt; We define a class HomePage where we will group all the code.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Step 2:&lt;/strong&gt; The &lt;em&gt;&lt;strong&gt;init&lt;/strong&gt;.py&lt;/em&gt; method takes an instance of a Playwright page as a parameter, and using that, we define two Playwright locators as instance attributes.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Step 3:&lt;/strong&gt; Search has a button role with the name ‘Search’ we use the &lt;em&gt;get_by_role()&lt;/em&gt; locator to locate it and store it in an instance attribute &lt;em&gt;search_button_locato&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Step 4:&lt;/strong&gt; My account has a link role with the name ‘My Account’ we can again use &lt;em&gt;get_by_role()&lt;/em&gt; locator to locate it and store it in an instance attribute &lt;em&gt;myaccount_link_locator&lt;/em&gt;&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;def navigate(self):
        self.page.goto("https://ecommerce-playground.lambdatest.io/")
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;&lt;em&gt;navigate()&lt;/em&gt; method defined in the HomePage class calls the &lt;em&gt;goto()&lt;/em&gt; method on the Playwright page instance and navigates to the home page URL supplied to it&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;def search_button(self):                
        try:
            expect(self.search_button_locator).to_be_visible()
            set_test_status(self.page, "Passed", "Search button is visible")
        except Exception as ex:
            set_test_status(self.page, "Failed", "Search button is not visible")
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;&lt;strong&gt;Step 1:&lt;/strong&gt; The search_button() method then calls the Playwright &lt;em&gt;except() *assertion to verify if the element located by *search_button_locator&lt;/em&gt; is visible&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Step 2:&lt;/strong&gt; The &lt;em&gt;set_test_status()&lt;/em&gt; method defined in the utilities.py is called then to inform the cloud grid of the status of the test if running on cloud grid&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;def myaccount_link(self):        
        try:
            expect(self.myaccount_link_locator).to_have_attribute("href", "https://ecommerce-playground.lambdatest.io/index.php?route=account/account")
            set_test_status(self.page, "Passed", "My account link is visible")
        except Exception as ex:
            set_test_status(self.page, "Failed", "My account link is not visible")
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;&lt;strong&gt;Step 1:&lt;/strong&gt; The myaccount_link() method then calls the Playwright except() assertion on the instance attribute myaccount_link_locator to expect it to have href attribute with the link for account section. You can learn more about it through this tutorial on &lt;a href="https://www.lambdatest.com/learning-hub/playwright-assertions?utm_source=devto&amp;amp;utm_medium=organic&amp;amp;utm_campaign=jan_03&amp;amp;utm_term=bw&amp;amp;utm_content=learning_hub" rel="noopener noreferrer"&gt;Playwright assertions&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Step 2:&lt;/strong&gt; The &lt;em&gt;set_test_status()&lt;/em&gt; method defined in the utilities.py is called to inform the cloud grid of the status of the test if running on the cloud grid.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Code Walkthrough (tests/test_homepage.py)&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;The class file &lt;em&gt;homepage.py&lt;/em&gt; contains all the location and assertion codes. All that remains to write in the &lt;em&gt;test_homepage.py&lt;/em&gt; is to call the methods defined by creating an object of the Homepage class. We have abstracted this object creation in the &lt;em&gt;fixture home_page()&lt;/em&gt; defined in the &lt;em&gt;conftest.py&lt;/em&gt; discussed above.&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;from playwright.sync_api import expect
from utilities.utilities import set_test_status
from pages.homepage import HomePage


def test_homepage_contains_search_button(browser, home_page):
    """
    Test to verify if search button exists on the home page
    """    
    home_page.navigate()
    home_page.search_button()
    browser.close()   
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;&lt;strong&gt;Step 1:&lt;/strong&gt; The &lt;em&gt;test_homepage_contains_search_button()&lt;/em&gt; takes in browser and home_page fixtures.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Step 2:&lt;/strong&gt; Using the &lt;em&gt;home_page&lt;/em&gt;, we call the &lt;em&gt;navigate()&lt;/em&gt; method to open the home page.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Step 3:&lt;/strong&gt; On the opened homepage, call the &lt;em&gt;search_button()&lt;/em&gt; method, which contains the test logic.&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;def test_homepage_contains_myaccount_link(browser, home_page):
    """
    Test to verify if my account link exists on the home page
    """        
    home_page.navigate()  
    home_page.myaccount_link()            
    browser.close()
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;&lt;strong&gt;Step 1:&lt;/strong&gt; The takes in browser and &lt;em&gt;home_page&lt;/em&gt; fixtures.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Step 2:&lt;/strong&gt; Using the &lt;em&gt;home_page&lt;/em&gt;, we call the &lt;em&gt;navigate()&lt;/em&gt; method to open the home page.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Step 3:&lt;/strong&gt; On the opened homepage, call the &lt;em&gt;myaccount_link()&lt;/em&gt; method, which contains the test logic.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Step 4:&lt;/strong&gt; We close the browser by calling &lt;em&gt;browser.close()&lt;/em&gt; to ensure the release of resources.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Test Scenarios (Account Page)&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;We will write three test scenarios for the account page defined below.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;The Account page should contain a registration link for new customers.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;The Account page should contain the Email &amp;amp; Password field in the login form for returning customers.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;The Account page should contain the Forgotten Password link.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Code Walkthrough (pages/account.py)&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;On inspecting the account page, we can see that New Customer Registration has a role link with the name &lt;em&gt;Continue&lt;/em&gt; and &lt;em&gt;href&lt;/em&gt; attribute to redirect to registration.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fcdn-images-1.medium.com%2Fmax%2F3200%2F1%2ACFrsbqINzPmWlhtP_ciSEg.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fcdn-images-1.medium.com%2Fmax%2F3200%2F1%2ACFrsbqINzPmWlhtP_ciSEg.png"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The Returning Customer form has fields for email &amp;amp; password with placeholders ‘E-Mail Address’ and ‘Password’.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fcdn-images-1.medium.com%2Fmax%2F3200%2F1%2ApdWZrFHh2K87-SXUNr7P4w.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fcdn-images-1.medium.com%2Fmax%2F3200%2F1%2ApdWZrFHh2K87-SXUNr7P4w.png"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Inspecting the Forgotten Password shows that it has the role link with the href attribute redirecting to the forgotten password.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fcdn-images-1.medium.com%2Fmax%2F3200%2F1%2AXVJ-b9rK7662J1pUS5vpvg.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fcdn-images-1.medium.com%2Fmax%2F3200%2F1%2AXVJ-b9rK7662J1pUS5vpvg.png"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;from playwright.sync_api import expect
from utilities.utilities import set_test_status


class AccountPage:

    def __init__(self, page):
        self.page = page
        self.new_registration_link_locator =  page.get_by_role(role="link", name="Continue")
        self.email_field_locator = page.get_by_placeholder("E-Mail Address")
        self.password_field_locator =  page.get_by_placeholder("Password")
        self.forgotten_password_link_locator = page.get_by_role(role="link", name="Forgotten Password", exact=True)
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;&lt;strong&gt;Step 1:&lt;/strong&gt; We define a class AccountPage where we will group all the tests related to the My Account page.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Step 2:&lt;/strong&gt; The &lt;strong&gt;&lt;strong&gt;init&lt;/strong&gt;.py&lt;/strong&gt; method takes in an instance of a Playwright page as a parameter, and using that, we define four Playwright locators in it as instance attributes.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Step 3:&lt;/strong&gt; New Registration has a link role with the name ‘Continue’, so we use the get_by_role() Playwright locator to locate it and store it in an instance attribute new_registration_link_locator.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Step 4:&lt;/strong&gt; Since the login form field email has a placeholder ‘E-Mail Address,’ we use the Playwright locator get_by_placeholder() and store the located element in an instance attribute email_field_locator.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Step 5:&lt;/strong&gt; The login form field password has a placeholder ‘password’, so we use the Playwright locator get_by_placeholder() and store the located element in an instance attribute password_field_locator.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Step 6:&lt;/strong&gt; Also, the ‘Forgotten Password’ has a link role, so we can use the get_by_role() Playwright locator and store it in an instance attribute forgotten_password_link_locator.&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;def navigate(self):
        self.page.goto("https://ecommerce-playground.lambdatest.io/index.php?route=account/login")
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;The navigate() method defined in the AccountPage class calls the goto() method on the Playwright page instance and navigates to the account page URL supplied to it.&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;def new_registration(self):                
        try:
             expect(self.new_registration_link_locator).to_have_attribute("href", "https://ecommerce-playground.lambdatest.io/index.php?route=account/register")
             set_test_status(self.page, "Passed", "Registration link is visible")
        except Exception as ex:
            set_test_status(self.page, "Failed", "Registration link is not visible")
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;&lt;strong&gt;Step 1:&lt;/strong&gt; The &lt;em&gt;new_registration()&lt;/em&gt; method then calls the Playwright except() assertion on the instance attribute &lt;em&gt;new_registration_link_locator&lt;/em&gt; and verifies if it has an href attribute and the link points to the registration endpoint.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Step 2:&lt;/strong&gt; The &lt;em&gt;set_test_status()&lt;/em&gt; method defined in the utilities.py is called to inform the cloud grid of the status of the test if running on the cloud grid.&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;def login_form(self):                
        try:
            expect(self.email_field_locator).to_be_visible()
            expect(self.password_field_locator).to_be_visible()
            set_test_status(self.page, "Passed", "Login form contains email &amp;amp; password fields")
        except Exception as ex:
            set_test_status(self.page, "Failed", "Login form does not contains email &amp;amp; password fields")
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;&lt;strong&gt;Step 1:&lt;/strong&gt; The &lt;em&gt;login_form()&lt;/em&gt; method is where we use the expect() Playwright assertion to verify that the elements located by email_field_locator and password_field_locator are visible.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Step 2:&lt;/strong&gt; The &lt;em&gt;set_test_status()&lt;/em&gt; method defined in the utilities.py is called to inform the cloud grid of the test status if running on the cloud grid.&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;def forgotten_password(self):                
        try:
         expect(self.forgotten_password_link_locator).to_have_attribute("href","https://ecommerce-playground.lambdatest.io/index.php?route=account/forgotten")    
            set_test_status(self.page, "Passed", "Forgotten Password link is visible")
        except Exception as ex:
            set_test_status(self.page, "Passed", "Forgotten Password link is not visible")
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;&lt;strong&gt;Step 1:&lt;/strong&gt; The forgotten_password() method is where we expect() that the located forgotten_password_link_locator to have an href attribute with the specific link.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Step 2:&lt;/strong&gt; The set_test_status() method defined in the utilities.py is called to inform the cloud grid of the status of the test if running on the cloud grid.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Code Walkthrough (tests/test_accountpage.py)&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;The class file accountpage.py contains all the element locations and assertion code. All that remains to write in the test_accountpage.py is to call the methods defined by creating objects of the class AccountPage.&lt;/p&gt;

&lt;p&gt;We have abstracted this object creation in the fixture account_page() defined in the conftest.py discussed above.&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;from playwright.sync_api import expect
from utilities.utilities import set_test_status
from pages.accountpage import AccountPage


def test_myaccount_page_contains_registration_link(browser, account_page):
    """
    Test to verify if new customer registration link exists on account page
    """
    account_page.navigate()
    account_page.new_registration()
    browser.close()
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;&lt;strong&gt;Step 1:&lt;/strong&gt; The &lt;em&gt;test_myaccount_page_contains_registration_link()&lt;/em&gt; takes in browser and account_page fixtures as parameters and using account_page, we call the navigate() method to open the account page.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Step 2:&lt;/strong&gt; This is followed by calling the new_registration() method, where we assert the registration link to be present.&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;def test_myaccount_page_contains_login_form_with_email_and_password(browser, account_page):
    """
    Test to verify if customer login form exists on account page and contains email and password fields
    """      
    account_page.navigate()  
    account_page.login_form()            
    browser.close()
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;&lt;strong&gt;Step 1:&lt;/strong&gt; The &lt;em&gt;test_myaccount_page_contains_login_form_with_email_and_password()&lt;/em&gt; takes in browser and account_page fixtures as parameters.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Step 2:&lt;/strong&gt; We use the account_page, which is nothing but an instance of the AccountPage class, to call the navigate() method to open the account page.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Step 3:&lt;/strong&gt; This is followed by calling the login_form() method, where we assert email &amp;amp; password fields to be visible to be present.&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;def test_myaccount_page_contains_forgotten_password_link(browser, account_page):
    """
    Test to verify if forgotten password link exists on account login page
    """      
    account_page.navigate()  
    account_page.forgotten_password()            
    browser.close()
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;&lt;strong&gt;Step 1:&lt;/strong&gt; The &lt;em&gt;test_myaccount_page_contains_forgotten_password_link()&lt;/em&gt; takes in browser and account_page fixtures as parameters.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Step 2:&lt;/strong&gt; We use the account_page, which is nothing but an instance of the AccountPage class, to call the navigate() method to open the account page.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Step 3:&lt;/strong&gt; This is followed by calling the forgotten_password() method, where we assert it to be a link with the href attribute.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Step 4:&lt;/strong&gt; Close the browser by calling the browser.close() ensures the release of resources.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Test Scenarios (Product Page)&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;We will write three test scenarios for the product page defined below.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;The Product page should contain an ‘Add To Cart’ button.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;The Product page should contain a ‘Buy Now’ button.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;The Product page should contain a checkout where the option to use coupon code exists.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Code Walkthrough (pages/productpage.py)&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Inspection of ‘ADD TO CART’ shows that it has a button role and is named ‘Add to Cart’&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fcdn-images-1.medium.com%2Fmax%2F3200%2F1%2AHPDXxKty6BsTOmzkREQfHA.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fcdn-images-1.medium.com%2Fmax%2F3200%2F1%2AHPDXxKty6BsTOmzkREQfHA.png"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Inspection of ‘BUY NOW’ shows that it has a button role and is named ‘Buy now’.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fcdn-images-1.medium.com%2Fmax%2F3200%2F1%2AgZNp3lpXF27xpHcFTXn62w.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fcdn-images-1.medium.com%2Fmax%2F3200%2F1%2AgZNp3lpXF27xpHcFTXn62w.png"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Inspection of the coupon code input field shows that it has a placeholder ‘Enter your coupon here’.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fcdn-images-1.medium.com%2Fmax%2F3200%2F1%2As7KC6EHCJzm9_ETxeOAJ1A.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fcdn-images-1.medium.com%2Fmax%2F3200%2F1%2As7KC6EHCJzm9_ETxeOAJ1A.png"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Based on the inspection, we can now write the page object ProductPage which abstracts all details necessary to run the tests.&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;from playwright.sync_api import expect
from utilities.utilities import set_test_status
import re


class ProductPage:

    def __init__(self, page):
        self.page = page
        self.addtocart_button_locator = page.get_by_role(role="button", name=re.compile("add to cart", re.IGNORECASE))
        self.buynow_button_locator = page.get_by_role(role="button", name=re.compile("buy now", re.IGNORECASE))
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;&lt;strong&gt;Step 1:&lt;/strong&gt; The page object class is called ProductPage since it deals with tests for information on the product.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Step 2:&lt;/strong&gt; The &lt;em&gt;&lt;strong&gt;init&lt;/strong&gt;.py&lt;/em&gt; method takes in an instance of a Playwright page as a parameter, and using that, we define two Playwright locators as instance attributes.&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;def navigate(self):
        self.page.goto("https://ecommerce-playground.lambdatest.io/index.php?route=product/product&amp;amp;path=34&amp;amp;product_id=28")
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;The &lt;em&gt;navigate()&lt;/em&gt; method remains the same as previous tests, and the only difference is it takes in a specific product URL.&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;def add_to_cart(self):                
        try:
            expect(self.addtocart_button_locator).to_be_visible()
            set_test_status(self.page, "Passed", "Add To Cart button is visible")
        except Exception as ex:
            set_test_status(self.page, "Failed", "Add To Cart button is not visible")
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;&lt;strong&gt;Step 1:&lt;/strong&gt; The &lt;em&gt;add_to_cart()&lt;/em&gt; method uses the Playwright &lt;em&gt;expect()&lt;/em&gt; construct to assert the element located by the addtocart_button_locator to be visible.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Step 2:&lt;/strong&gt; The &lt;em&gt;set_test_status()&lt;/em&gt; method defined in the utilities.py is called to inform the cloud grid of the test status if running on the cloud grid.&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;def buy_now(self):                
        try:
            expect(self.buynow_button_locator).to_be_visible()
            set_test_status(self.page, "Passed", "Buy Now button is visible")
        except Exception as ex:
            set_test_status(self.page, "Failed", "Buy Now button is visible")
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;&lt;strong&gt;Step 1:&lt;/strong&gt; &lt;em&gt;The buy_now()&lt;/em&gt; method is similar to &lt;em&gt;add_to_cart()&lt;/em&gt; the only difference being it verifies the element located by buynow_button_locator to be visible.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Step 2:&lt;/strong&gt; The &lt;em&gt;set_test_status()&lt;/em&gt; method defined in the &lt;em&gt;utilities.py&lt;/em&gt; is called to inform the cloud grid of the test status.&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;def checkout_product(self):                
        try:            
            # Add a product to cart first &amp;amp; click checkout
            self.page.goto("https://ecommerce-playground.lambdatest.io/")
            self.page.get_by_role("button", name="Shop by Category").click()
            self.page.get_by_role("link", name="MP3 Players").click()
            self.page.get_by_role("link", name="HTC Touch HD HTC Touch HD HTC Touch HD HTC Touch HD").click()
            self.page.get_by_role("button", name="Add to Cart").click()
            self.page.get_by_role("link", name="Checkout ").click()

            # Perform the test on the checkout
            coupon_code_locator = self.page.get_by_placeholder("Enter your coupon here")
            expect(coupon_code_locator).to_be_visible()
            set_test_status(self.page, "Passed", "Use Coupon Code option is visible")
        except Exception as ex:
            set_test_status(self.page, "Failed", "Use Coupon Code option is visible")
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;&lt;strong&gt;Step 1:&lt;/strong&gt; The &lt;em&gt;checkout_product()&lt;/em&gt; method differs from the other methods we have written so far in the sense that we first need to add a product to the cart and checkout.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Step 2:&lt;/strong&gt; We start this method by navigating to the homepage of the test website using &lt;em&gt;page.goto()&lt;/em&gt;.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Step 3:&lt;/strong&gt; Once on the homepage, we locate the product, and on the located product, use the &lt;em&gt;Playwright click()&lt;/em&gt; action to add it to the cart.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Step 4:&lt;/strong&gt; On adding to the cart, we again use the Playwright click() action to check out the product.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Step 5:&lt;/strong&gt; On checkout, we create a locator using the &lt;em&gt;get_by_placeholder()&lt;/em&gt; Playwright locator to locate the coupon code text box and store it in a variable &lt;em&gt;coupon_code_locator&lt;/em&gt;.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Step 6:&lt;/strong&gt; We then &lt;em&gt;expect()&lt;/em&gt; the element located by &lt;em&gt;coupon_code_locator to_be_visible()&lt;/em&gt;.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Step 7:&lt;/strong&gt; The &lt;em&gt;set_test_status()&lt;/em&gt; method defined in the &lt;em&gt;utilities.py&lt;/em&gt; is called to inform the cloud grid of the test status if running on the cloud grid.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Code Walkthrough (tests/test_productpage.py)&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;The class file &lt;em&gt;productpage.py&lt;/em&gt; contains all of the element location and assertion code. All that remains to write in the &lt;em&gt;test_productpage.py&lt;/em&gt; is to call the methods defined by creating objects of the class &lt;em&gt;ProductPage&lt;/em&gt;.&lt;/p&gt;

&lt;p&gt;We have abstracted this object creation in the fixture &lt;em&gt;product_page()&lt;/em&gt; defined in the &lt;em&gt;conftest.py&lt;/em&gt; discussed above.&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;from playwright.sync_api import expect
from utilities.utilities import set_test_status
from pages.productpage import ProductPage


def test_product_page_contains_addtocart_button(browser, product_page):
    """
    Test to verify if product page has "add to cart" button
    """    
    product_page.navigate()
    product_page.add_to_cart()
    browser.close() 
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;&lt;strong&gt;Step 1:&lt;/strong&gt; The &lt;em&gt;test_product_page_contains_addtocart_button()&lt;/em&gt; calls the &lt;em&gt;navigate()&lt;/em&gt; method using the product_page object to open the product page.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Step 2:&lt;/strong&gt; Calling the &lt;em&gt;add_to_cart()&lt;/em&gt; then executes the &lt;em&gt;expect()&lt;/em&gt; assertion written in it.&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;def test_product_page_contains_buynow_button(browser, product_page):
    """
    Test to verify if product page has "buy now" button
    """    
    product_page.navigate()  
    product_page.buy_now()            
    browser.close()
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;&lt;strong&gt;Step 1:&lt;/strong&gt; The &lt;em&gt;test_product_page_contains_buynow_button()&lt;/em&gt; calls the &lt;em&gt;navigate()&lt;/em&gt; method using the product_page object to open the product page.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Step 2:&lt;/strong&gt; Calling the &lt;em&gt;buy_now()&lt;/em&gt; then executes the &lt;em&gt;expect()&lt;/em&gt; assertion written in it.&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;def test_checkout_page_contains_use_couponcode_option(browser, product_page):
    """
    Test to verify if product page has "use coupon code" option is available on checkout page
    """    
    product_page.checkout_product()            
    browser.close()
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;The &lt;em&gt;test_checkout_page_contains_use_couponcode_option()&lt;/em&gt; calls directly the &lt;em&gt;checkout_product()&lt;/em&gt; method where we execute the &lt;em&gt;expect()&lt;/em&gt; assertion written in it.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Execution:&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Now that we’ve written 8 comprehensive test cases, let’s run them first without dockerizing them to ensure they are working correctly. All the tests are saved in a file test.py, and to run them, we run the pytest -v test.py command. The -v flag is for verbose output. As we can see from the screenshot below, all test cases are running perfectly.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fcdn-images-1.medium.com%2Fmax%2F3200%2F1%2AM8jMeWBdCioi08m9iW9tPg.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fcdn-images-1.medium.com%2Fmax%2F3200%2F1%2AM8jMeWBdCioi08m9iW9tPg.png"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;As the number of tests increases, we can leverage the multi-core CPU to run tests in parallel using the &lt;em&gt;–numprocesses &lt;/em&gt; option of pytest. The performance and number of parallel processes we can spawn depend on the configuration of the host CPU.&lt;/p&gt;

&lt;p&gt;For demo purposes, since we have 8 tests, we will set it to 3 to run 3 tests at a time. As you can see from the image below 3 different workers were spawned, and tests were run parallel depending on the availability of the workers. Parallel execution is an excellent way to speed up test execution for larger test suites.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fcdn-images-1.medium.com%2Fmax%2F2378%2F1%2AISV4ohy61MdoGq4jMJWviA.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fcdn-images-1.medium.com%2Fmax%2F2378%2F1%2AISV4ohy61MdoGq4jMJWviA.png"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Freezing The Python Requirements
&lt;/h2&gt;

&lt;p&gt;Now that the tests are perfectly running locally, before proceeding to dockerizing the tests, we will export all the Python dependencies we installed in the virtual environment in the &lt;em&gt;requirements.txt&lt;/em&gt; file. This is a standard process in the Python world to share dependencies. Further reading about freezing requirements is available in the official pip documentation.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fcdn-images-1.medium.com%2Fmax%2F2000%2F1%2AFSZLZHX1AkhTkIsx0JBvTQ.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fcdn-images-1.medium.com%2Fmax%2F2000%2F1%2AFSZLZHX1AkhTkIsx0JBvTQ.png"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Below are the contents of the generated requirements.txt file, which contains the dependency name and the version. Now, we can share this with other team members and colleagues to ensure everyone uses the same Python libraries.&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;attrs==22.2.0
certifi==2022.12.7
charset-normalizer==3.0.1
colorama==0.4.6
exceptiongroup==1.1.0
greenlet==2.0.1
idna==3.4
iniconfig==2.0.0
packaging==23.0
playwright==1.30.0
pluggy==1.0.0
pyee==9.0.4
pytest==7.2.1
pytest-base-url==2.0.0
pytest-playwright==0.3.0
python-dotenv==0.21.1
python-slugify==8.0.0
requests==2.28.2
text-unidecode==1.3
tomli==2.0.1
typing_extensions==4.4.0
urllib3==1.26.14
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;
&lt;h2&gt;
  
  
  Running Playwright Docker Tests Locally
&lt;/h2&gt;

&lt;p&gt;Running Playwright Docker tests locally allow you to test your Playwright scripts in a containerized environment, which can help ensure consistency and reproducibility across different environments.&lt;/p&gt;

&lt;p&gt;In this Playwright Docker tutorial, we will go through the steps to set up and run Playwright with Docker on your local machine. We will cover how to create a Dockerfile, build a Docker image, and run a container with Playwright and Python installed. By the end of this Playwright Docker tutorial, you will have a working setup to run Playwright in a Docker container on your local machine.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Simplify your &lt;a href="https://www.lambdatest.com/free-online-tools/css-to-sass?utm_source=devto&amp;amp;utm_medium=organic&amp;amp;utm_campaign=jan_03&amp;amp;utm_term=bw&amp;amp;utm_content=free_online_tools" rel="noopener noreferrer"&gt;CSS to SASS&lt;/a&gt; conversion with our free tool! Start now!&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h2&gt;
  
  
  Writing The Docker File
&lt;/h2&gt;

&lt;p&gt;Now that the Playwright Docker tests are working locally, let’s move on to converting them to a Docker image. For that, we will first need a Docker file. By convention, the name of the file is &lt;strong&gt;&lt;em&gt;Dockerfile.&lt;/em&gt;&lt;/strong&gt; We will create this in the project’s root directory, i.e., outside the tests folder.&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;FROM mcr.microsoft.com/playwright/python:v1.30.0-focal


WORKDIR /app


COPY pages /app/pages
COPY tests /app/tests
COPY utilities /app/utilities


COPY requirements.txt /app/


RUN pip install --no-cache-dir --upgrade pip   &amp;amp;&amp;amp; pip install --no-cache-dir -r requirements.txt
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;&lt;strong&gt;Walkthrough Of DockerFile&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;The first line tells Docker to pull the Python Playwright Docker image from Docker Hub.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;We create a working directory called /app.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;We copy the pages directory, which contains all the object representations of the web pages into &lt;em&gt;/app/pages&lt;/em&gt;.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;This is followed by copying the &lt;em&gt;tests&lt;/em&gt; and the &lt;em&gt;utilities&lt;/em&gt; directories to &lt;em&gt;/app/tests&lt;/em&gt; and &lt;em&gt;/app/utilities&lt;/em&gt;, respectively.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Next, we copy the &lt;em&gt;requirements.txt&lt;/em&gt; into the Docker container.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;We upgrade pip and install the dependencies from the &lt;em&gt;requirements.txt&lt;/em&gt; file we copied in the above step.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Building The Docker Image
&lt;/h2&gt;

&lt;p&gt;The next step is building the Docker image. We talked about what an image is earlier in this Playwright Docker tutorial. The command to build the image is below.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;&lt;em&gt;docker build -t playwright-docker&lt;/em&gt;&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;The output of the process is shown below.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fcdn-images-1.medium.com%2Fmax%2F3200%2F1%2Awy_2tT_oZ_pk2lhGL25Ehg.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fcdn-images-1.medium.com%2Fmax%2F3200%2F1%2Awy_2tT_oZ_pk2lhGL25Ehg.png"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;&lt;em&gt;docker build&lt;/em&gt; is the command to build images.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;-t is shorthand for name and optionally a tag to the resulting image being built. We have chosen &lt;em&gt;playwright-docker&lt;/em&gt;, and since the tag, which can also be called version, is omitted will be defaulted to the latest.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;The period ‘.’ tells Docker that the Dockerfile is in the current directory.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The created image can be viewed by using the docker image ls command.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fcdn-images-1.medium.com%2Fmax%2F2000%2F1%2AtDRK8aDqrDXrTfvNXNrw4w.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fcdn-images-1.medium.com%2Fmax%2F2000%2F1%2AtDRK8aDqrDXrTfvNXNrw4w.png"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Running The Tests In Docker Container
&lt;/h2&gt;

&lt;p&gt;Running the containers can simply be done using the &lt;em&gt;docker run&lt;/em&gt; command.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;&lt;em&gt;docker run -it playwright-docker pytest -v&lt;/em&gt;&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;This will run the Playwright Docker tests, and here is the output.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fcdn-images-1.medium.com%2Fmax%2F3200%2F1%2AUr_6WwDSH1coJJRXEbgIdQ.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fcdn-images-1.medium.com%2Fmax%2F3200%2F1%2AUr_6WwDSH1coJJRXEbgIdQ.png"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Similar to local execution, we can also run the Playwright Docker tests in parallel in a Docker container.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fcdn-images-1.medium.com%2Fmax%2F3200%2F1%2AWrHxjqrSCEvW5R-N1ZnAfw.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fcdn-images-1.medium.com%2Fmax%2F3200%2F1%2AWrHxjqrSCEvW5R-N1ZnAfw.png"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;We defaulted the fixture to always run on the local grid, remember?&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fcdn-images-1.medium.com%2Fmax%2F2000%2F1%2AT4MaH7Go5FpAnb-tShT1lA.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fcdn-images-1.medium.com%2Fmax%2F2000%2F1%2AT4MaH7Go5FpAnb-tShT1lA.png"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;But the code is designed to operate based on the environment variables configured in the &lt;em&gt;secrets.env&lt;/em&gt; file. In Docker, to explicitly pass env variables, the best practice is to add all the environment variables in a key value file. This file can be passed to the &lt;em&gt;docker run&lt;/em&gt; command when we run the container.&lt;/p&gt;

&lt;p&gt;Just to remind you, here is what the &lt;em&gt;secrets.env&lt;/em&gt; file looks like for us.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fcdn-images-1.medium.com%2Fmax%2F2000%2F1%2Ay4TBQTNeLhWmh1s7YF_EXw.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fcdn-images-1.medium.com%2Fmax%2F2000%2F1%2Ay4TBQTNeLhWmh1s7YF_EXw.png"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;To run the docker container by passing it the env file, we need to leverage the — — env-file flag and the docker run command.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;&lt;em&gt;docker run -it — env-file secrets.env playwright-docker pytest -v&lt;/em&gt;&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;The below snapshot shows the result of the container run.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fcdn-images-1.medium.com%2Fmax%2F3200%2F1%2AyTRd34Q7BZm3XfX94k0GvQ.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fcdn-images-1.medium.com%2Fmax%2F3200%2F1%2AyTRd34Q7BZm3XfX94k0GvQ.png"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;&lt;em&gt;docker container ls -a&lt;/em&gt;&lt;/strong&gt; command can be used to view a history of all the containers that have been run so far&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fcdn-images-1.medium.com%2Fmax%2F2584%2F1%2AJtg1qtoaqaJjhThAA6Woig.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fcdn-images-1.medium.com%2Fmax%2F2584%2F1%2AJtg1qtoaqaJjhThAA6Woig.png"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Running Playwright Docker Tests On The Cloud Grid
&lt;/h2&gt;

&lt;p&gt;So far, it’s clear to notice that running the Playwright Docker tests locally has some disadvantages in terms of choices of browsers and operating systems. The problems of browsers are easy to get around by installing various browsers, and the Playwright Docker image also comes pre-baked with all the browsers.&lt;/p&gt;

&lt;p&gt;However, the issue of conducting Playwright Docker tests on different operating systems persists. How can this be resolved? One possible solution is to utilize a cloud grid provider such as LambdaTest.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://www.lambdatest.com/blog/benefits-of-cloud-testing/?utm_source=devto&amp;amp;utm_medium=organic&amp;amp;utm_campaign=jan_03&amp;amp;utm_term=bw&amp;amp;utm_content=blog" rel="noopener noreferrer"&gt;Cloud testing&lt;/a&gt; platforms like LambdaTest offer the capability to conduct &lt;a href="https://www.lambdatest.com/blog/playwright-framework/?utm_source=devto&amp;amp;utm_medium=organic&amp;amp;utm_campaign=jan_03&amp;amp;utm_term=bw&amp;amp;utm_content=blog" rel="noopener noreferrer"&gt;Playwright automated testing&lt;/a&gt; on a large scale, significantly reducing the time it takes to run these &lt;a href="https://www.lambdatest.com/playwright-testing?utm_source=devto&amp;amp;utm_medium=organic&amp;amp;utm_campaign=jan_03&amp;amp;utm_term=bw&amp;amp;utm_content=webpage" rel="noopener noreferrer"&gt;Playwright tests&lt;/a&gt;. With access to a browser farm of over 50 different versions of popular browsers such as Chrome, Chromium, Microsoft Edge, Mozilla Firefox, and Webkit, you can efficiently execute your Playwright Docker tests.&lt;br&gt;
&lt;iframe width="710" height="399" src="https://www.youtube.com/embed/ioJaNCYX7Qc"&gt;
&lt;/iframe&gt;
&lt;br&gt;
You can also subscribe to the LambdaTest YouTube Channel and stay updated with the latest tutorial around &lt;a href="https://www.lambdatest.com/selenium-automation?utm_source=devto&amp;amp;utm_medium=organic&amp;amp;utm_campaign=jan_03&amp;amp;utm_term=bw&amp;amp;utm_content=webpage" rel="noopener noreferrer"&gt;Selenium testing&lt;/a&gt;, &lt;a href="https://www.lambdatest.com/cypress-e2e-testing?utm_source=devto&amp;amp;utm_medium=organic&amp;amp;utm_campaign=jan_03&amp;amp;utm_term=bw&amp;amp;utm_content=webpage" rel="noopener noreferrer"&gt;Cypress E2E testing&lt;/a&gt;, &lt;a href="https://www.lambdatest.com/appium-mobile-testing?utm_source=devto&amp;amp;utm_medium=organic&amp;amp;utm_campaign=jan_03&amp;amp;utm_term=bw&amp;amp;utm_content=webpage" rel="noopener noreferrer"&gt;Appium&lt;/a&gt;, and more.&lt;/p&gt;

&lt;p&gt;To leverage LambdaTest Docker grid, we need a username and access key, which is available in the &lt;a href="https://accounts.lambdatest.com/detail/profile" rel="noopener noreferrer"&gt;LambdaTest Profile Section&lt;/a&gt; once we create an account. We will now modify the secrets.env file to add the username and access key as two more environment variables named &lt;em&gt;LT_USERNAME *and *LT_ACCESS_KEY&lt;/em&gt;.&lt;/p&gt;

&lt;p&gt;The &lt;em&gt;RUN_ON&lt;/em&gt; variable has also been modified to toggle the pytest fixture to initialize the connection to the cloud grid.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fcdn-images-1.medium.com%2Fmax%2F2000%2F1%2All96JO9txiG1KWOaIIlDiQ.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fcdn-images-1.medium.com%2Fmax%2F2000%2F1%2All96JO9txiG1KWOaIIlDiQ.png"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;We do not need to rebuild the Docker image since we control the behavior externally via environment variables. Another good reason we should use environment variables to store controlling flags and data which drive the code behavior.&lt;/p&gt;

&lt;p&gt;Let’s now rerun the Docker container with three parallel Playwright Docker tests.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;&lt;em&gt;docker run -it –env-file secrets.enf playwright-docker pytest -v –numprocesses 3&lt;/em&gt;&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fcdn-images-1.medium.com%2Fmax%2F2274%2F1%2AgmbtZCFnEko2syJNfEWfTw.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fcdn-images-1.medium.com%2Fmax%2F2274%2F1%2AgmbtZCFnEko2syJNfEWfTw.png"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Below snapshot from the &lt;a href="https://accounts.lambdatest.com/dashboard" rel="noopener noreferrer"&gt;LambdaTest Dashboard&lt;/a&gt; shows the three Playwright Docker tests executing in parallel. The browser and OS we chose are also shown as small icons, along with the test results.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fcdn-images-1.medium.com%2Fmax%2F3200%2F1%2AycHoJ-x1DqDn4l30aOf9Fw.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fcdn-images-1.medium.com%2Fmax%2F3200%2F1%2AycHoJ-x1DqDn4l30aOf9Fw.png"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;If we open one of the individual Playwright Docker tests, we can also get a finer view of the test run along with the steps, screenshots, video of execution, browser and OS, and other details.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fcdn-images-1.medium.com%2Fmax%2F3200%2F1%2AUke6I9TTyVn-jF00XjLNEg.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fcdn-images-1.medium.com%2Fmax%2F3200%2F1%2AUke6I9TTyVn-jF00XjLNEg.png"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Now, let’s run the same Playwright Docker tests on macOS and Safari. For that, we will need to modify the environment file &lt;em&gt;secrets.env&lt;/em&gt; to amend the &lt;em&gt;BROWSER&lt;/em&gt; and &lt;em&gt;PLATFORM&lt;/em&gt; variables. Here is how the updated file now looks after the changes.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fcdn-images-1.medium.com%2Fmax%2F2000%2F1%2ALBQqLolJFBPV2-63SKnzqg.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fcdn-images-1.medium.com%2Fmax%2F2000%2F1%2ALBQqLolJFBPV2-63SKnzqg.png"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;There is no need to rebuild the image; we can simply run it using the docker run command below.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;&lt;em&gt;docker run -it — env-file secrets.env playwright-docker pytest -v –numprocesses 3&lt;/em&gt;&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fcdn-images-1.medium.com%2Fmax%2F2418%2F1%2AU7DZ7To7_HaMDW_2qLuqnQ.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fcdn-images-1.medium.com%2Fmax%2F2418%2F1%2AU7DZ7To7_HaMDW_2qLuqnQ.png"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Here is the new snapshot of the LambaTest Dashboard, again notice the browser and OS version are clearly shown as icons verifying that the &lt;em&gt;Playwright Docker tests&lt;/em&gt; did indeed run on the configurations we made in the secrets.env file.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fcdn-images-1.medium.com%2Fmax%2F3044%2F1%2AAgdN0742xklgaMANz0UDyQ.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fcdn-images-1.medium.com%2Fmax%2F3044%2F1%2AAgdN0742xklgaMANz0UDyQ.png"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;And to see the details of an individual test, we can open it and check for the execution details.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fcdn-images-1.medium.com%2Fmax%2F3200%2F1%2AcVvU1KaGDhzgDleYSH1EXw.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fcdn-images-1.medium.com%2Fmax%2F3200%2F1%2AcVvU1KaGDhzgDleYSH1EXw.png"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Docker Compose For Running Multi-Container Docker Applications
&lt;/h2&gt;

&lt;p&gt;For this Playwright Docker tutorial, we have written eight tests and built a single Docker image for them. It’s easy to run the &lt;strong&gt;&lt;em&gt;docker run&lt;/em&gt;&lt;/strong&gt; command for one image. But for testing more complicated solutions with many containers and dependencies on other containers, the &lt;strong&gt;&lt;em&gt;docker run&lt;/em&gt;&lt;/strong&gt; is less than ideal. To run such multi-container applications, Docker provides an efficient tool called &lt;strong&gt;&lt;em&gt;docker compose&lt;/em&gt;&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;The &lt;strong&gt;&lt;em&gt;docker compose&lt;/em&gt;&lt;/strong&gt; the need to use the &lt;strong&gt;&lt;em&gt;docker run&lt;/em&gt;&lt;/strong&gt; command and can start all the containers in a single command. All we need to do is define the setup in a yaml file. The use of Docker compose in Production is always debated and unsuitable for all. This &lt;a href="https://stackoverflow.com/questions/37521440/is-docker-compose-suitable-for-production" rel="noopener noreferrer"&gt;Stack Overflow discussion&lt;/a&gt; is nice for getting a better idea.&lt;/p&gt;

&lt;p&gt;But for local testing, we can use it. Let’s see that in the next section of this Playwright Docker tutorial.&lt;/p&gt;

&lt;h2&gt;
  
  
  Writing The Docker Compose File
&lt;/h2&gt;

&lt;p&gt;To leverage &lt;strong&gt;&lt;em&gt;docker compose&lt;/em&gt;&lt;/strong&gt;, we need to write a special file called &lt;strong&gt;&lt;em&gt;docker-compose.yaml&lt;/em&gt;&lt;/strong&gt;.&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;version: '3.9'
services:
testing:
image: playwright-docker
env_file:
- ./secrets.env
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;The first line &lt;em&gt;version&lt;/em&gt; of the file contains the docker compose version. Details of which version to use can be obtained from the official Docker docs.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;The next line of &lt;em&gt;services&lt;/em&gt; is the start of the service definition, i.e., all containers this docker-compose will start.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;The next line of &lt;em&gt;testing&lt;/em&gt; is the name of the service. It can be any meaningful name that defines what the service does.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;The following line &lt;em&gt;image&lt;/em&gt; defines which image to use. If present locally, it will be used; otherwise, Docker compose will try to pull it from the Docker hub.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Instead of &lt;em&gt;image&lt;/em&gt;, we can also use the build and give it the location of the Dockerfile, and the image will be built locally.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;The next file, &lt;em&gt;env_file&lt;/em&gt;, is where we pass the env file we define.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Execution
&lt;/h2&gt;

&lt;p&gt;Let’s now run the container using the &lt;em&gt;docker compose up&lt;/em&gt; command, and we can see all the Playwright Docker tests are still running, but the command needed to do it has been greatly simplified.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fcdn-images-1.medium.com%2Fmax%2F3200%2F1%2Akwvue1msT5kQV_GYYvEW4A.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fcdn-images-1.medium.com%2Fmax%2F3200%2F1%2Akwvue1msT5kQV_GYYvEW4A.png"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Conclusion
&lt;/h2&gt;

&lt;p&gt;In conclusion, using Docker to run Playwright tests has many benefits. By building and running tests inside Docker containers, we can ensure that the Playwright Docker tests run in a consistent environment, regardless of the host machine on which they run. This can help to eliminate environment-specific bugs and make it easier to reproduce issues.&lt;/p&gt;

&lt;p&gt;One advantage of using Docker to run Playwright tests is the ability to easily supply environment files with sensitive data, such as access keys and usernames. We could also use the env file to toggle between running Docker locally and on the cloud Docker grid. Creating separate environment files for each environment (e.g., development, staging, production) allows us to easily switch between environments without modifying the test code.&lt;/p&gt;

&lt;p&gt;Additionally, using Docker Compose to orchestrate the test environment can simplify the process of spinning up and tearing down the necessary services for the Playwright Docker tests. By defining the services in a YAML file and running a single command, we can start all the services needed for the tests, including databases and web servers, and ensure that they are properly configured and running before running the tests.&lt;/p&gt;

&lt;p&gt;Overall, running Playwright Docker tests can make the testing process more efficient, reliable, and secure. Whether we are running tests locally or in a continuous integration environment, Docker can help to streamline the process and ensure that the Playwright Docker tests are running in a consistent environment, with the necessary services and data available.&lt;/p&gt;

</description>
      <category>playwright</category>
      <category>opensource</category>
      <category>tutorial</category>
      <category>testing</category>
    </item>
    <item>
      <title>How to Perform Screenshot Comparison in Playwright</title>
      <dc:creator>Jaydeep Karale</dc:creator>
      <pubDate>Mon, 01 Jan 2024 12:48:59 +0000</pubDate>
      <link>https://dev.to/_jaydeepkarale/how-to-perform-screenshot-comparison-in-playwright-d3k</link>
      <guid>https://dev.to/_jaydeepkarale/how-to-perform-screenshot-comparison-in-playwright-d3k</guid>
      <description>&lt;p&gt;In the ever-evolving digital landscape, accessing the Internet and browsing websites has undergone significant transformations. With the rise of laptops, mobile devices, and an array of screen sizes and resolutions, how we engage with the web has evolved considerably. Furthermore, the multitude of browsers available for web browsing has added further complexity to web development and testing.&lt;/p&gt;

&lt;p&gt;It is crucial to prioritize the consistent rendering of web content across a diverse range of devices and screen sizes. Ensuring that web pages and applications appear and function flawlessly across different viewports has become a paramount concern in web development practices.&lt;/p&gt;

&lt;p&gt;To ensure faster and defect-free delivery, it has become increasingly important to reduce the time spent doing &lt;a href="https://www.lambdatest.com/learning-hub/manual-testing?utm_source=devto&amp;amp;utm_medium=organic&amp;amp;utm_campaign=dec_26&amp;amp;utm_term=bw&amp;amp;utm_content=learning_hub" rel="noopener noreferrer"&gt;manual testing&lt;/a&gt;, be it functional or visual. Whilst functional testing helps ensure that the features work correctly, visual testing ensures that the website is rendered correctly as per the design.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://www.lambdatest.com/learning-hub/visual-testing?utm_source=devto&amp;amp;utm_medium=organic&amp;amp;utm_campaign=dec_26&amp;amp;utm_term=bw&amp;amp;utm_content=learning_hub" rel="noopener noreferrer"&gt;Visual testing&lt;/a&gt; can be done manually, but it’s time-consuming, and it’s absolutely not possible to catch all changes with the naked eye. One way to reduce human errors and speed up the testing is by leveraging &lt;a href="https://www.lambdatest.com/blog/best-test-automation-frameworks/?utm_source=devto&amp;amp;utm_medium=organic&amp;amp;utm_campaign=dec_26&amp;amp;utm_term=bw&amp;amp;utm_content=blog" rel="noopener noreferrer"&gt;web automation frameworks&lt;/a&gt;. Playwright is one such framework that enables reliable &lt;a href="https://www.lambdatest.com/learning-hub/end-to-end-testing?utm_source=devto&amp;amp;utm_medium=organic&amp;amp;utm_campaign=dec_26&amp;amp;utm_term=bw&amp;amp;utm_content=learning_hub" rel="noopener noreferrer"&gt;end to end testing&lt;/a&gt; for web apps.&lt;/p&gt;

&lt;p&gt;Playwright provides screenshot comparison out of the box with its TypeScript and JavaScript API, which can help visit a website, take a screenshot, and compare it with a baseline or last good known screenshot to detect any visual differences reliably.&lt;/p&gt;

&lt;p&gt;Screenshot comparison using Playwright is a critical approach in &lt;a href="https://www.lambdatest.com/learning-hub/software-testing?utm_source=devto&amp;amp;utm_medium=organic&amp;amp;utm_campaign=dec_26&amp;amp;utm_term=bw&amp;amp;utm_content=learning_hub" rel="noopener noreferrer"&gt;software testing&lt;/a&gt;, ensuring visual consistency, improving user experience and maintaining &lt;a href="https://www.lambdatest.com/learning-hub/software-quality?utm_source=devto&amp;amp;utm_medium=organic&amp;amp;utm_campaign=dec_26&amp;amp;utm_term=bw&amp;amp;utm_content=learning_hub" rel="noopener noreferrer"&gt;software quality&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;By incorporating Playwright screenshot comparison into the testing process we can identify and address visual issues promptly, enhancing the overall reliability and appeal of the applications and websites.&lt;/p&gt;

&lt;p&gt;In this article, we will demonstrate how to perform screenshot comparisons in Playwright.&lt;/p&gt;

&lt;h2&gt;
  
  
  Why Playwright Screenshot Comparison?
&lt;/h2&gt;

&lt;p&gt;Playwright screenshot comparison falls under visual testing which is a critical aspect of &lt;a href="https://www.lambdatest.com/learning-hub/software-quality-assurance?utm_source=devto&amp;amp;utm_medium=organic&amp;amp;utm_campaign=dec_26&amp;amp;utm_term=bw&amp;amp;utm_content=learning_hub" rel="noopener noreferrer"&gt;software quality assurance&lt;/a&gt; that focuses on validating the visual appearance and consistency of websites and mobile apps.&lt;/p&gt;

&lt;p&gt;It involves comparing visual representations, such as screenshots or designs of a rendered website with the expected rendering as per product design and specification. By validating the pixel-level details, visual testing aims to identify any visual discrepancies, layout issues, or unintended changes that may impact the user experience.&lt;/p&gt;

&lt;p&gt;Let us now see a business scenario that highlights the importance of visual testing.&lt;/p&gt;

&lt;p&gt;An organization recently gave its website a facelift, hoping to make it look better and provide a more user-friendly experience. However, after these changes, the organization noticed a significant drop in key performance indicators like how many users were sticking around, how often people were leaving the site after viewing just one page (bounce rate), and the amount of time users spent on the site. These drops in performance raised concerns among the people in charge.&lt;/p&gt;

&lt;p&gt;To determine what was causing these problems and make things right, the organization decided to use visual testing as a part of its quality control process. Visual testing would help them find any issues with the website’s appearance or issues that had come up due to the design changes. These problems might be why users were engaging less with the website.&lt;/p&gt;

&lt;p&gt;To put this into context, the organization had already tried &lt;a href="https://www.lambdatest.com/learning-hub/ab-testing?utm_source=devto&amp;amp;utm_medium=organic&amp;amp;utm_campaign=dec_26&amp;amp;utm_term=bw&amp;amp;utm_content=learning_hub" rel="noopener noreferrer"&gt;A/B testing&lt;/a&gt;, which showed that the new design was probably causing issues with user engagement. Visual testing was the next step in their quality control process to find the specific visual issues that made users less engaged with the website.&lt;/p&gt;

&lt;p&gt;In a nutshell, visual testing is a vital part of the software testing process, focusing on the visual aspects that contribute to a seamless user experience. By employing visual testing techniques and leveraging the right tools, we can ensure the consistent and visually appealing delivery of their software, enhancing user satisfaction and overall quality.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://www.lambdatest.com/blog/visual-testing-tools/?utm_source=devto&amp;amp;utm_medium=organic&amp;amp;utm_campaign=dec_26&amp;amp;utm_term=bw&amp;amp;utm_content=blog" rel="noopener noreferrer"&gt;Automated visual testing&lt;/a&gt; tools, such as Playwright, provide powerful capabilities to capture screenshots, compare pixel differences, and generate detailed reports. And talking about leveraging the right tools, let’s understand Playwright, which we will use to compare screenshots.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Need a secure hash? Try our &lt;a href="https://www.lambdatest.com/free-online-tools/ripemd160-hash-calculator?utm_source=devto&amp;amp;utm_medium=organic&amp;amp;utm_campaign=dec_26&amp;amp;utm_term=bw&amp;amp;utm_content=free_online_tools" rel="noopener noreferrer"&gt;RIPEMD160 hash calculator&lt;/a&gt; for quick results!&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2&gt;
  
  
  What is Playwright?
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://www.lambdatest.com/playwright?utm_source=devto&amp;amp;utm_medium=organic&amp;amp;utm_campaign=dec_26&amp;amp;utm_term=bw&amp;amp;utm_content=webpage" rel="noopener noreferrer"&gt;Playwright&lt;/a&gt; is an end-to-end web testing and browser automation framework by Microsoft. The Playwright API is available in multiple languages, such as Python, Java, .Net, JavaScript, and TypeScript, making it a strong candidate for end-to-end &lt;a href="https://www.lambdatest.com/learning-hub/web-testing?utm_source=devto&amp;amp;utm_medium=organic&amp;amp;utm_campaign=dec_26&amp;amp;utm_term=bw&amp;amp;utm_content=learning_hub" rel="noopener noreferrer"&gt;web testing&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;At the time of writing this article on screenshot comparison in Playwright, the latest stable version of Playwright is 1.35.1, and as evident from npm trends, its popularity has been on the rise consistently since its release in 2020.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--2h-2qklt--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://cdn-images-1.medium.com/max/2742/0%2AK9gX4lBUbIYMDjdN.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--2h-2qklt--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://cdn-images-1.medium.com/max/2742/0%2AK9gX4lBUbIYMDjdN.png" width="800" height="438"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The reason for the rise in the popularity of Playwright is the easy-to-use API support for multiple languages, making adoption by developers of various tech stacks easy. This makes Playwright the perfect choice for end to end testing of web applications.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Key features of Playwright&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;&lt;a href="https://www.lambdatest.com/learning-hub/cross-browser-compatibility?utm_source=devto&amp;amp;utm_medium=organic&amp;amp;utm_campaign=dec_26&amp;amp;utm_term=bw&amp;amp;utm_content=learning_hub" rel="noopener noreferrer"&gt;Cross browser compatibility&lt;/a&gt; and multi-platform support.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Reliable testing with intelligent wait and web-specific assertions.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Complete isolation and lightning-fast execution.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Advanced tooling&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  How to Setup Playwright?
&lt;/h2&gt;

&lt;p&gt;Before we move on to comparing screenshots using Playwright, let’s set up our development environment. Playwright is flexible and allows working with various languages such as Python, NodeJs, Java, .NET, JavaScript, and TypeScript.&lt;/p&gt;

&lt;p&gt;For this blog, we will use the TypeScript API to perform a Playwright screenshot comparison.&lt;/p&gt;

&lt;p&gt;Using Playwright with its Typescript API has its own advantages. In terms of installation, there are two ways in which Playwright can be installed. One of them is using the Node.js package manager like npm. The second method is using the Playwright VS Code extension.&lt;/p&gt;

&lt;p&gt;Navigate to the extension manager on VS Code and search for Playwright. Then, install the official Playwright Test for VS Code, as shown in the image below.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--Ldr3r0CZ--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://cdn-images-1.medium.com/max/3200/0%2A7D06plcRPeAa-RC5.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--Ldr3r0CZ--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://cdn-images-1.medium.com/max/3200/0%2A7D06plcRPeAa-RC5.png" width="800" height="431"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Once the official extension is installed, use the &lt;strong&gt;Install Playwright&lt;/strong&gt; action from the command panel to get started.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--8Ekm5i4B--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://cdn-images-1.medium.com/max/3200/0%2AOEaung0_XWCoILCw.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--8Ekm5i4B--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://cdn-images-1.medium.com/max/3200/0%2AOEaung0_XWCoILCw.png" width="800" height="432"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--aZuDwzwj--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://cdn-images-1.medium.com/max/3200/0%2AsgtQGXvfSOrZMOOk.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--aZuDwzwj--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://cdn-images-1.medium.com/max/3200/0%2AsgtQGXvfSOrZMOOk.png" width="800" height="430"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Successful installation will result in logs that look like the below image.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--nkjitCvP--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://cdn-images-1.medium.com/max/3166/0%2AE8rU_GqB3bx8hPBU.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--nkjitCvP--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://cdn-images-1.medium.com/max/3166/0%2AE8rU_GqB3bx8hPBU.png" width="800" height="431"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;All the &lt;a href="https://www.lambdatest.com/learning-hub/test-scenario?utm_source=devto&amp;amp;utm_medium=organic&amp;amp;utm_campaign=dec_26&amp;amp;utm_term=bw&amp;amp;utm_content=learning_hub" rel="noopener noreferrer"&gt;test scenarios&lt;/a&gt; used for the demo in this blog are compatible with Playwright version 1.32.2, Node.js version v18.15.0, and npm version 9.5.0.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Secure your data with our &lt;a href="https://www.lambdatest.com/free-online-tools/whirlpool-hash-calculator?utm_source=devto&amp;amp;utm_medium=organic&amp;amp;utm_campaign=dec_26&amp;amp;utm_term=bw&amp;amp;utm_content=free_online_tools" rel="noopener noreferrer"&gt;Whirlpool hash calculator&lt;/a&gt; — Fast &amp;amp; reliable!&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2&gt;
  
  
  How does Playwright Screenshot Comparison Work?
&lt;/h2&gt;

&lt;p&gt;Under the hood, Playwright uses the pixelmatch library, a pixel-level image comparison library. When running the test for the first time, Playwright generates a reference or baseline screenshot.&lt;/p&gt;

&lt;p&gt;This is also the reason why the first test will always fail with an error “&lt;em&gt;Error: A snapshot doesn’t exist at … writing actual&lt;/em&gt;”. The complete error is shown in the image below.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--zuS8a5Tk--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://cdn-images-1.medium.com/max/3200/0%2AWV7NA2k47Ggg70CT.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--zuS8a5Tk--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://cdn-images-1.medium.com/max/3200/0%2AWV7NA2k47Ggg70CT.png" width="800" height="310"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;This step generates the reference/baseline screenshot at a location that is prepared using the naming convention *test-results&amp;lt; testscreenshot-name-of-test-browser&amp;gt;*&lt;/p&gt;

&lt;p&gt;To provide a predefined reference screenshot, we need to create a folder with the name &lt;em&gt;&amp;lt; filename_containing_tests-snapshots &amp;gt; under tests and save the reference file with the name &amp;lt; testname-1-browser-operating_system.png &amp;gt;&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;In our case, this is how the reference screenshot would look like if we chose to provide it.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--licrmSIe--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://cdn-images-1.medium.com/max/2000/0%2AXjGrgqWS9-8Y7DPI.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--licrmSIe--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://cdn-images-1.medium.com/max/2000/0%2AXjGrgqWS9-8Y7DPI.png" width="521" height="112"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;This is extremely helpful when running testing pipelines in a continuous integration setup. All well-known screenshots can be configured so that each test run compares against the expected result. This helps ensure that our pipeline can identify any deviation from the ideal results.&lt;/p&gt;

&lt;p&gt;In the next section, we will look at how to run a screenshot comparison in Playwright.&lt;/p&gt;

&lt;h2&gt;
  
  
  How to Capture Screenshots Using Playwright?
&lt;/h2&gt;

&lt;p&gt;To explore screenshot comparison in Playwright, we first need to understand how to capture screenshots using Playwright. The main API to capture screenshots is the page.screenshot(), shown below, is the method information:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Syntax:&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;page.screenshot()&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Parameters&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;type: (string, default: png)&lt;/strong&gt; — Decides the file extension of the saved screenshot&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;path: (string)&lt;/strong&gt; — The file path to save the image. If the &lt;em&gt;path&lt;/em&gt; is a relative path, then it is resolved relative to the current working directory. If no &lt;em&gt;path&lt;/em&gt; is provided, the image won’t be saved to the disk&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;mask: (Array&amp;lt; Playwright Locator &amp;gt;)&lt;/strong&gt; — Used to specify the locators that should be masked when the screenshot is taken. The default overlay color is pink, but it can be customized with the help of the &lt;em&gt;maskColor&lt;/em&gt; parameter&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;maskColor: (string)&lt;/strong&gt; — Used to customize the color of the masked locators.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;clip: (x: number, y: number, width: number, height: number)&lt;/strong&gt; — An object that allows clipping of the resulting screenshot&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Parameters (Optional)&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Shown below are some other optional configurable parameters that can be further used with the &lt;em&gt;page.screenshot()&lt;/em&gt; method in Playwright:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;&lt;em&gt;animations: (string, default: allow)&lt;/em&gt;&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;When set to &lt;em&gt;disabled&lt;/em&gt;, it stops &lt;a href="https://www.lambdatest.com/blog/css-animations-tutorial/?utm_source=devto&amp;amp;utm_medium=organic&amp;amp;utm_campaign=dec_26&amp;amp;utm_term=bw&amp;amp;utm_content=blog" rel="noopener noreferrer"&gt;CSS animations&lt;/a&gt;, &lt;a href="https://www.lambdatest.com/blog/css-transforms-and-transitions-property/?utm_source=devto&amp;amp;utm_medium=organic&amp;amp;utm_campaign=dec_26&amp;amp;utm_term=bw&amp;amp;utm_content=blog" rel="noopener noreferrer"&gt;CSS transitions&lt;/a&gt;, and web animations.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--1mU01lDv--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://cdn-images-1.medium.com/max/2336/0%2AMJT3Yu6ojtD8fUZE.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--1mU01lDv--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://cdn-images-1.medium.com/max/2336/0%2AMJT3Yu6ojtD8fUZE.png" width="800" height="252"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;&lt;em&gt;caret: (string, default: hide)&lt;/em&gt;&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;When set to &lt;em&gt;hide&lt;/em&gt;, the screenshot will hide text caret.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--pDvjnDi0--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://cdn-images-1.medium.com/max/3200/0%2ASgggKW656foh3F7h.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--pDvjnDi0--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://cdn-images-1.medium.com/max/3200/0%2ASgggKW656foh3F7h.png" width="800" height="193"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;&lt;em&gt;omitBackground: (boolean, default: false)&lt;/em&gt;&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;It hides the default white background and allows capturing screenshots with transparency. Not applicable to &lt;em&gt;jpeg&lt;/em&gt; images.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--R2_Xm6n2--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://cdn-images-1.medium.com/max/2000/0%2A9aTsWV41Uz_zSlp1.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--R2_Xm6n2--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://cdn-images-1.medium.com/max/2000/0%2A9aTsWV41Uz_zSlp1.png" width="628" height="368"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;&lt;em&gt;scale: (string, default: device)&lt;/em&gt;&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Adjust the screenshot depending on devices with high dpi.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--YiocoRSL--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://cdn-images-1.medium.com/max/3200/0%2AGuXNwWXYkPeiT43d.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--YiocoRSL--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://cdn-images-1.medium.com/max/3200/0%2AGuXNwWXYkPeiT43d.png" width="800" height="246"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;&lt;em&gt;timeout: (number, default: device)&lt;/em&gt;&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Adjust the screenshot depending on devices with high-dpi.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--oj_JPZo0--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://cdn-images-1.medium.com/max/3200/0%2AXKU7R2VCmfeHHTu8.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--oj_JPZo0--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://cdn-images-1.medium.com/max/3200/0%2AXKU7R2VCmfeHHTu8.png" width="800" height="191"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Let us now demo how we can configure its behavior to &lt;em&gt;capture full-page screenshot, element screenshot, and capture the data into a buffer,&lt;/em&gt; which can be passed to third-party pixel comparison tools.&lt;/p&gt;

&lt;p&gt;To demonstrate the Playwright screenshot comparison, we will be using the &lt;a href="https://ecommerce-playground.lambdatest.io/" rel="noopener noreferrer"&gt;LambdaTest eCommerce playground&lt;/a&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Full-Page Screenshot Comparison Using Playwright
&lt;/h2&gt;

&lt;p&gt;Let us see how to perform a full-page screenshot comparison in Playwright. We will do this by capturing a full-page screenshot of the LambdaTest eCommerce playground homepage.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Step 1:&lt;/strong&gt; Navigate to the URL of the LambdaTest eCommerce demo website using the &lt;em&gt;page.goto()&lt;/em&gt; method.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Step 2:&lt;/strong&gt; Once on the page, use &lt;em&gt;page.screenshot()&lt;/em&gt; to capture a full page screenshot by setting the parameter &lt;em&gt;fullPage: true&lt;/em&gt;. The path parameter is used to set the name and location of the captured file.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--mCb2rzZJ--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://cdn-images-1.medium.com/max/2796/0%2AdPj8EZGUGMnT0Xp4.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--mCb2rzZJ--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://cdn-images-1.medium.com/max/2796/0%2AdPj8EZGUGMnT0Xp4.png" width="800" height="254"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Here is what the generated screenshot looks like.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--vALAwAvm--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://cdn-images-1.medium.com/max/3200/0%2AteI_8GhcZW9Q65VB.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--vALAwAvm--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://cdn-images-1.medium.com/max/3200/0%2AteI_8GhcZW9Q65VB.png" width="800" height="480"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Simplify your coding! Use our &lt;a href="https://www.lambdatest.com/free-online-tools/html-escape?utm_source=devto&amp;amp;utm_medium=organic&amp;amp;utm_campaign=dec_26&amp;amp;utm_term=bw&amp;amp;utm_content=free_online_tools" rel="noopener noreferrer"&gt;HTML escape&lt;/a&gt; tool for hassle-free web development.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2&gt;
  
  
  Element Screenshot Comparison Using Playwright
&lt;/h2&gt;

&lt;p&gt;The overall appearance and user experience of a website may not change entirely on a regular basis, but it’s a common occurrence for specific elements to be modified or updated. In such a case, we can combine &lt;a href="https://www.lambdatest.com/blog/playwright-locators/?utm_source=devto&amp;amp;utm_medium=organic&amp;amp;utm_campaign=dec_26&amp;amp;utm_term=bw&amp;amp;utm_content=blog" rel="noopener noreferrer"&gt;Playwright locators&lt;/a&gt; and the &lt;em&gt;page.screenshot()&lt;/em&gt; method.&lt;/p&gt;

&lt;p&gt;For example, consider that on the demo eCommerce website, we have to capture the screenshot of the search field before and after the search operation is performed. For implementing such a test, we will need first to locate the search field and then capture the screenshot.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Step 1:&lt;/strong&gt; To locate the search field, we use the Playwright locator &lt;em&gt;getByRole(‘textbox’, {name: ‘Search For Products’}).&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--wJzQpkhF--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://cdn-images-1.medium.com/max/3200/0%2AathBIlg5UREaq5Yz.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--wJzQpkhF--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://cdn-images-1.medium.com/max/3200/0%2AathBIlg5UREaq5Yz.png" width="800" height="465"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Step 2:&lt;/strong&gt; On the located element, call the &lt;em&gt;screenshot()&lt;/em&gt; method by using chaining.&lt;/p&gt;

&lt;p&gt;The &lt;em&gt;path&lt;/em&gt; is the file path to save the image. The screenshot type will be inferred from the file extension. If the &lt;em&gt;path&lt;/em&gt; is a relative path, then it is resolved relative to the current working directory. If no &lt;em&gt;path&lt;/em&gt; is provided, the image won’t be saved to the disk.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--63Uz8gpb--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://cdn-images-1.medium.com/max/3200/0%2A9ZoIJeqP8bYiUgHc.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--63Uz8gpb--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://cdn-images-1.medium.com/max/3200/0%2A9ZoIJeqP8bYiUgHc.png" width="800" height="185"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Execution&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--40YNle0l--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://cdn-images-1.medium.com/max/3200/0%2A0pIb-r9M5mByspuP.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--40YNle0l--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://cdn-images-1.medium.com/max/3200/0%2A0pIb-r9M5mByspuP.png" width="800" height="480"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Capture Screenshot Into Buffer Using Playwright
&lt;/h2&gt;

&lt;p&gt;We can also leverage third-party image comparison or post-processing tools by capturing the image data into a buffer.&lt;/p&gt;

&lt;p&gt;We use the same example of locating the &lt;em&gt;Search&lt;/em&gt; field, but instead of capturing a screenshot, we write the data to a variable called &lt;em&gt;buffer&lt;/em&gt;.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--5j8YtS67--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://cdn-images-1.medium.com/max/3200/0%2A12VN_l6N2m1_14ik.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--5j8YtS67--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://cdn-images-1.medium.com/max/3200/0%2A12VN_l6N2m1_14ik.png" width="800" height="228"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Execution&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--7_KMvZmS--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://cdn-images-1.medium.com/max/3200/0%2AOfhwRtAWcV_lOTzS.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--7_KMvZmS--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://cdn-images-1.medium.com/max/3200/0%2AOfhwRtAWcV_lOTzS.png" width="800" height="287"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Now that we have seen how to capture screenshots using Playwright let us move on to understand how &lt;a href="https://www.lambdatest.com/learning-hub/visual-regression-testing?utm_source=devto&amp;amp;utm_medium=organic&amp;amp;utm_campaign=dec_26&amp;amp;utm_term=bw&amp;amp;utm_content=learning_hub" rel="noopener noreferrer"&gt;visual regression testing&lt;/a&gt; can be performed using screenshot comparison in Playwright.&lt;/p&gt;

&lt;h2&gt;
  
  
  How to Perform Screenshot Comparison Using Playwright?
&lt;/h2&gt;

&lt;p&gt;In this section on performing screenshot comparison using Playwright, we will use the &lt;a href="https://ecommerce-playground.lambdatest.io/" rel="noopener noreferrer"&gt;LambdaTest eCommerce website&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Test Scenario:&lt;/strong&gt;&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;&lt;p&gt;Capture the baseline screenshot of the LambdaTest eCommerce homepage.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Populate the text ‘&lt;em&gt;test compare screenshot&lt;/em&gt;’ in the homepage search input field and take a screenshot.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Expect baseline and new screenshots to show the difference in text only&lt;br&gt;
.&lt;/p&gt;&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Implementation for Generating Baseline Screenshot&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;test('compare screenshot', async ({ page }) =&amp;gt; {
  await page.goto('https://ecommerce-playground.lambdatest.io/')
  await expect(page).toHaveScreenshot();
});
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;ol&gt;
&lt;li&gt;&lt;p&gt;Call the &lt;em&gt;goto()&lt;/em&gt; method on the page object to navigate to the demo website.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Use the &lt;em&gt;expect()&lt;/em&gt; Playwright assertion on the &lt;em&gt;page&lt;/em&gt; object to call &lt;em&gt;toHaveScreenshot()&lt;/em&gt; method to capture the baseline.&lt;/p&gt;&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;&lt;strong&gt;Execution&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;We run the single test using the &lt;em&gt;npx playwright test -g “compare screenshot”&lt;/em&gt; command on Chromium.&lt;/p&gt;

&lt;p&gt;As expected, the test fails since we only capture the baseline screenshot for subsequent comparison.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--f-qr0hPP--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://cdn-images-1.medium.com/max/3034/0%2AtFnEdySYWZBoAaQd.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--f-qr0hPP--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://cdn-images-1.medium.com/max/3034/0%2AtFnEdySYWZBoAaQd.png" width="800" height="312"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The capture baseline screenshot is created in the directory *test-results\testscreenshot-compare-screenshot-chromium* based on the name of the test and the browser being used.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--njC3lx6h--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://cdn-images-1.medium.com/max/3200/0%2A3KjXjLXOWgkJT5MX.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--njC3lx6h--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://cdn-images-1.medium.com/max/3200/0%2A3KjXjLXOWgkJT5MX.png" width="800" height="463"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Implementation for Introducing Difference&lt;/strong&gt;&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;test('compare screenshot', async ({ page }) =&amp;gt; {
  await page.goto('https://ecommerce-playground.lambdatest.io/')
  await page.getByRole("textbox", {name : "Search For Products"}).fill('test compare screenshot')
  await expect(page).toHaveScreenshot();
});
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;The steps to navigate to the demo website remain the same.&lt;/p&gt;

&lt;p&gt;On the page, we use the &lt;em&gt;getByRole()&lt;/em&gt; Playwright locator to find the search input field. We use &lt;em&gt;fill()&lt;/em&gt; in the located search field with ‘test compare screenshot’ arbitrary text to introduce a difference.&lt;/p&gt;

&lt;p&gt;We then capture the screenshot using &lt;em&gt;toHaveScreenshot()&lt;/em&gt; and the &lt;em&gt;expect()&lt;/em&gt; assertion.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Execution&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Run the test using the &lt;em&gt;npx playwright test -g “compare screenshot”&lt;/em&gt; command on Chromium.&lt;/p&gt;

&lt;p&gt;The command does the following things:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;&lt;p&gt;Show the difference in the number of pixels between the baseline and the latest screenshot.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Generate two more screenshots, one named “&lt;em&gt;compare-screenshot-1-expected.png&lt;/em&gt;,” representing the expected output according to Playwright, and the other named “&lt;em&gt;compare-screenshot-1-diff.png&lt;/em&gt;,” illustrating the disparity between the baseline and the captured screenshot.&lt;/p&gt;&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--Ab3b8fdl--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://cdn-images-1.medium.com/max/3200/0%2AelDlj9aWkkDb5JKY.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--Ab3b8fdl--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://cdn-images-1.medium.com/max/3200/0%2AelDlj9aWkkDb5JKY.png" width="800" height="419"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Here is what the &lt;em&gt;test-results&lt;/em&gt; folder looks like after command execution, and it contains all the screenshots, i.e., the baseline, the expected, and the difference.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--Va88i4to--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://cdn-images-1.medium.com/max/2000/0%2AWoiq-Okj2j0tNeeM.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--Va88i4to--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://cdn-images-1.medium.com/max/2000/0%2AWoiq-Okj2j0tNeeM.png" width="793" height="174"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Shown below is &lt;em&gt;compare-screenshot-1-diff.png&lt;/em&gt;, which highlights the difference between the expected and actual image. It is clear that in addition to the text we introduced, our demo website also has some other font-related issues.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--3nUVhwPH--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://cdn-images-1.medium.com/max/2560/0%2A1ZxB9zZ06VH6KG73.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--3nUVhwPH--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://cdn-images-1.medium.com/max/2560/0%2A1ZxB9zZ06VH6KG73.png" width="800" height="450"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Here are all three images, i.e., the baseline, the screenshot captured, and the difference, all presented side-by-side to get a better idea.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--NrO3nwtZ--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://cdn-images-1.medium.com/max/3200/0%2AcLf2vHG3k_N6qo40.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--NrO3nwtZ--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://cdn-images-1.medium.com/max/3200/0%2AcLf2vHG3k_N6qo40.png" width="800" height="566"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Let us repeat the test, but instead of searching for an arbitrary text, this time, search for an actual product in the database.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Test Scenario:&lt;/strong&gt;&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;&lt;p&gt;Capture the baseline screenshot of the LambdaTest eCommerce homepage.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Search for ‘&lt;em&gt;Apple&lt;/em&gt;’ products and take screenshots.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Expect a baseline and a new screenshot to show the difference.&lt;/p&gt;&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;&lt;strong&gt;Implementation for Generating Baseline Screenshot&lt;/strong&gt;&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;test('compare screenshot search for apple product', async ({ page }) =&amp;gt; {
  await page.goto('https://ecommerce-playground.lambdatest.io/')  
  await expect(page).toHaveScreenshot();
});
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;We will write a new test named ‘compare screenshot search for apple product.’&lt;/p&gt;

&lt;p&gt;The &lt;em&gt;page.goto()&lt;/em&gt; method is used to navigate the demo website under test. The &lt;em&gt;toHaveScreenshot()&lt;/em&gt; method is used to capture the baseline screenshot.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Execution&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;The test can be run using the command &lt;em&gt;npx playwright test -g “compare screenshot search for apple product.”&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;As expected, the test fails and generates the baseline screenshot under the &lt;em&gt;test-results&lt;/em&gt; directory but in a newly created folder based on the name of the test, &lt;em&gt;compare screenshot search for apple product&lt;/em&gt;.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--ZWcVXo04--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://cdn-images-1.medium.com/max/3200/0%2Ap5IepIcx4it0A23G.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--ZWcVXo04--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://cdn-images-1.medium.com/max/3200/0%2Ap5IepIcx4it0A23G.png" width="800" height="314"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Shown below is the generated baseline screenshot.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--01AShxIE--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://cdn-images-1.medium.com/max/3200/0%2AgGzvCSu0zvtdoVvy.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--01AShxIE--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://cdn-images-1.medium.com/max/3200/0%2AgGzvCSu0zvtdoVvy.png" width="800" height="467"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Implementation for Generating Apple Product Search Screenshot&lt;/strong&gt;&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;test('compare screenshot search for apple product', async ({ page }) =&amp;gt; {
  await page.goto('https://ecommerce-playground.lambdatest.io/')  
  await page.getByRole("textbox", {name : "Search For Products"}).fill('apple')
  await expect(page).toHaveScreenshot();
});
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;We use the Playwright locator to find the ‘&lt;em&gt;search&lt;/em&gt;’ field and &lt;em&gt;fill()&lt;/em&gt; with the search term ‘&lt;em&gt;apple&lt;/em&gt;.’&lt;/p&gt;

&lt;p&gt;Capture the screenshot after the search by calling the &lt;em&gt;toHaveScreenshot()&lt;/em&gt; method on the page.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Execution&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Run the test using the command &lt;em&gt;npx playwright test -g “compare screenshot search for apple product”&lt;/em&gt;.&lt;/p&gt;

&lt;p&gt;The output of the test run indicates the difference in the number of pixels, which is significant this time and also generated the expected and the &lt;em&gt;diff&lt;/em&gt; images.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--Sk9nrKEq--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://cdn-images-1.medium.com/max/3200/0%2AsxFjl2-3hmQCEKfN.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--Sk9nrKEq--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://cdn-images-1.medium.com/max/3200/0%2AsxFjl2-3hmQCEKfN.png" width="800" height="515"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Here is the folder structure where all the screenshots are stored.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--iGUE7BzY--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://cdn-images-1.medium.com/max/2006/0%2A4suAhMStxvLNEyE1.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--iGUE7BzY--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://cdn-images-1.medium.com/max/2006/0%2A4suAhMStxvLNEyE1.png" width="800" height="134"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;And the image showing the pixel difference between the baseline and the captured screenshot below shows considerable differences.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--nc1xf-Vx--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://cdn-images-1.medium.com/max/2560/0%2AQ6f6PEHAyf3AqTBQ.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--nc1xf-Vx--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://cdn-images-1.medium.com/max/2560/0%2AQ6f6PEHAyf3AqTBQ.png" width="800" height="450"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Here are all three images, i.e., the baseline, the captured, and the difference shown side-by-side.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--YOwINmER--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://cdn-images-1.medium.com/max/3200/0%2AK7ELzBsixQDFYeTb.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--YOwINmER--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://cdn-images-1.medium.com/max/3200/0%2AK7ELzBsixQDFYeTb.png" width="800" height="566"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;So far, we have been using the &lt;em&gt;toHaveScreenshot()&lt;/em&gt; method with its default configuration. But since, under the hood, it uses the &lt;em&gt;PixelMatch&lt;/em&gt; library, the method has a few configurations available to customize the test.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Get creative with text! Try our &lt;a href="https://www.lambdatest.com/free-online-tools/shuffle-letters?utm_source=devto&amp;amp;utm_medium=organic&amp;amp;utm_campaign=dec_26&amp;amp;utm_term=bw&amp;amp;utm_content=free_online_tools" rel="noopener noreferrer"&gt;Shuffle letters&lt;/a&gt; tool for unique variations.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2&gt;
  
  
  How to Allow Acceptable Pixel Difference?
&lt;/h2&gt;

&lt;p&gt;There are two ways in which we can configure an acceptable pixel difference when comparing screenshots with Playwright using the &lt;em&gt;maxDiffPixelRatio&lt;/em&gt; and &lt;em&gt;maxDiffPixels *parameters of the *toHaveScreenshot()&lt;/em&gt; method.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;maxDiffPixelRatio&lt;/em&gt; is any number between 0 and 1, whereas &lt;em&gt;maxDiffPixels&lt;/em&gt; is any number value as per the project requirement.&lt;/p&gt;

&lt;p&gt;These parameters can be set either at each test level or can be configured as a global default inside the &lt;em&gt;playwright.config.ts&lt;/em&gt; file within expect.&lt;/p&gt;

&lt;p&gt;For example, to set a global default max pixel difference of 100, we would add the below line in the &lt;em&gt;playwright.config.ts&lt;/em&gt; file.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--m4K1ill3--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://cdn-images-1.medium.com/max/2000/0%2AezYt_2JM4OVgHIKK.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--m4K1ill3--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://cdn-images-1.medium.com/max/2000/0%2AezYt_2JM4OVgHIKK.png" width="800" height="412"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;To set a global default max pixel difference ratio of 0.5, we would add the below line in the &lt;em&gt;playwright.config.ts&lt;/em&gt; file.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--ksI7g0sV--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://cdn-images-1.medium.com/max/2000/0%2AI93NemNnUpwfiB4S.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--ksI7g0sV--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://cdn-images-1.medium.com/max/2000/0%2AI93NemNnUpwfiB4S.png" width="800" height="412"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;For the demo, we will repeat the scenario where we added arbitrary text in the search field of the demo website homepage.&lt;/p&gt;

&lt;p&gt;The test result had a pixel difference of 784. Let us repeat the test to allow a pixel difference of 800 to be acceptable and rerun the test.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--2pHODMB6--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://cdn-images-1.medium.com/max/2000/0%2A84hMGX4bX9u7sc3i.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--2pHODMB6--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://cdn-images-1.medium.com/max/2000/0%2A84hMGX4bX9u7sc3i.png" width="460" height="130"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Test Scenario:&lt;/strong&gt;&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;&lt;p&gt;Capture the baseline screenshot of the LambdaTest eCommerce homepage.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Populate the text ‘&lt;em&gt;test compare screenshot&lt;/em&gt;’ in the homepage search input field and take a screenshot but with a &lt;em&gt;maxDiffPixels&lt;/em&gt; set to 800.&lt;/p&gt;&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;&lt;strong&gt;Implementation for Generating Baseline Screenshot&lt;/strong&gt;&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;test('allow max pixel diff while compare screenshot', async ({ page }) =&amp;gt; {
  await page.goto('https://ecommerce-playground.lambdatest.io/')
  await expect(page).toHaveScreenshot();
});
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;&lt;strong&gt;Execution&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Run the test using &lt;em&gt;npx playwright -g “allow max pixel diff while compare screenshot”&lt;/em&gt; command.&lt;/p&gt;

&lt;p&gt;As expected, the test fails and generates the baseline screenshot.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--zO7iCSlC--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://cdn-images-1.medium.com/max/3200/0%2AcGGp2jb6qjuWG59N.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--zO7iCSlC--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://cdn-images-1.medium.com/max/3200/0%2AcGGp2jb6qjuWG59N.png" width="800" height="243"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Implementation for Generating Screenshot With MaxDiffPixels&lt;/strong&gt;&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;test('allow max pixel diff while compare screenshot', async ({ page }) =&amp;gt; {
  await page.goto('https://ecommerce-playground.lambdatest.io/')
  await page.getByRole("textbox", {name : "Search For Products"}).fill('test compare screenshot')
  await expect(page).toHaveScreenshot({maxDiffPixels : 800});
});
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;On the &lt;em&gt;page&lt;/em&gt;, locate the search input field using Playwright locator &lt;em&gt;getByRole()&lt;/em&gt; by supplying the role as a &lt;em&gt;textbox&lt;/em&gt; and the name of the field, which is &lt;em&gt;Search For Products&lt;/em&gt;. Fill the text ‘test &lt;em&gt;compare screenshot&lt;/em&gt;’ in the located input field.&lt;/p&gt;

&lt;p&gt;Post filling in the text, capture a screenshot of the page using the &lt;em&gt;toHaveScreenshot()&lt;/em&gt; method, but this time setting &lt;em&gt;maxDiffPixels&lt;/em&gt; parameter 800, which means that if the actual and expected image differ by less than 800 pixels, it is okay to pass the test.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--MlFO73A_--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://cdn-images-1.medium.com/max/2304/0%2A8v9xpaG3P4rl5ED4.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--MlFO73A_--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://cdn-images-1.medium.com/max/2304/0%2A8v9xpaG3P4rl5ED4.png" width="800" height="229"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Execution&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Run the test using &lt;em&gt;npx playwright -g “allow max pixel diff while compare screenshot”&lt;/em&gt; command.&lt;/p&gt;

&lt;p&gt;As expected, the test passes since the pixel difference is 784, which is less than the 800 acceptable limit we set.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--KCpbpcBQ--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://cdn-images-1.medium.com/max/3200/0%2AchuTfzfGW0g6DSF7.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--KCpbpcBQ--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://cdn-images-1.medium.com/max/3200/0%2AchuTfzfGW0g6DSF7.png" width="800" height="390"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;There are other parameters available to customize and configure the Playwright screenshot comparison. Do check the official documents for the same.&lt;/p&gt;

&lt;p&gt;We have primarily used the Chromium browser to run our tests. This configuration is done in the &lt;em&gt;playwright.config.ts&lt;/em&gt; file. However, based on the requirement, alternate browsers and mobile viewports can be enabled in the file, which by default are commented out.&lt;/p&gt;

&lt;p&gt;The screenshot of the complete file is provided below, and as we can see, only Chromium is enabled, but that can be changed easily by uncommenting the required browser and viewport.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--2Vcbwnwq--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://cdn-images-1.medium.com/max/2000/0%2AesTvwctVbiI3AfrE.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--2Vcbwnwq--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://cdn-images-1.medium.com/max/2000/0%2AesTvwctVbiI3AfrE.png" width="800" height="1435"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The downside of this approach is that we have to install all the required browsers locally. Installing browsers is not a big deal, but then comes the question of running the tests against different operating systems and a combination of operating systems and browsers.&lt;/p&gt;

&lt;p&gt;To overcome this hindrance, we will leverage the cloud grid provided by LambdaTest, an AI-powered test orchestration and execution platform, and run the tests on a cloud-based scalable infrastructure. Using a cloud grid removes the dependency of installing various browser engines and setting up multiple operating system based infra to run the tests.&lt;/p&gt;

&lt;p&gt;LambdaTest helps in Playwright screenshot comparison using its AI-powered SmartUI platform to ensure the quality and accuracy of your web application across various browsers and devices.&lt;br&gt;
&lt;iframe width="710" height="399" src="https://www.youtube.com/embed/F-RvD2UsqR4"&gt;
&lt;/iframe&gt;
&lt;br&gt;
Catch up on the latest tutorial around the &lt;a href="https://www.lambdatest.com/learning-hub/playwright-visual-regression-testing?utm_source=devto&amp;amp;utm_medium=organic&amp;amp;utm_campaign=dec_26&amp;amp;utm_term=bw&amp;amp;utm_content=learning_hub" rel="noopener noreferrer"&gt;Playwright visual regression testing&lt;/a&gt;, &lt;a href="https://www.lambdatest.com/automation-testing?utm_source=devto&amp;amp;utm_medium=organic&amp;amp;utm_campaign=dec_26&amp;amp;utm_term=bw&amp;amp;utm_content=webpage" rel="noopener noreferrer"&gt;automation testing&lt;/a&gt;, and more. Subscribe to the &lt;a href="https://www.youtube.com/c/LambdaTest?sub_confirmation=1" rel="noopener noreferrer"&gt;LambdaTest YouTube Channel&lt;/a&gt; for quick updates.&lt;/p&gt;

&lt;p&gt;It provides an AI-powered Smart UI testing platform to run automated visual tests across 3000+ desktop and mobile environments. It uses AI algorithms to compare screenshots and detect differences in layout, text, graphics, or other elements. This ensures your web application looks and functions consistently on different devices and browsers.&lt;/p&gt;

&lt;p&gt;Refer to the documentation to get started with &lt;a href="https://www.lambdatest.com/support/docs/playwright-visual-regression/?utm_source=devto&amp;amp;utm_medium=organic&amp;amp;utm_campaign=dec_26&amp;amp;utm_term=bw&amp;amp;utm_content=support_doc" rel="noopener noreferrer"&gt;Smart UI testing with Playwright&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Let us now see the Playwright screenshot comparison on the cloud grid provided by LambdaTest.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Efficiently manage your strings! Use our &lt;a href="https://www.lambdatest.com/free-online-tools/string-split-by-delimiter?utm_source=devto&amp;amp;utm_medium=organic&amp;amp;utm_campaign=dec_26&amp;amp;utm_term=bw&amp;amp;utm_content=free_online_tools" rel="noopener noreferrer"&gt;String split by delimiter&lt;/a&gt; tool for easy parsing.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2&gt;
  
  
  How to Perform Playwright Screenshot Comparison on Cloud?
&lt;/h2&gt;

&lt;p&gt;To power our screenshot comparison, we will be leveraging the LambdaTest Smart UI platform, where we can access various different browsers and operating systems versions. The ready availability of infra also helps perform testing at scale with minimum hassle.&lt;/p&gt;

&lt;p&gt;To leverage the LambdaTest cloud grid, we first need to configure the username and API access key, which can be obtained from your LambdaTest Profile. To configure the username and API access key for our project to do so, we will store them in a .&lt;em&gt;env&lt;/em&gt; file.&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;LT_USERNAME=&amp;lt;LT_USERNAME&amp;gt;
LT_ACCESS_KEY=&amp;lt;LT_ACCESS_KEY&amp;gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;Remember to replace the &lt;em&gt;&amp;lt; LT_USERNAME &amp;gt;&lt;/em&gt; and &lt;em&gt;&amp;lt; LT_ACCESS_KEY &amp;gt;&lt;/em&gt; with proper credentials when running the tests.&lt;/p&gt;

&lt;p&gt;Next, we need to set up the &lt;em&gt;playwright.config.ts&lt;/em&gt; to be able to leverage the infrastructure on the cloud. For this demo, we will be using Windows 11 and Chrome (latest).&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;import { defineConfig, devices } from '@playwright/test';


export default defineConfig({  
  testDir: './tests',  
  fullyParallel: true,  
  forbidOnly: !!process.env.CI,  
  retries: process.env.CI ? 2 : 0,  
  workers: process.env.CI ? 1 : undefined,  
  reporter: 'html',  
  use: {    
    trace: 'on-first-retry',
  },


  /* Configure projects for major browsers */
  projects: [
    {
      name: "chrome:latest:Windows 11@lambdatest",    
    },


    /* Comment the above lines &amp;amp; Uncomment the below lines to run on local */
    // {
    //   name: 'chromium',
    //   use: { ...devices['Desktop Chrome'] },
    // },  
  ],
});
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;To connect to the LambdaTest cloud grid info we have amended the &lt;em&gt;projects&lt;/em&gt; array to add the Windows 11 and Chrome setup and added the keyword ‘&lt;em&gt;@lambdatest&lt;/em&gt;’.&lt;/p&gt;

&lt;p&gt;If we want to run the tests locally, we will simply comment the LambdaTest line and uncomment the Chromium browser or add any other browser on which you intend to run the tests.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--8fjfPQBi--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://cdn-images-1.medium.com/max/2856/0%2A9viWgxac0JibS_B-.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--8fjfPQBi--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://cdn-images-1.medium.com/max/2856/0%2A9viWgxac0JibS_B-.png" width="800" height="398"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The next file needed to leverage the LambdaTest cloud grid, which we will look at, is the &lt;em&gt;lambdatest-setup.ts&lt;/em&gt;. Here is how the complete file looks like.&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;import * as base from "@playwright/test";
import path from "path";
import { chromium } from "@playwright/test"
import dotenv from 'dotenv';
dotenv.config();


// LambdaTest capabilities
const capabilities = {
  browserName: "Chrome", // Browsers allowed: `Chrome`, `MicrosoftEdge`, `pw-chromium`, `pw-firefox` and `pw-webkit`
  browserVersion: "latest",
  "LT:Options": {
    platform: "Windows 11",
    build: "Playwright SmartUI Build",
    name: "Playwright Compare Screenshots",
    user: process.env.LT_USERNAME,
    accessKey: process.env.LT_ACCESS_KEY,    
    network: true,
    video: true,
    console: true,    
    geoLocation: "" ,// country code can be fetched from https://www.lambdatest.com/capabilities-generator/
    "smartUIProjectName": "Playwright-SmartUI-Project",
  },
};


// Patching the capabilities dynamically according to the project name.
const modifyCapabilities = (configName, testName) =&amp;gt; {
  let config = configName.split("@lambdatest")[0];
  let [browserName, browserVersion, platform] = config.split(":");
  capabilities.browserName = browserName
    ? browserName
    : capabilities.browserName;
  capabilities.browserVersion = browserVersion
    ? browserVersion
    : capabilities.browserVersion;
  capabilities["LT:Options"]["platform"] = platform
    ? platform
    : capabilities["LT:Options"]["platform"];
  capabilities["LT:Options"]["name"] = testName;
};


const getErrorMessage = (obj, keys) =&amp;gt;
  keys.reduce(
    (obj, key) =&amp;gt; (typeof obj == "object" ? obj[key] : undefined),
    obj
  );


const test = base.test.extend({
  page: async ({ page, playwright }, use, testInfo) =&amp;gt; {  
    // Configure LambdaTest platform for cross-browser testing
    let fileName = testInfo.file.split(path.sep).pop();
    if (testInfo.project.name.match(/lambdatest/)) {
      modifyCapabilities(
        testInfo.project.name,
        `${testInfo.title} - ${fileName}`
      );
      const browser = await chromium.connect({
        wsEndpoint: `wss://cdp.lambdatest.com/playwright?capabilities=${encodeURIComponent(
          JSON.stringify(capabilities)
        )}`,
      });


      const ltPage = await browser.newPage(testInfo.project.use);
      await use(ltPage);

      await ltPage.close();
      await browser.close();
    } else {
      // Run tests in local in case of local config provided
      await use(page);
    }
  },
});


export default test;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;&lt;strong&gt;Code Walkthrough&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Let us now understand the individual components of the file in detail one by one.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Step 1: Understand Imports&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--_uujiUAR--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://cdn-images-1.medium.com/max/2000/0%2AjwP8uxxI-4HmBoWB.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--_uujiUAR--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://cdn-images-1.medium.com/max/2000/0%2AjwP8uxxI-4HmBoWB.png" width="800" height="426"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The imports are standard apart from the dotenv package, which we use to read the &lt;em&gt;USERNAME&lt;/em&gt; and &lt;em&gt;ACCESS_KEY&lt;/em&gt; from the.env file.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Step 2: Understanding the JSON capabilities&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;The capabilities JSON object is where we configure the behavior of the LambdaTest cloud grid.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--IPAHPWH3--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://cdn-images-1.medium.com/max/3200/0%2AFQELonqc3je_cHdh.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--IPAHPWH3--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://cdn-images-1.medium.com/max/3200/0%2AFQELonqc3je_cHdh.png" width="800" height="175"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Set the default browser and version to be used on the cloud grid.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--WS3YE1fK--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://cdn-images-1.medium.com/max/2000/0%2AP7km9FMXdSgn0IJY.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--WS3YE1fK--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://cdn-images-1.medium.com/max/2000/0%2AP7km9FMXdSgn0IJY.png" width="800" height="406"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;&lt;em&gt;platform:&lt;/em&gt;&lt;/strong&gt; Used to set the default operating system.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;&lt;em&gt;build:&lt;/em&gt;&lt;/strong&gt; Specifies the name of the current build under which all tests will be grouped.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;&lt;em&gt;name:&lt;/em&gt;&lt;/strong&gt; Specifies the test name.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--kSVlEz0y--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://cdn-images-1.medium.com/max/2000/0%2A4DtFGibHpYuF0CFm.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--kSVlEz0y--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://cdn-images-1.medium.com/max/2000/0%2A4DtFGibHpYuF0CFm.png" width="800" height="337"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The &lt;em&gt;USERNAME&lt;/em&gt; and &lt;em&gt;ACCESS_KEY&lt;/em&gt; required to connect to the LambdaTest cloud grid are read from environment values.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--G18Jblk8--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://cdn-images-1.medium.com/max/2000/0%2ANLmKuzMuGmS6Ljsv.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--G18Jblk8--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://cdn-images-1.medium.com/max/2000/0%2ANLmKuzMuGmS6Ljsv.png" width="520" height="406"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;&lt;em&gt;network:&lt;/em&gt;&lt;/strong&gt; Used to configure if the cloud grid captures network logs.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;&lt;em&gt;video:&lt;/em&gt;&lt;/strong&gt; Used to configure if the cloud grid captures video of test execution&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;&lt;em&gt;console:&lt;/em&gt;&lt;/strong&gt; Used to configure if the cloud grid captures console logs&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--x-0ksrbI--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://cdn-images-1.medium.com/max/3200/0%2AKTYWf-oIgUot7CsQ.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--x-0ksrbI--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://cdn-images-1.medium.com/max/3200/0%2AKTYWf-oIgUot7CsQ.png" width="800" height="145"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;em&gt;geoLocation&lt;/em&gt; is used to configure the geographic location used to run the test.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--_iN6Sgzc--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://cdn-images-1.medium.com/max/2336/0%2And954XSoBO8Yu64V.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--_iN6Sgzc--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://cdn-images-1.medium.com/max/2336/0%2And954XSoBO8Yu64V.png" width="800" height="226"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;em&gt;smartUIProjectName is the name of the Smart UI project on the LambdaTest dashboard.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;Here is the complete JSON object for reference.&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;// LambdaTest capabilities
const capabilities = {
  browserName: "Chrome", // Browsers allowed: `Chrome`, `MicrosoftEdge`, `pw-chromium`, `pw-firefox` and `pw-webkit`
  browserVersion: "latest",
  "LT:Options": {
    platform: "Windows 11",
    build: "Playwright SmartUI Build",
    name: "Playwright Compare Screenshots",
    user: process.env.LT_USERNAME,
    accessKey: process.env.LT_ACCESS_KEY,    
    network: true,
    video: true,
    console: true,    
    geoLocation: "" ,// country code can be fetched from https://www.lambdatest.com/capabilities-generator/
    smartUIProjectName: "Playwright-SmartUI-Screenshot-Compare",
  },
};
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;&lt;strong&gt;Step 3: Overriding the *@playwright/test *method to facilitate LambdaTest cloud grid connection&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--NWqTeWL2--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://cdn-images-1.medium.com/max/3164/0%2Au_O4XAZSne3JuXGi.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--NWqTeWL2--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://cdn-images-1.medium.com/max/3164/0%2Au_O4XAZSne3JuXGi.png" width="800" height="494"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The next part of the &lt;em&gt;lambdatest-setup.ts&lt;/em&gt; file is to override the &lt;em&gt;@playwright/test&lt;/em&gt; method to ensure that we can run the tests either on the cloud grid or locally.&lt;/p&gt;

&lt;p&gt;We check the &lt;em&gt;playwright.config.ts&lt;/em&gt; file to see if the project’s array contains the keyword &lt;em&gt;lambdatest&lt;/em&gt;. If it does perform a connection using the capabilities object to LambdaTest Smart UI and initialize the page object using cloud infrastructure.&lt;/p&gt;

&lt;p&gt;For connecting to the cloud grid infrastructure, a WebSocket connection using the CDP (Chrome DevTools Protocol) endpoint is used. A CDP endpoint refers to the URL or address that allows communication between a client (such as a web browser) and the Chrome DevTools Protocol server.&lt;/p&gt;

&lt;p&gt;The Chrome DevTools Protocol is a debugging protocol used by various browser developer tools to interact with and control a web browser instance programmatically.&lt;/p&gt;

&lt;p&gt;To establish a WebSocket connection with the Chrome DevTools Protocol server, you need to specify the CDP endpoint. The endpoint typically follows the format: &lt;em&gt;wss://{host}:{port}/devtools/{pageTarget}&lt;/em&gt;. More details below:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;{host}:&lt;/strong&gt; Refers to the host or IP address where the Chrome DevTools Protocol server is running. It can be a local or remote address.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;{port}:&lt;/strong&gt; Represents the port number used by the Chrome DevTools Protocol server for WebSocket connections. The default port is usually 9222.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;{pageTarget}:&lt;/strong&gt; Specifies the target page or tab in the web browser for which you want to establish a DevTools Protocol connection. This can be a unique identifier or a specific page URL.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;By connecting to the CDP endpoint via a WebSocket, you can send commands, receive responses, and listen for various events related to the web browser’s behavior, network activity, debugging, and more. This enables developers and tools to automate tasks, analyze performance, debug code, and gather information about the web page or application being inspected.&lt;/p&gt;

&lt;p&gt;If the keyword is not detected, a local instance of the Playwright page is returned using the browser configured in &lt;em&gt;playwright.config.ts&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;This completes the setup required to leverage the LambdaTest Smart UI. Now, we’ll craft the test scenario, closely resembling the one employed previously during our local screenshot comparison testing.&lt;/p&gt;

&lt;h2&gt;
  
  
  Test Scenario
&lt;/h2&gt;

&lt;ol&gt;
&lt;li&gt;&lt;p&gt;Navigate to the LambdaTest demo eCommerce website homepage and capture the baseline screenshot.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Navigate to the LambdaTest demo eCommerce website homepage, search for Apple products, and take another screenshot.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Need to convert text quickly? Use our &lt;a href="https://www.lambdatest.com/free-online-tools/text-lowercase?utm_source=devto&amp;amp;utm_medium=organic&amp;amp;utm_campaign=dec_26&amp;amp;utm_term=bw&amp;amp;utm_content=free_online_tools" rel="noopener noreferrer"&gt;Text lowercase&lt;/a&gt; tool for instant results.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;/li&gt;
&lt;/ol&gt;

&lt;h2&gt;
  
  
  Implementation
&lt;/h2&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;import test from "../lambdatest-setup";
est('capture full page screenshot using SMARTUI', async ({ page }) =&amp;gt; {
  await page.goto('https://ecommerce-playground.lambdatest.io/')
  await page.evaluate((_) =&amp;gt; {},
    'lambdatest_action: {"action":"smartui.takeScreenshot","arguments":{"fullPage":true,"screenshotName":"homepage-without-search.png"}}')
  await page.evaluate(_ =&amp;gt; {}, 'lambdatest_action: {"action":"setTestStatus","arguments":{"status":"passed","remark":"Screenshot captured"}}')
});
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;Let us see a step-by-step walkthrough of the code.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Step 1:&lt;/strong&gt; The &lt;em&gt;test&lt;/em&gt; method being used now is from &lt;em&gt;lambdatest-setup.ts&lt;/em&gt; instead of &lt;em&gt;@playwright/test&lt;/em&gt;.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--q3lW7ZjL--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://cdn-images-1.medium.com/max/2000/0%2AOplVC_e-qr4ingy9.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--q3lW7ZjL--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://cdn-images-1.medium.com/max/2000/0%2AOplVC_e-qr4ingy9.png" width="800" height="313"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Step 2:&lt;/strong&gt; Navigate to the demo website homepage using the &lt;em&gt;page.goto()&lt;/em&gt; method.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--7EDOKEeO--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://cdn-images-1.medium.com/max/2396/0%2Asy5WtgvHswehRISx.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--7EDOKEeO--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://cdn-images-1.medium.com/max/2396/0%2Asy5WtgvHswehRISx.png" width="800" height="220"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Step 3:&lt;/strong&gt; The &lt;em&gt;evaluate()&lt;/em&gt; function requests the SmartUI to trigger the &lt;em&gt;takeScreenshot&lt;/em&gt; function. Additional arguments are used to dictate the function to take a &lt;em&gt;fullPage&lt;/em&gt; screenshot and also specify the &lt;em&gt;screenshotName&lt;/em&gt;.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--MPkNlkX1--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://cdn-images-1.medium.com/max/3200/0%2AryEwiwIjLAzu43Gx.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--MPkNlkX1--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://cdn-images-1.medium.com/max/3200/0%2AryEwiwIjLAzu43Gx.png" width="800" height="124"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Step 4:&lt;/strong&gt; Finally, we run &lt;em&gt;evaluate()&lt;/em&gt; again, this time to mark the test successful, saying the screenshot has been captured.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--9NOBcPSS--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://cdn-images-1.medium.com/max/3200/0%2AkoqIERs_LyMI9IUO.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--9NOBcPSS--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://cdn-images-1.medium.com/max/3200/0%2AkoqIERs_LyMI9IUO.png" width="800" height="110"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Execution
&lt;/h2&gt;

&lt;p&gt;Run the test using &lt;em&gt;npx playwright test -g “capture full page screenshot using SMARTUI&lt;/em&gt;”.&lt;/p&gt;

&lt;p&gt;The build gets passed successfully, which can be seen on the LambdaTest Web Automation Dashboard.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s---nA9M_sc--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://cdn-images-1.medium.com/max/3200/0%2AVpJCuvqMXfRGN6gp.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s---nA9M_sc--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://cdn-images-1.medium.com/max/3200/0%2AVpJCuvqMXfRGN6gp.png" width="800" height="416"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;On clicking the name of the test, we can also see detailed execution logs.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--JdRtejjc--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://cdn-images-1.medium.com/max/3200/0%2Ajrdl0-R45hk5qZ-J.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--JdRtejjc--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://cdn-images-1.medium.com/max/3200/0%2Ajrdl0-R45hk5qZ-J.png" width="800" height="416"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The LambdaTest Smart UI page also reflects the details under the name we configured in the capabilities object in &lt;em&gt;lambdatest-setup.ts&lt;/em&gt;.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--a1-VYrC2--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://cdn-images-1.medium.com/max/3200/0%2A0FbRRLDTdIR17rth.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--a1-VYrC2--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://cdn-images-1.medium.com/max/3200/0%2A0FbRRLDTdIR17rth.png" width="800" height="566"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;When the test passes, the screenshot captured is treated as a baseline for future comparisons.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--MvfD1aBy--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://cdn-images-1.medium.com/max/3200/0%2ASbSYrsRdDDYQd4Gy.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--MvfD1aBy--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://cdn-images-1.medium.com/max/3200/0%2ASbSYrsRdDDYQd4Gy.png" width="800" height="243"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;If the captured screenshot fails for some reason, such as a timeout network issue or any other reason, a screenshot of a subsequent run can be manually marked as the baseline.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--lagZnVkL--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://cdn-images-1.medium.com/max/3200/0%2A4yCcZJcGwhqEaPj7.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--lagZnVkL--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://cdn-images-1.medium.com/max/3200/0%2A4yCcZJcGwhqEaPj7.png" width="800" height="111"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;This is what the baseline screen looks like.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--dp-5khWu--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://cdn-images-1.medium.com/max/3200/0%2AyG60tFBWBME8OTyT.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--dp-5khWu--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://cdn-images-1.medium.com/max/3200/0%2AyG60tFBWBME8OTyT.png" width="800" height="417"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Now that a baseline is established, let us amend the test to search for Apple products.&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;test('capture full page screenshot using SMARTUI', async ({ page }) =&amp;gt; {
  await page.goto('https://ecommerce-playground.lambdatest.io/')
  await page.getByRole("textbox", {name : "Search For Products"}).fill('apple')
  await page.evaluate((_) =&amp;gt; {},
    'lambdatest_action: {"action":"smartui.takeScreenshot","arguments":{"fullPage":true,"screenshotName":"homepage-without-search.png"}}')
  await page.evaluate(_ =&amp;gt; {}, 'lambdatest_action: {"action":"setTestStatus","arguments":{"status":"passed","remark":"Screenshot captured"}}')
});
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;The modified test code has one line of change, i.e., locating the search field and filling in the text apple.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--4vV2URpF--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://cdn-images-1.medium.com/max/2856/0%2AueiDiSUytqnE5ETu.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--4vV2URpF--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://cdn-images-1.medium.com/max/2856/0%2AueiDiSUytqnE5ETu.png" width="800" height="185"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;We run the test again using the command &lt;em&gt;npx playwright test -g “capture full page screenshot using SMARTUI&lt;/em&gt;”.&lt;/p&gt;

&lt;p&gt;Smart UI has found issues as expected since we introduced it for demo purposes by searching Apple products.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--xgYsQerC--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://cdn-images-1.medium.com/max/3200/0%2AP3wNpeUCAAXZXfCn.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--xgYsQerC--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://cdn-images-1.medium.com/max/3200/0%2AP3wNpeUCAAXZXfCn.png" width="800" height="238"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The highlighted section shows the thresholds and the % mismatch. It also has the option to approve/reject the changes.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--06mTrt8H--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://cdn-images-1.medium.com/max/3200/0%2ArNxZORvUpkJnZvcG.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--06mTrt8H--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://cdn-images-1.medium.com/max/3200/0%2ArNxZORvUpkJnZvcG.png" width="800" height="415"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The Smart UI project is also configurable from the &lt;a href="https://smartui.lambdatest.com/?utm_source=devto&amp;amp;utm_medium=organic&amp;amp;utm_campaign=dec_26&amp;amp;utm_term=bw&amp;amp;utm_content=webpage" rel="noopener noreferrer"&gt;LambdaTest Smart UI Dashboard&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--5Gul9lIh--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://cdn-images-1.medium.com/max/3200/0%2AohihRSQ_9iwobpno.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--5Gul9lIh--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://cdn-images-1.medium.com/max/3200/0%2AohihRSQ_9iwobpno.png" width="800" height="209"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Some important settings have been highlighted &amp;amp; explained below&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;The addition of an Approver allows the configuration of test approvers.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Project Name can be configured in SmartUI and within the capabilities of JSON in the &lt;em&gt;lambdatest-setup.ts file&lt;/em&gt;.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;The Pixel Threshold establishes the granularity at which pixel blocks are generated.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;The Custom Mis-Match % Acceptance Rate allows the configuration of pixel-to-pixel acceptance below the specified percentage for automatic approval and rejection.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--6Rp8i5nI--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://cdn-images-1.medium.com/max/3200/0%2A32y9Upv4ew1fY0SV.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--6Rp8i5nI--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://cdn-images-1.medium.com/max/3200/0%2A32y9Upv4ew1fY0SV.png" width="800" height="566"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Conclusion
&lt;/h2&gt;

&lt;p&gt;In summary, Playwright offers a robust solution for screenshot comparison, helping teams effectively manage visual regression and maintain the visual consistency of their apps and websites.&lt;/p&gt;

&lt;p&gt;With Playwright’s robust API and wide browser compatibility, developers and testers can effortlessly automate browser interactions and capture screenshots. By utilizing Playwright’s screenshot comparison features, teams can create baseline screenshots and compare them to test runs or production updates later.&lt;/p&gt;

&lt;p&gt;This enables the identification of visual regressions stemming from code modifications, layout discrepancies, or unexpected rendering differences across various browsers and devices.&lt;/p&gt;

&lt;p&gt;We have also seen how we can leverage the Smart UI platform of the LambdaTest cloud grid to compare screenshots. Using a cloud grid allows us to perform screenshot comparisons on various browsers and versions. This helps make the test suite more robust and scalable.&lt;/p&gt;

</description>
      <category>testing</category>
      <category>screenshot</category>
      <category>playwright</category>
      <category>ui</category>
    </item>
    <item>
      <title>How To Use Playwright Inspector For Debugging</title>
      <dc:creator>Jaydeep Karale</dc:creator>
      <pubDate>Mon, 04 Dec 2023 10:43:23 +0000</pubDate>
      <link>https://dev.to/_jaydeepkarale/how-to-use-playwright-inspector-for-debugging-303k</link>
      <guid>https://dev.to/_jaydeepkarale/how-to-use-playwright-inspector-for-debugging-303k</guid>
      <description>&lt;p&gt;Debugging is the process of identifying and resolving errors or bugs in software. In web &lt;a href="https://www.lambdatest.com/automation-testing?utm_source=devto&amp;amp;utm_medium=organic&amp;amp;utm_campaign=nov_28&amp;amp;utm_term=bw&amp;amp;utm_content=webpage" rel="noopener noreferrer"&gt;automation testing&lt;/a&gt;, debugging plays a crucial role in ensuring the reliability and accuracy of automated tests. Additionally, it enables the validation of functionality by applying dynamic values.&lt;/p&gt;

&lt;p&gt;According to the Systems Sciences Institute at IBM, the expense of addressing an error discovered after product release was four to five times higher than identifying it during the design phase and even up to 100 times greater than one detected during the maintenance phase.&lt;/p&gt;

&lt;p&gt;The debugging process typically involves noting the error, identifying the error location based on expertise, setting debugger breakpoints, analyzing the code step-by-step at runtime, finding the exact location of the issue, fixing the problematic code line, and validating the code to ensure a successful error fix.&lt;/p&gt;

&lt;p&gt;Choosing an &lt;a href="https://www.lambdatest.com/blog/automation-testing-frameworks/?utm_source=devto&amp;amp;utm_medium=organic&amp;amp;utm_campaign=nov_28&amp;amp;utm_term=bw&amp;amp;utm_content=blog" rel="noopener noreferrer"&gt;automation testing framework&lt;/a&gt; with effective and robust debugging tooling is key to writing and maintaining a high-quality test automation suite. &lt;a href="https://www.lambdatest.com/playwright?utm_source=devto&amp;amp;utm_medium=organic&amp;amp;utm_campaign=nov_28&amp;amp;utm_term=bw&amp;amp;utm_content=webpage" rel="noopener noreferrer"&gt;Playwright&lt;/a&gt;, an open-source framework released by Microsoft in 2020, is quickly becoming popular for cross-browser testing.&lt;/p&gt;

&lt;p&gt;Playwright can work with the browser at the API level and provides robust tooling with the built-in Playwright Inspector.Playwright Inspector simplifies creating automation scripts by providing auto code generation capabilities. With Playwright Inspector, developers, and testers can interactively explore web applications, inspect elements, and perform actions such as clicking buttons, filling forms, and navigating through pages.&lt;/p&gt;

&lt;p&gt;Playwright Inspector lets you view the &lt;a href="https://www.lambdatest.com/blog/document-object-model/?utm_source=devto&amp;amp;utm_medium=organic&amp;amp;utm_campaign=nov_28&amp;amp;utm_term=bw&amp;amp;utm_content=blog" rel="noopener noreferrer"&gt;DOM&lt;/a&gt; structure of a webpage, inspect CSS styles, and interact with web elements using the Playwright API. It allows stepping through tests, live locators editing, picking locators, and seeing actionability logs.&lt;/p&gt;

&lt;p&gt;One of the standout features of Playwright Inspector is its ability to record and replay user interactions. This functionality allows developers to reproduce bugs and see how their web applications behave in different scenarios. This information can be used to debug and fix issues more effectively.&lt;/p&gt;

&lt;p&gt;In this blog, we will explore Playwright Inspector, a graphical user interface (GUI) tool that allows us to inspect and debug Playwright scripts interactively in great detail.&lt;/p&gt;

&lt;p&gt;So, let’s get started!&lt;/p&gt;

&lt;h2&gt;
  
  
  What is Playwright?
&lt;/h2&gt;

&lt;p&gt;Playwright, developed by Microsoft, is an open-source end-to-end &lt;a href="https://www.lambdatest.com/learning-hub/web-testing?utm_source=devto&amp;amp;utm_medium=organic&amp;amp;utm_campaign=nov_28&amp;amp;utm_term=bw&amp;amp;utm_content=learning_hub" rel="noopener noreferrer"&gt;web testing&lt;/a&gt; and browser automation framework. It offers excellent &lt;a href="https://www.lambdatest.com/learning-hub/cross-browser-compatibility?utm_source=devto&amp;amp;utm_medium=organic&amp;amp;utm_campaign=nov_28&amp;amp;utm_term=bw&amp;amp;utm_content=learning_hub" rel="noopener noreferrer"&gt;cross-browser compatibility&lt;/a&gt; and user-friendly features, making it an ideal option for automation and quality assurance purposes.&lt;/p&gt;

&lt;p&gt;It is flexible and supports multiple languages such as Python, TypeScript, JavaScript, Java, and C#. It uses a single automation API which makes it reliable and fast and produces consistent results while performing &lt;a href="https://www.lambdatest.com/online-browser-testing?utm_source=devto&amp;amp;utm_medium=organic&amp;amp;utm_campaign=nov_28&amp;amp;utm_term=bw&amp;amp;utm_content=webpage" rel="noopener noreferrer"&gt;cross browser testing&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Playwright provides a robust and powerful set of testing features that can be used to simulate user interactions with the web application. With Playwright, we can test complex scenarios such as login flows, file uploads, and other interactions that are difficult to test with other testing frameworks.&lt;/p&gt;

&lt;p&gt;It offers a wide range of built-in and custom locators to help developers easily and efficiently locate elements on web pages. Locators can identify elements based on their attributes, text content, position on the page, and more. &lt;a href="https://www.lambdatest.com/blog/playwright-locators/?utm_source=devto&amp;amp;utm_medium=organic&amp;amp;utm_campaign=nov_28&amp;amp;utm_term=bw&amp;amp;utm_content=blog" rel="noopener noreferrer"&gt;Playwright locators&lt;/a&gt; help to reliably identify elements on DOM and thus build resilient and robust test suites.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--VAkGIH1c--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://cdn-images-1.medium.com/max/2448/0%2AB2ZRsDynaLqDx_15.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--VAkGIH1c--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://cdn-images-1.medium.com/max/2448/0%2AB2ZRsDynaLqDx_15.png" width="800" height="311"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;If you are preparing for an interview you can learn more through &lt;a href="https://www.lambdatest.com/learning-hub/playwright-interview-questions?utm_source=devto&amp;amp;utm_medium=organic&amp;amp;utm_campaign=nov_28&amp;amp;utm_term=bw&amp;amp;utm_content=learning_hub" rel="noopener noreferrer"&gt;Playwright interview questions&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;A quick summary of features of Playwright&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Multi-Browser Support&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Playwright supports cross-browser automation, which includes Chrome, Firefox, Safari, and Microsoft Edge. This makes it easier for developers to write tests that can run on different browsers.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Advanced Browser Automation&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Playwright provides a rich set of APIs for automating browser interactions such as page navigation, filling out forms, clicking buttons, and much more.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Powerful Debugging Capabilities&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Playwright allows developers to easily debug their tests with tools like the built-in debugger and visual logs, making it easier to diagnose issues during test development.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Easy to Use and Flexible&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Playwright is designed to be easy to use and flexible, with a simple API that allows developers to write tests in their preferred programming languages, such as JavaScript, TypeScript, or Python.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Scalability&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Playwright allows developers to run tests in parallel across multiple browsers and devices, making it easy to scale up test suites to handle large-scale testing scenarios.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Auto-wait and Retry&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Playwright’s auto-wait and retry features make it easier to handle flaky tests by automatically waiting for page elements to become available and retrying failed test steps, improving the reliability of tests.&lt;br&gt;
&lt;iframe width="710" height="399" src="https://www.youtube.com/embed/wawbt1cATsk"&gt;
&lt;/iframe&gt;
&lt;br&gt;
You can also subscribe to the &lt;a href="https://www.youtube.com/channel/UCCymWVaTozpEng_ep0mdUyw?sub_confirmation=1" rel="noopener noreferrer"&gt;LambdaTest YouTube Channel&lt;/a&gt; and stay updated with the latest tutorial around &lt;a href="https://www.lambdatest.com/selenium-automation?utm_source=devto&amp;amp;utm_medium=organic&amp;amp;utm_campaign=nov_28&amp;amp;utm_term=bw&amp;amp;utm_content=webpage" rel="noopener noreferrer"&gt;Selenium testing&lt;/a&gt;, &lt;a href="https://www.lambdatest.com/blog/playwright-framework/?utm_source=devto&amp;amp;utm_medium=organic&amp;amp;utm_campaign=nov_28&amp;amp;utm_term=bw&amp;amp;utm_content=blog" rel="noopener noreferrer"&gt;Playwright browser testing&lt;/a&gt;, &lt;a href="https://www.lambdatest.com/appium-mobile-testing?utm_source=devto&amp;amp;utm_medium=organic&amp;amp;utm_campaign=nov_28&amp;amp;utm_term=bw&amp;amp;utm_content=webpage" rel="noopener noreferrer"&gt;Appium&lt;/a&gt;, and more.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Ensure compatibility across platforms with &lt;a href="https://www.lambdatest.com/?utm_source=devto&amp;amp;utm_medium=organic&amp;amp;utm_campaign=nov_28&amp;amp;utm_term=bw&amp;amp;utm_content=webpage" rel="noopener noreferrer"&gt;cross browser device testing&lt;/a&gt;.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h2&gt;
  
  
  What is a Playwright Inspector?
&lt;/h2&gt;

&lt;p&gt;Playwright Inspector is a GUI-based tool that helps developers &amp;amp; testers record scripts and debug &lt;a href="https://www.lambdatest.com/playwright-testing?utm_source=devto&amp;amp;utm_medium=organic&amp;amp;utm_campaign=nov_28&amp;amp;utm_term=bw&amp;amp;utm_content=webpage" rel="noopener noreferrer"&gt;Playwright tests&lt;/a&gt;. It helps generate boilerplate code, which can be a good starting point for building code. This helps developers &amp;amp; QA teams save time which may be required when writing everything from scratch.&lt;/p&gt;

&lt;p&gt;Developers can use the saved time to understand the code base and then confidently make relevant changes as and when needed to improve the &lt;a href="https://www.lambdatest.com/learning-hub/test-suite?utm_source=devto&amp;amp;utm_medium=organic&amp;amp;utm_campaign=nov_28&amp;amp;utm_term=bw&amp;amp;utm_content=learning_hub" rel="noopener noreferrer"&gt;test suites&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--DcCCvGW5--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://cdn-images-1.medium.com/max/2260/0%2AgGghT7M5n52A_lQD.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--DcCCvGW5--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://cdn-images-1.medium.com/max/2260/0%2AgGghT7M5n52A_lQD.png" width="800" height="813"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The Playwright Inspector can be started using the codegen command followed by the website URL to inspect. The URL is optional and can also be added when the browser starts.&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;playwright codegen https://ecommerce-playground.lambdatest.io/
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;
&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--Gq1l3ffX--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://cdn-images-1.medium.com/max/3200/0%2AFfpPPohWm7GAA_wq.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--Gq1l3ffX--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://cdn-images-1.medium.com/max/3200/0%2AFfpPPohWm7GAA_wq.png" width="800" height="429"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;On running the command by specifying the URL:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;&lt;p&gt;Playwright Inspector will open the mentioned website in Chromium by default&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Initiate the Playwright Inspector tool, record actions up to the current point, and showcase the corresponding code snippet.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Our action at this point has been to only navigate to the &lt;a href="https://ecommerce-playground.lambdatest.io/?utm_source=devto&amp;amp;utm_medium=organic&amp;amp;utm_campaign=nov_28&amp;amp;utm_term=bw&amp;amp;utm_content=ecommerce" rel="noopener noreferrer"&gt;LambdaTest eCommerce Playground&lt;/a&gt; URL code for that action to be present.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Playwright, by default, will display code using the Library API to handle the creation of the &lt;em&gt;browser, context&lt;/em&gt;, and &lt;em&gt;page&lt;/em&gt; explicitly.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;On the created page, the &lt;em&gt;goto()&lt;/em&gt; method is called with the URL specified on the command line.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Run Automated Playwright Tests Online.&lt;a href="https://accounts.lambdatest.com/register" rel="noopener noreferrer"&gt;Try LambdaTest Now!&lt;/a&gt;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;h2&gt;
  
  
  Features of Playwright Inspector
&lt;/h2&gt;

&lt;p&gt;Generating code first and subsequently refining it is a practical and time-saving approach. One of the USPs of Playwright is the Playwright Inspector tool, which helps developers record scripts and debug &lt;a href="https://www.lambdatest.com/playwright-testing-tool?utm_source=devto&amp;amp;utm_medium=organic&amp;amp;utm_campaign=nov_28&amp;amp;utm_term=bw&amp;amp;utm_content=webpage" rel="noopener noreferrer"&gt;Playwright tests&lt;/a&gt; using breakpoints. Here are some of the key features of the Playwright Inspector tool:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Boilerplate Code&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;With its intuitive interface and powerful features, Playwright Inspector makes it easy for developers to find and fix bugs in their code. One of the standout features of Playwright Inspector is its ability to record and replay user interactions.&lt;/p&gt;

&lt;p&gt;Playwright Inspector records user actions and helps find elements by using the Playwright locators and selecting the best locator for the element. This functionality allows developers to reproduce bugs and see how their web applications behave in different scenarios.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Network trace analysis&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;This feature enables tracking the performance of web applications and identifies any bottlenecks that may be affecting user experience. This information can then be used to optimize code to improve the performance of web applications.&lt;/p&gt;

&lt;p&gt;As Playwright supports multiple languages, you can choose your preferred language using the drop-down at the top right, as seen in the image below.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--Rm8-Qhbh--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://cdn-images-1.medium.com/max/3200/0%2A-M9NBIYEMPsIJ4rj.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--Rm8-Qhbh--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://cdn-images-1.medium.com/max/3200/0%2A-M9NBIYEMPsIJ4rj.png" width="800" height="429"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The Playwright Inspector tool will always use Chromium by default, but that behavior can be overridden using the &lt;em&gt;— -browser&lt;/em&gt; flag on the command line. As Playwright supports WebKit, Chromium, and Firefox, the other option we can use with &lt;em&gt;— -browser&lt;/em&gt; is WebKit.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Ensure your website’s compatibility across devices. &lt;a href="https://www.lambdatest.com/test-site-on-mobile?utm_source=devto&amp;amp;utm_medium=organic&amp;amp;utm_campaign=nov_28&amp;amp;utm_term=bw&amp;amp;utm_content=webpage" rel="noopener noreferrer"&gt;Test your website on different devices&lt;/a&gt; now!&lt;/p&gt;
&lt;/blockquote&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;playwright codegen https://ecommerce-playground.lambdatest.io/ --browser firefox
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;
&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--Sb9i5NgZ--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://cdn-images-1.medium.com/max/3200/0%2AZ82-BNq79JVgfYNO.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--Sb9i5NgZ--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://cdn-images-1.medium.com/max/3200/0%2AZ82-BNq79JVgfYNO.png" width="800" height="430"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Now we have seen some of the core features of Playwright Inspector. In the next section, we will learn more about its advantages and shortcomings.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Optimize your app’s performance on various gadgets with &lt;a href="https://www.lambdatest.com/real-device-cloud?utm_source=devto&amp;amp;utm_medium=organic&amp;amp;utm_campaign=nov_28&amp;amp;utm_term=bw&amp;amp;utm_content=webpage" rel="noopener noreferrer"&gt;device testing&lt;/a&gt;.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h2&gt;
  
  
  Advantages of Playwright Inspector
&lt;/h2&gt;

&lt;p&gt;The Playwright Inspector tool offers several advantages. Let’s look at some key benefits of the Playwright Inspector:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Easy Debugging&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Playwright Inspector simplifies the process of debugging Playwright scripts by providing a visual interface that allows developers to inspect the page, modify its state, and identify the cause of issues quickly.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Record and Replay&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Playwright Inspector offers a record and replay feature that allows testers and QA teams to record user actions on the page and replay them for debugging purposes.&lt;/p&gt;

&lt;p&gt;This feature can help identify issues that may only occur during specific user interactions.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Intuitive Interface&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Playwright Inspector has an intuitive and user-friendly interface. It is easy to navigate and use, even for testers and QA teams new to Playwright.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Robust Logging&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Playwright Inspector offers robust logging capabilities that allow testers and QA teams to trace the execution flow of their test script.&lt;/p&gt;

&lt;p&gt;This helps quickly identify issues and make fixes, thereby improving trust in the code and application itself.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Breakpoints&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The Playwright Inspector tool allows testers and QA teams to set breakpoints in their test script, pause test execution at specific points and investigate the page’s state.&lt;/p&gt;

&lt;p&gt;This provides greater control and visibility over the testing process. It helps improve the overall efficiency and effectiveness of the testing process&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Cross-Browser Support&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Playwright Inspector supports multiple Chromium-based browsers, including Chrome, Edge, and Firefox, allowing developers to test and debug their applications across various platforms.&lt;/p&gt;

&lt;p&gt;Overall, the Playwright Inspector tool is valuable for developers looking to debug their Playwright scripts effectively. Its intuitive interface, record and replay capabilities, and robust logging features make it a powerful tool for identifying and resolving issues quickly.&lt;/p&gt;
&lt;h2&gt;
  
  
  Shortcomings of Playwright Inspector
&lt;/h2&gt;

&lt;p&gt;Although Playwright Inspector is a powerful tool for debugging test scripts while performing &lt;a href="https://www.lambdatest.com/blog/playwright-framework/?utm_source=devto&amp;amp;utm_medium=organic&amp;amp;utm_campaign=nov_28&amp;amp;utm_term=bw&amp;amp;utm_content=blog" rel="noopener noreferrer"&gt;Playwright automation&lt;/a&gt;, it does have a few limitations and potential drawbacks:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Failure To Record Mouse Scroll&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Playwright Inspector does not record mouse scroll events. Hence, for tests that require us to scroll down to elements to prepare locators, a manual task is involved in writing the mouse scroll event. The video/gif shows the user action of scrolling down on a page and Playwright Inspector, which makes no record of the scroll&lt;br&gt;
&lt;iframe width="710" height="399" src="https://www.youtube.com/embed/1ik5shcvRWM"&gt;
&lt;/iframe&gt;
&lt;br&gt;
The image below shows a sample test &lt;em&gt;dynamic&lt;/em&gt; element, which requires the &lt;em&gt;mouse.wheel()&lt;/em&gt; to be added manually.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--mByleFUw--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://cdn-images-1.medium.com/max/3200/0%2AAzNADhOOeoBgJSeq.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--mByleFUw--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://cdn-images-1.medium.com/max/3200/0%2AAzNADhOOeoBgJSeq.png" width="800" height="269"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Master iOS app automation! Learn how to use &lt;a href="https://www.lambdatest.com/blog/how-to-automate-ios-app-using-appium/?utm_source=devto&amp;amp;utm_medium=organic&amp;amp;utm_campaign=nov_28&amp;amp;utm_term=bw&amp;amp;utm_content=blog" rel="noopener noreferrer"&gt;Appium on iOS real device&lt;/a&gt; with our guide.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;ul&gt;
&lt;li&gt;Manual Coding For Testing Lazy-Loaded Elements&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Locating lazy-loaded elements requires scrolling them. Since the scroll events are not recorded by the Playwright Inspector, it becomes challenging to test lazy loading elements as supporting code needs to be written manually.&lt;br&gt;
&lt;iframe width="710" height="399" src="https://www.youtube.com/embed/1ik5shcvRWM"&gt;
&lt;/iframe&gt;
&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Overhead &amp;amp; Resource Consumption&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Since the Playwright Inspector launches Chromium in non-headless mode, it consumes additional resources (i.e., bump-up in CPU consumption, increased RAM utilization), which can impact the performance tests being executed. This can lead to slower test execution, give an impression of a slow-running application, and increase the likelihood of false positives and negatives.&lt;/p&gt;

&lt;p&gt;The overhead depends on several factors, such as the machine’s capacity where tests are run in terms of Operating System, RAM, and CPU cores.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Limited Customization&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The Playwright Inspector tool has limited customization options. While it does allow modifying the page’s state, it does not provide much flexibility in terms of adding custom code or changing the test script within the GUI.&lt;/p&gt;

&lt;p&gt;Despite these limitations, Playwright Inspector remains a valuable tool for debugging Playwright scripts and can significantly reduce the time and effort required to identify and fix issues during the development and &lt;a href="https://www.lambdatest.com/blog/playwright-framework/?utm_source=devto&amp;amp;utm_medium=organic&amp;amp;utm_campaign=nov_28&amp;amp;utm_term=bw&amp;amp;utm_content=blog" rel="noopener noreferrer"&gt;Playwright automated testing&lt;/a&gt; process.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Streamline your iOS app’s functionality with &lt;a href="https://www.lambdatest.com/test-on-ios-devices?utm_source=devto&amp;amp;utm_medium=organic&amp;amp;utm_campaign=nov_28&amp;amp;utm_term=bw&amp;amp;utm_content=webpage" rel="noopener noreferrer"&gt;ios device testing online&lt;/a&gt;.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2&gt;
  
  
  How to generate tests using Playwright Inspector?
&lt;/h2&gt;

&lt;p&gt;Playwright is language agnostic, and its API is available in various languages like Java, Python, .Net, JavaScript, and TypeScript. For this blog, we will be using the TypeScript API of Playwright. Before we start writing tests, let’s quickly understand the installation process for using Playwright with TypeScript.&lt;/p&gt;

&lt;h2&gt;
  
  
  How to install Playwright using TypeScript?
&lt;/h2&gt;

&lt;p&gt;Combining Playwright with TypeScript offers a powerful and efficient approach to web automation and testing. TypeScript, a typed superset of JavaScript, enhances the development experience by providing type safety, enhanced IDE support, modern JavaScript features, and improved code readability.&lt;/p&gt;

&lt;p&gt;When used with Playwright, a versatile browser automation tool, TypeScript unleashes its full potential, enabling developers to build robust, maintainable, and scalable automation scripts. In terms of installation, there are two ways in which Playwright can be installed.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;Using the Node.js package manager like npm.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Using the Playwright VSCode extension.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Using the Playwright VSCode extension is much easier and more user-friendly. We simply need to navigate to the extension manager and search for Playwright. Then install the official Playwright Test for VSCode, as shown in the image below.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--_O7-r-01--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://cdn-images-1.medium.com/max/3200/0%2Aip4i-ApDyOWhFgQr.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--_O7-r-01--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://cdn-images-1.medium.com/max/3200/0%2Aip4i-ApDyOWhFgQr.png" width="800" height="431"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Once the official extension is installed, use the ‘Install Playwright’ action from the command panel to get started.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--G6nmRDaq--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://cdn-images-1.medium.com/max/3200/0%2A6LJu7AdI9oh78aDl.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--G6nmRDaq--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://cdn-images-1.medium.com/max/3200/0%2A6LJu7AdI9oh78aDl.png" width="800" height="432"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--Hs8y74xr--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://cdn-images-1.medium.com/max/3200/0%2Au3EFrgb-Kb7HlTmZ.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--Hs8y74xr--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://cdn-images-1.medium.com/max/3200/0%2Au3EFrgb-Kb7HlTmZ.png" width="800" height="430"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;A successful installation will result in logs that look like the below image.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--6hcCkriB--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://cdn-images-1.medium.com/max/3166/0%2Ay4K-8x6kqA-0L76Y.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--6hcCkriB--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://cdn-images-1.medium.com/max/3166/0%2Ay4K-8x6kqA-0L76Y.png" width="800" height="431"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--emeLaVSs--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://cdn-images-1.medium.com/max/3200/0%2AGSNP-1S2Y09NkGjr.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--emeLaVSs--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://cdn-images-1.medium.com/max/3200/0%2AGSNP-1S2Y09NkGjr.png" width="800" height="329"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The installation will also result in a proper directory structure that resembles the image shown. The tests folder will contain a sample &lt;em&gt;test&lt;/em&gt;, and all new tests should be added to this folder only. The &lt;em&gt;package.json&lt;/em&gt; will contain the Playwright version, and the demo test app is also included in the &lt;em&gt;test-examples&lt;/em&gt; folder.&lt;/p&gt;

&lt;p&gt;The &lt;em&gt;playwright.config.ts&lt;/em&gt; file, generated during installation, holds crucial configuration information, including the test lookup folder, making it an essential component of the setup.&lt;/p&gt;

&lt;p&gt;The below line implies that Playwright will look for tests to be executed under the folder tests at the project’s root.&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;export default defineConfig({
  testDir: './tests',
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;The reporter to be used to generate reports after tests are run.&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;reporter: 'html',
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;List of browsers and device viewports to run the tests on. We will be later configuring this file to debug tests on mobile viewports.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--lLxet47L--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://cdn-images-1.medium.com/max/2000/0%2AoHEArUQVeyCXIT59.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--lLxet47L--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://cdn-images-1.medium.com/max/2000/0%2AoHEArUQVeyCXIT59.png" width="798" height="1600"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--ClqobmMp--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://cdn-images-1.medium.com/max/2604/0%2AiL9HHvhiOEAT0PLF.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--ClqobmMp--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://cdn-images-1.medium.com/max/2604/0%2AiL9HHvhiOEAT0PLF.png" width="800" height="983"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;An optional &lt;em&gt;github/workflow&lt;/em&gt; has been included because we chose to do so when installing Playwright. This step is optional and is only necessary if CI/CD integration needs to be done.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Achieve flawless user experience. Start &lt;a href="https://www.lambdatest.com/?utm_source=devto&amp;amp;utm_medium=organic&amp;amp;utm_campaign=nov_28&amp;amp;utm_term=bw&amp;amp;utm_content=webpage" rel="noopener noreferrer"&gt;cross device testing&lt;/a&gt; with LambdaTest today!&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2&gt;
  
  
  How to write a Playwright TypeScript test?
&lt;/h2&gt;

&lt;p&gt;Now that the installation is completed let’s move on to writing the tests. We will use the &lt;a href="https://ecommerce-playground.lambdatest.io/index.php?route=account/login" rel="noopener noreferrer"&gt;LambdaTest eCommerce Playground&lt;/a&gt; website for writing the tests for this blog.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Test Scenario 1: Assert the ‘Continue’ link exists for new customer registration and is visible&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Step 1&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Navigate to the website under test. This will open up the Playwright Inspector and Chromium, the default browser, and record the first action, navigating to the My Account page as specified by the URL.&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;npx playwright codegen 
https://ecommerce-playground.lambdatest.io/index.php?route=account/login
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;&lt;strong&gt;Step 2&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Stop the recording by clicking the ‘Record’ button.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--soyfYsmC--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://cdn-images-1.medium.com/max/2000/0%2A8jWuhFmpOvqgTP5x.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--soyfYsmC--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://cdn-images-1.medium.com/max/2000/0%2A8jWuhFmpOvqgTP5x.png" width="800" height="862"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Step 3&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;The ‘Pick Locator’ will be visible immediately, and the ‘Record’ button will be grayed out, indicating recording is stopped.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--QdvzjCp3--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://cdn-images-1.medium.com/max/2218/0%2AahfpCk6stRtt38JE.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--QdvzjCp3--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://cdn-images-1.medium.com/max/2218/0%2AahfpCk6stRtt38JE.png" width="800" height="739"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Step 4&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;We can either start typing in the text form to manually locate elements or click on ‘Pick Locator’ to automatically let the Playwright Inspector choose the best locator for the particular element. Locators are central to Playwright and its ability to find elements on the DOM.&lt;/p&gt;

&lt;p&gt;Playwright provides seven built-in locators, which are recommended and can suffice most scenarios. If there remains a need to work with CSS or &lt;a href="https://www.lambdatest.com/blog/complete-guide-for-using-xpath-in-selenium-with-examples/?utm_source=devto&amp;amp;utm_medium=organic&amp;amp;utm_campaign=nov_28&amp;amp;utm_term=bw&amp;amp;utm_content=blog" rel="noopener noreferrer"&gt;XPath&lt;/a&gt;, the &lt;em&gt;page.locator()&lt;/em&gt; can be used to build custom locators. Playwright locators play a key part in making auto-wait and retry possible.&lt;/p&gt;

&lt;p&gt;In the below image, we see how we can locate all the links using the &lt;em&gt;getByRole()&lt;/em&gt; Playwright locator.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--4wtGsJIl--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://cdn-images-1.medium.com/max/3200/0%2AAOG_bpxCVkhNzNOc.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--4wtGsJIl--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://cdn-images-1.medium.com/max/3200/0%2AAOG_bpxCVkhNzNOc.png" width="800" height="432"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Step 5&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;To avoid manual selection, we can click on the ‘Pick Locator’, which will enable automatic locator selection. The ‘Pick Locator’ will also turn blue, indicating automatic locator selection is on.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--Me8YEOew--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://cdn-images-1.medium.com/max/2000/0%2ALwR3pMfDxitbhiVq.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--Me8YEOew--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://cdn-images-1.medium.com/max/2000/0%2ALwR3pMfDxitbhiVq.png" width="800" height="824"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Step 6&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Once the automatic locator selection is on, we need to act on the element we want to locate. We want to locate the &lt;em&gt;‘Continue’&lt;/em&gt; button, so we click on it. The Playwright Inspector will automatically choose the ideal locator for the element, in this case, &lt;em&gt;‘getByRole()’,&lt;/em&gt; to consistently locate the element under test.&lt;/p&gt;

&lt;p&gt;This locator is optimized for identifying elements based on their roles, aligning with how users and assistive technology perceive the page. It helps determine whether an element is a button, checkbox, or other relevant components.&lt;/p&gt;

&lt;p&gt;Role locators encompass a wide range of elements, such as buttons, checkboxes, links, lists, tables, and more. These locators adhere to the W3C specifications for ARIA roles, ARIA attributes, and accessible names, ensuring compatibility with accessibility standards.&lt;/p&gt;

&lt;p&gt;This generated Playwright locator can be copied using the ‘Copy’ button and added to the test.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--PJDcPwTg--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://cdn-images-1.medium.com/max/3200/0%2AlcJISRU0AK3x87y1.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--PJDcPwTg--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://cdn-images-1.medium.com/max/3200/0%2AlcJISRU0AK3x87y1.png" width="800" height="431"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Playwright Inspector assigns a default name to the generated test. To align with the specific scenario being tested, it is necessary to rename it appropriately and assign a more fitting name. Lastly, on the located element, we use the &lt;em&gt;expect()&lt;/em&gt; &lt;a href="https://www.lambdatest.com/learning-hub/playwright-assertions?utm_source=devto&amp;amp;utm_medium=organic&amp;amp;utm_campaign=nov_28&amp;amp;utm_term=bw&amp;amp;utm_content=learning_hub" rel="noopener noreferrer"&gt;Playwright assertion&lt;/a&gt; and the &lt;em&gt;toBeVisible()&lt;/em&gt; method to ensure the element is visible on the page under test.&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;import { test, expect } from '@playwright/test';


test('new customer registration link to be visible', async ({ page }) =&amp;amp;gt; {
  await page.goto('https://ecommerce-playground.lambdatest.io/index.php?route=account/login');
  await expect(page.getByRole('link', { name: 'Continue' })).toBeVisible()
});
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;&lt;strong&gt;Test Scenario 2: Assert ‘E-mail’ and ‘Password’ fields exist on the customer login form and are visible&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;In the upcoming scenario, our focus will be on generating tests to ensure that the customer login form includes a designated input field for entering both email and password.&lt;/p&gt;

&lt;p&gt;The steps for initiating the Playwright Inspector tool, pausing the recording, and enabling automatic locator selection remain consistent with the previous test.&lt;/p&gt;

&lt;p&gt;On clicking the ‘E-Mail Address’ field, the &lt;em&gt;getByPlaceholder(‘E-mail Address’)&lt;/em&gt; locator is prompted by the Playwright Inspector tool.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--dxYcOn_A--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://cdn-images-1.medium.com/max/3200/0%2AtTblwAu8QhdZgFLG.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--dxYcOn_A--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://cdn-images-1.medium.com/max/3200/0%2AtTblwAu8QhdZgFLG.png" width="800" height="349"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Similarly, on clicking the ‘Password’ field, the &lt;em&gt;getByPlaceholder(‘Password’)&lt;/em&gt; locator is prompted by the Playwright Inspector tool.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--MdGSaOWq--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://cdn-images-1.medium.com/max/3200/0%2AggmYJdtMzalCnFRd.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--MdGSaOWq--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://cdn-images-1.medium.com/max/3200/0%2AggmYJdtMzalCnFRd.png" width="800" height="358"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The &lt;em&gt;getByPlaceholder()&lt;/em&gt; Playwright locator leverages the presence of the placeholder attribute in form input fields, providing a useful hint to users regarding the expected input value. This locator is particularly advantageous in scenarios involving form fields, especially when labels are absent, but placeholder texts are available.&lt;/p&gt;

&lt;p&gt;On the locators, we use the Playwright &lt;em&gt;expect()&lt;/em&gt; and &lt;em&gt;toBeVisible()&lt;/em&gt; assertion to ensure that the locator points to an element visible on the DOM.&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;test('email password to be visible', async ({ page }) =&amp;amp;gt; {
  await page.goto('https://ecommerce-playground.lambdatest.io/index.php?route=account/login');
  await expect(page.getByPlaceholder('Password')).toBeVisible()
  await expect(page.getByPlaceholder('E-Mail Address')).toBeVisible()
});
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;&lt;strong&gt;Test Scenario 3: Assert ‘Forgotten Password’ link exists on Returning Customer form and is visible&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;The next scenario we will generate tests for is to make sure the customer login form has the ‘Forgotten Password’ link in case a returning customer&lt;/p&gt;

&lt;p&gt;The steps to start the Playwright Inspector tool, pause the recording and enable automatic locator selection remain unchanged.&lt;/p&gt;

&lt;p&gt;On pausing the record and clicking the ‘Forgotten Password’ link, we can notice the prompted locator is again &lt;em&gt;getByRole(‘link’, {name: ‘Forgotten Password’, exact: True’}).&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--hVhRIV73--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://cdn-images-1.medium.com/max/2826/0%2A3IJ7pC-O3Tocjf0c.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--hVhRIV73--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://cdn-images-1.medium.com/max/2826/0%2A3IJ7pC-O3Tocjf0c.png" width="800" height="416"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The &lt;em&gt;exact&lt;/em&gt;: True filter is applied by Playwright Inspector to the &lt;em&gt;getByRole()&lt;/em&gt; locator as the ‘Forgotten Password’ link is present twice on the page. The image below shows that if we remove &lt;em&gt;exact: True&lt;/em&gt;, the locator matches two elements with the HTML link role and name matching the text ‘Forgotten Password’. The second ‘Forgotten Password’ has a space after the ‘Password’ filter helps eliminate it from the selection.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--IEIKnRFD--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://cdn-images-1.medium.com/max/2842/0%2AhLXiItV_iOvkpXp5.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--IEIKnRFD--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://cdn-images-1.medium.com/max/2842/0%2AhLXiItV_iOvkpXp5.png" width="800" height="462"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;To prevent such behavior and ensure precise element matching, the Playwright Inspector intelligently includes an additional parameter in the generated locator. This ensures that only the intended element is matched, eliminating ambiguity or unintended matches.&lt;/p&gt;

&lt;p&gt;We then copy the generated test code and locator and give it a proper name. We then use the &lt;em&gt;expect()&lt;/em&gt; and &lt;em&gt;toBeVisible()&lt;/em&gt; Playwright assertion to ensure the element under test is visible on the DOM.&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;test('forgotten password to be visible', async ({ page }) =&amp;amp;gt; {
  await page.goto('https://ecommerce-playground.lambdatest.io/index.php?route=account/login');
  await expect(page.getByRole('link', { name: 'Forgotten Password', exact: true })).toBeVisible()
});
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;Let’s now execute these three tests before we move on to writing the fourth and final test of this blog.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Execution&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Running tests is possible using the VSCode extension or &lt;em&gt;nodejs&lt;/em&gt; CLI (command line). The simplest command is the &lt;em&gt;npx playwright D:\Python\pwinspectorjs\test&lt;/em&gt;. This command searches in the TestDir (&lt;em&gt;default: ./tests&lt;/em&gt;) for all files with the extensions .&lt;em&gt;spec.ts(js)&lt;/em&gt;. Tests are run against all browsers.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--PvD8gHO8--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://cdn-images-1.medium.com/max/2092/0%2Aa_bK0f_8KoTQFetU.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--PvD8gHO8--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://cdn-images-1.medium.com/max/2092/0%2Aa_bK0f_8KoTQFetU.png" width="800" height="278"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;We can also generate a detailed text execution HTML report using the &lt;em&gt;npx playwright show-report&lt;/em&gt; command. The output of which looks like below.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--p2l-WHH3--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://cdn-images-1.medium.com/max/3200/0%2ARc-o_u0qeUTfq-L-.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--p2l-WHH3--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://cdn-images-1.medium.com/max/3200/0%2ARc-o_u0qeUTfq-L-.png" width="800" height="316"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Test Scenario 4: Assert Add Quantity &amp;amp; Product Image Gallery&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Hovering over any product shows a pop-up menu that contains options like ‘add to car’, ‘add to wish list’, ‘quick view’, and ‘compare product’. On clicking the ‘quick view’ button, a subsequent modal opens up that has the product info along with an &lt;em&gt;input&lt;/em&gt; field to increase quantity, add to cart, and buy now buttons.&lt;/p&gt;

&lt;p&gt;modal opens up that has the product info along with an input field to increase quantity, add to cart, and buy now buttons.&lt;/p&gt;

&lt;p&gt;Here is the overall scenario:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;&lt;p&gt;Click the ‘+’ icon 5 times and verify if the quantity is updated to 6.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Product image gallery to have a total of 5 images.&lt;/p&gt;&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--hLcf_O77--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://cdn-images-1.medium.com/max/3200/0%2AsescBqiw6T788bcd.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--hLcf_O77--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://cdn-images-1.medium.com/max/3200/0%2AsescBqiw6T788bcd.png" width="800" height="415"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--QzgaWv0O--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://cdn-images-1.medium.com/max/2000/0%2AJ8USJ6BFJCt5Dc8_.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--QzgaWv0O--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://cdn-images-1.medium.com/max/2000/0%2AJ8USJ6BFJCt5Dc8_.png" width="800" height="479"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The URL of the product page is&lt;/p&gt;

&lt;p&gt;&lt;a href="https://ecommerce-playground.lambdatest.io/index.php?route=product/category&amp;amp;path=57" rel="noopener noreferrer"&gt;*https://ecommerce-playground.lambdatest.io/index.php?route=product/category&amp;amp;path=57&lt;/a&gt;*&lt;/p&gt;

&lt;p&gt;Open the Playwright Inspector tool and pass the URL as shown below:&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;npx playwright codegen
https://ecommerce-playground.lambdatest.io/index.php?route=product/category&amp;amp;path=57
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--SLZC5Pq7--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://cdn-images-1.medium.com/max/3200/0%2AaxKC7bCAZyHYS5hb.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--SLZC5Pq7--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://cdn-images-1.medium.com/max/3200/0%2AaxKC7bCAZyHYS5hb.png" width="800" height="338"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The opened chromium and Playwright Inspector resemble a blank canvas where we are free to perform actions, and the recording will start.&lt;/p&gt;

&lt;p&gt;After entering the URL and pressing enter, the action is recorded, as seen in the image below.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--T7SoByzd--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://cdn-images-1.medium.com/max/3200/0%2A1S_5I1yqORLzDY9M.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--T7SoByzd--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://cdn-images-1.medium.com/max/3200/0%2A1S_5I1yqORLzDY9M.png" width="800" height="431"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The product images on the test website are &lt;a href="https://www.lambdatest.com/blog/how-to-lazy-load-images-javascript/?utm_source=devto&amp;amp;utm_medium=organic&amp;amp;utm_campaign=nov_28&amp;amp;utm_term=bw&amp;amp;utm_content=blog" rel="noopener noreferrer"&gt;lazy-loaded&lt;/a&gt;, i.e., we have to scroll down to load them and be able to locate them.&lt;/p&gt;

&lt;p&gt;⚠️The downside of the Playwright Inspector, however, is that it cannot record the scroll event. So, later in the generated code, we will have to add the mouse scroll manually.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--4kp4QU38--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://cdn-images-1.medium.com/max/3200/0%2AauDLvagYN7Smu-8B.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--4kp4QU38--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://cdn-images-1.medium.com/max/3200/0%2AauDLvagYN7Smu-8B.png" width="800" height="427"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;For now, let’s continue locating the product and pop-up menu. The pop-up menu is available only when we hover over the product. The Playwright Inspector doesn’t record this hover action as well.&lt;/p&gt;

&lt;p&gt;So, another issue that we need to handle manually. But it gives us the locator of the product, which is enough. On this locator, we can call the Playwright &lt;em&gt;hover()&lt;/em&gt; method.&lt;/p&gt;

&lt;p&gt;Next, we locate ‘Quick View’ from the pop-up menu and click on it.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--N6YLs1Db--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://cdn-images-1.medium.com/max/3200/0%2ANiK7ZnJbSH0ga4aB.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--N6YLs1Db--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://cdn-images-1.medium.com/max/3200/0%2ANiK7ZnJbSH0ga4aB.png" width="800" height="430"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;On the subsequent modal that opens, we can locate the ‘+’ element, which increases the quantity and can be clicked to increase the quantity. Here we can either perform the click action multiple times using the Playwright click() method or supply the &lt;em&gt;{clickCount: 5}&lt;/em&gt; parameter to the &lt;em&gt;click()&lt;/em&gt; method to increase it to 6 directly.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--Axsn5fx7--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://cdn-images-1.medium.com/max/3200/0%2AiYvli-IZReTblaDN.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--Axsn5fx7--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://cdn-images-1.medium.com/max/3200/0%2AiYvli-IZReTblaDN.png" width="800" height="430"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The last one is locating the image group, which cannot be directly reached. We have to use Playwright locator chaining first to get the image gallery element and then all the links with the name HTC Touch HD. We can see there are a total of 5 images, so the locator should match 5 elements. This is exactly the assertion we will verify.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--pPhWuqBd--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://cdn-images-1.medium.com/max/3200/0%2A56xHJjw5wKQSyx3E.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--pPhWuqBd--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://cdn-images-1.medium.com/max/3200/0%2A56xHJjw5wKQSyx3E.png" width="800" height="427"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The final boilerplate code generated by Playwright Inspector looks like below. While it’s not perfect and lacks the &lt;em&gt;hover()&lt;/em&gt; on the product and the mouse scroll, it’s a good starting point. Even with these shortcomings, Playwright Inspector did a good job of providing complex locators, and in the case of the image gallery, even was able to generate chained Playwright locators.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--BXVemLF7--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://cdn-images-1.medium.com/max/2000/0%2Ai0XdShLCkCBsQ1pw.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--BXVemLF7--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://cdn-images-1.medium.com/max/2000/0%2Ai0XdShLCkCBsQ1pw.png" width="800" height="366"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;test('dynamic element’, async ({ page }) =&amp;gt; {
  await page.goto('https://ecommerce-playground.lambdatest.io/index.php?route=product/category&amp;amp;path=57');
  await page.mouse.wheel(0, 50)
  await page.getByRole('link', { name: 'HTC Touch HD HTC Touch HD HTC Touch HD HTC Touch HD' }).hover()
  await page.locator('button:nth-child(3)').first().click();
// the { clickCount:5 } has been added manually to simulate adding 5 items as once
  await page.getByRole('button', { name: 'Increase quantity' }).click({clickCount: 5});


// the following two lines which perform the expect test assertion have been added manually
  await expect(page.getByRole("spinbutton", { name :'Qty' })).toHaveValue('6')
  await expect(page.locator('#image-gallery-212946').getByRole('link', { name: 'HTC Touch HD' })).toHaveCount(5)
await page.pause()
}); 
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;&lt;strong&gt;Code Walkthrough&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Step 1&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;We first navigate to the product page URL.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--Knv68tBZ--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://cdn-images-1.medium.com/max/3200/0%2AX1d9L82zfx7IpuKl.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--Knv68tBZ--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://cdn-images-1.medium.com/max/3200/0%2AX1d9L82zfx7IpuKl.png" width="800" height="145"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Step 2&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;The product images are lazy loaded, so we simulate the mouse scroll, which the Playwright Inspector does not capture.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--wU7eaTKH--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://cdn-images-1.medium.com/max/2612/0%2AtFldpv5Lw3wk7Mny.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--wU7eaTKH--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://cdn-images-1.medium.com/max/2612/0%2AtFldpv5Lw3wk7Mny.png" width="800" height="225"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Step 3&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Next, we &lt;em&gt;hover()&lt;/em&gt; over the product image to make the pop-up menu visible.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--gjl82RSM--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://cdn-images-1.medium.com/max/3200/0%2AxwAgB6ywqWzNhWeV.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--gjl82RSM--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://cdn-images-1.medium.com/max/3200/0%2AxwAgB6ywqWzNhWeV.png" width="800" height="147"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Step 4&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;On the pop-up menu, ‘Quick View’ is the 3rd element located by the tool.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--qVLQMKVl--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://cdn-images-1.medium.com/max/2272/0%2AfAc_ZfMguhsQLU6L.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--qVLQMKVl--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://cdn-images-1.medium.com/max/2272/0%2AfAc_ZfMguhsQLU6L.png" width="800" height="232"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Step 5&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;The ‘+’, which increases the quantity, is a button. We locate and click on it and amend the &lt;em&gt;click()&lt;/em&gt; method to simulate 5 clicks by adding the &lt;em&gt;{clickCount :5 }&lt;/em&gt; argument&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--3R_qMPC9--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://cdn-images-1.medium.com/max/3200/0%2ALddCshlrnqvaa6KY.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--3R_qMPC9--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://cdn-images-1.medium.com/max/3200/0%2ALddCshlrnqvaa6KY.png" width="800" height="183"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Step 6&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;After this, we locate the input element where the quantity is displayed and use the expect() assertion to make sure it has the value 6&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--llbUHrIs--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://cdn-images-1.medium.com/max/2828/0%2AAkIgZp_DLQw_x4SP.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--llbUHrIs--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://cdn-images-1.medium.com/max/2828/0%2AAkIgZp_DLQw_x4SP.png" width="800" height="187"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Step 7&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Lastly, we use the &lt;em&gt;expect()&lt;/em&gt; assertion of the product image gallery locator and assert it to have matched 5 elements for the 5 images seen.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--X1SUuz7k--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://cdn-images-1.medium.com/max/3200/0%2Aod_MEwClijl0sxbP.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--X1SUuz7k--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://cdn-images-1.medium.com/max/3200/0%2Aod_MEwClijl0sxbP.png" width="800" height="134"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Step 8&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;The &lt;em&gt;await page.pause()&lt;/em&gt; method supports the pause of debugging execution at this point.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Execution&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Now, let’s execute the single test. This can be done by using the -g flag and giving the name of the single test we want to run.&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;npx playwright test -g “dynamic element”
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--XziajuEM--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://cdn-images-1.medium.com/max/2000/0%2AoF7mEn1mgJv25rAq.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--XziajuEM--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://cdn-images-1.medium.com/max/2000/0%2AoF7mEn1mgJv25rAq.png" width="800" height="338"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;em&gt;npx playwright show-report&lt;/em&gt; allows viewing the HTML report providing insights into the test run such as the number of tests run, passed, failed, and flaky tests. The individual tests also show browsers against which it was run. Clicking on individual tests also shows details about steps and actionability logs.&lt;/p&gt;

&lt;p&gt;The below image shows the overview of the generated HTML report.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--nPNuX73b--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://cdn-images-1.medium.com/max/3200/0%2ANKgA-9Ypi98IRfHi.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--nPNuX73b--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://cdn-images-1.medium.com/max/3200/0%2ANKgA-9Ypi98IRfHi.png" width="800" height="268"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;We can click on the individual test and then view the actionability logs in each step, as shown in the image below.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--NHh_0uC1--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://cdn-images-1.medium.com/max/3200/0%2AJqn9YeHRSoA1Jv7L.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--NHh_0uC1--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://cdn-images-1.medium.com/max/3200/0%2AJqn9YeHRSoA1Jv7L.png" width="800" height="419"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The Playwright Inspector is not only capable of recording user actions to generate scripts and tests but can also be used to debug tests.&lt;/p&gt;

&lt;h2&gt;
  
  
  How to debug tests using Playwright Inspector?
&lt;/h2&gt;

&lt;p&gt;To run the Playwright Inspector in debug mode, we need to use the — — &lt;em&gt;debug *flag. By default, this will run all the tests one by one based on the browsers configured in the *playwright.config.ts&lt;/em&gt; file and open the inspector and a browser window for each test.&lt;br&gt;
&lt;iframe width="710" height="399" src="https://www.youtube.com/embed/8yUGBClKn-s"&gt;
&lt;/iframe&gt;
&lt;br&gt;
We will be reusing the same test suite we wrote in the blog to understand debugging using Playwright Inspector. Since we have only configured one browser, Chromium, in our config file &lt;em&gt;playwright.config.ts&lt;/em&gt;, only that will be used for debugging run.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--ef6-09Qc--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://cdn-images-1.medium.com/max/2604/0%2AhHahVe2ZHMkGnzQM.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--ef6-09Qc--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://cdn-images-1.medium.com/max/2604/0%2AhHahVe2ZHMkGnzQM.png" width="800" height="983"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Starting the execution of the tests in debug mode.&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;npx playwright test --debug
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--kUKNHoAt--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://cdn-images-1.medium.com/max/2000/0%2Af_YjHkyICZP-VwoU.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--kUKNHoAt--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://cdn-images-1.medium.com/max/2000/0%2Af_YjHkyICZP-VwoU.png" width="800" height="175"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The Playwright Inspector will start in debug mode, and the top menu will reveal options to resume (F8), Step over (F19) tests, which allow step-by-step debugging, are now activated.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--WGUUVGoc--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://cdn-images-1.medium.com/max/2000/0%2AWUdvK7DMt0xKu4D2.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--WGUUVGoc--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://cdn-images-1.medium.com/max/2000/0%2AWUdvK7DMt0xKu4D2.png" width="416" height="101"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The actionability logs also start showing at the console, indicating the progress of the test.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--_JVJvGQJ--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://cdn-images-1.medium.com/max/3200/0%2Af-6EAJHpTPAMpnsr.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--_JVJvGQJ--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://cdn-images-1.medium.com/max/3200/0%2Af-6EAJHpTPAMpnsr.png" width="800" height="429"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The Chromium browser will always launch in headless mode with no default timeout. No action will be performed if we do not press the &lt;em&gt;Resume&lt;/em&gt; or &lt;em&gt;Step over button.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;The upper half of the Playwright Inspector shows the code under test, and the lower half shows the actionability log. This can help us understand what happened during our test and what Playwright did or tried to do. The log informs if the element was visible, enabled, and stable, if the locator resolved to an element, scrolled into view, and so much more. If actionability can’t be reached, it will show the action in the Pending state.&lt;/p&gt;

&lt;h2&gt;
  
  
  Live Editing Playwright Locators
&lt;/h2&gt;

&lt;p&gt;When a running test is paused, we can also experiment with various locators to find elements. ‘Pick Locator’ needs to be enabled, and then on the text field next to it, we can use the built-in or custom locators to find elements.&lt;/p&gt;

&lt;p&gt;The example image below shows the use of the &lt;em&gt;getByRole(“link”)&lt;/em&gt; locator to find all the links on the page. This helps us debug the existing locators and fine-tune the auto-generated locator.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--4ewMsBq4--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://cdn-images-1.medium.com/max/3200/0%2ABxVIztd3JxScg5Cr.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--4ewMsBq4--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://cdn-images-1.medium.com/max/3200/0%2ABxVIztd3JxScg5Cr.png" width="800" height="397"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Debug a Test From a Specific Breakpoint
&lt;/h2&gt;

&lt;p&gt;Sometimes, we don’t want to step through the entire test execution and speed up the debugging process by stopping after certain actions or several lines have elapsed. By default, when running the tests in debug mode, the control execution stops at each step on pressing the step over button and only runs the entire test if we press the resume button.&lt;/p&gt;

&lt;p&gt;To override this behavior, we can add &lt;em&gt;await page.pause()&lt;/em&gt; call in our tests. When we do this and start test execution, the Playwright inspector will only run the test until the point it encounters &lt;em&gt;await page.pause()&lt;/em&gt;. As soon as it finds the &lt;em&gt;await page.pause()&lt;/em&gt;, the execution is halted until we press the Resume button on the page overlay or there is a call to &lt;em&gt;playwright.resume()&lt;/em&gt;.&lt;/p&gt;

&lt;p&gt;To demonstrate this, let’s add await &lt;em&gt;page.pause()&lt;/em&gt; to the &lt;em&gt;test(’email password to be visible’, async ({ page })&lt;/em&gt; after the Password email assertion. Shown below is the modified implementation:&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;test('email password to be visible', async ({ page }) =&amp;gt; {
  await page.goto('https://ecommerce-playground.lambdatest.io/index.php?route=account/login');  
  await expect(page.getByPlaceholder('Password')).toBeVisible()
  await page.pause()
  await expect(page.getByPlaceholder('E-Mail Address')).toBeVisible()
});
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;Let’s now run a single test in debug mode&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;npx playwright test  -g “email password to be visible”  -- debug
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;And as expected the test control execution has stopped at the first instance of await &lt;em&gt;page.pause().&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--GEMnLNnf--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://cdn-images-1.medium.com/max/3200/0%2AFKXSsgUTbK4lj9Iy.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--GEMnLNnf--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://cdn-images-1.medium.com/max/3200/0%2AFKXSsgUTbK4lj9Iy.png" width="800" height="431"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Debug Tests on Mobile Viewport
&lt;/h2&gt;

&lt;p&gt;The TypeScript and JavaScript API of Playwright Inspector supports debugging tests on all or a single browser or even on mobile viewports. The configuration for setting the allowed browsers and mobile viewports needs to be done in the &lt;em&gt;playwright.config.ts&lt;/em&gt; file.&lt;/p&gt;

&lt;p&gt;Consider, for example, that we want to debug the tests written in file &lt;em&gt;example.spec.ts&lt;/em&gt; only on Mobile Safari.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--dynUVLki--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://cdn-images-1.medium.com/max/3200/0%2As2_AaXN4fNlyaeVB.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--dynUVLki--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://cdn-images-1.medium.com/max/3200/0%2As2_AaXN4fNlyaeVB.png" width="800" height="450"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Modify the &lt;em&gt;playwright.config.ts&lt;/em&gt; to include the Mobile Safari viewport and the device as &lt;em&gt;iPhone 12&lt;/em&gt;. The browsers and device settings are done under the &lt;em&gt;projects&lt;/em&gt; list, as shown below.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--psWfvPFl--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://cdn-images-1.medium.com/max/2604/0%2A_NM7eL31-eyYmH2Q.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--psWfvPFl--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://cdn-images-1.medium.com/max/2604/0%2A_NM7eL31-eyYmH2Q.png" width="800" height="983"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Shown below is the debug output:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--mp-EFZOL--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://cdn-images-1.medium.com/max/2000/0%2Ad7dbBH1jnnbbWzWz.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--mp-EFZOL--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://cdn-images-1.medium.com/max/2000/0%2Ad7dbBH1jnnbbWzWz.png" width="800" height="618"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;And if we now use the npx *playwright show-report *command, the output clearly shows the viewport configuration and numbers of tests run.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--0BljkbYj--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://cdn-images-1.medium.com/max/3200/0%2AlJXsh71Rd7mtvLyL.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--0BljkbYj--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://cdn-images-1.medium.com/max/3200/0%2AlJXsh71Rd7mtvLyL.png" width="800" height="357"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Stepping Over Tests
&lt;/h2&gt;

&lt;p&gt;The Playwright Inspector allows stepping over tests using the top menu. We can either click the Step over button or use the keyboard shortcut F10.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--HySqVTXw--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://cdn-images-1.medium.com/max/2000/0%2AxndGzkOFcVglwEIh.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--HySqVTXw--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://cdn-images-1.medium.com/max/2000/0%2AxndGzkOFcVglwEIh.png" width="476" height="102"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The current action is shown in the Playwright Inspector, and the selected element is highlighted in the browser window. The second half of the Playwright Inspector also shows the actionability log indicating the executed lines and the currently executing line.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--4iH98WAY--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://cdn-images-1.medium.com/max/3200/0%2AbTNkIHgN3365gJ76.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--4iH98WAY--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://cdn-images-1.medium.com/max/3200/0%2AbTNkIHgN3365gJ76.png" width="800" height="429"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Debug Dynamically Loading Elements
&lt;/h2&gt;

&lt;p&gt;In the second test scenario, we wrote a test case for dynamically loading elements using the TypeScript API of Playwright. Dynamically loading elements make the perfect candidate for debugging as there is a chance of flakiness in such tests.&lt;/p&gt;

&lt;p&gt;Debugging a single test can be started by firing the below command where the -g flag indicates we wish to run the single test specified by its name.&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;npx playwright test -g "dynamic element" --debug
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;We use the step over (F10) button and stop first after the scroll. The actionability logs clearly show the progress so far.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--729CvdzH--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://cdn-images-1.medium.com/max/3200/0%2ACLNBvAaovG_4CQ2k.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--729CvdzH--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://cdn-images-1.medium.com/max/3200/0%2ACLNBvAaovG_4CQ2k.png" width="800" height="432"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Before completing the &lt;em&gt;click()&lt;/em&gt; action on the increase quantity button, we can see the count is 1. After stepping over, the count becomes 6, which is clear to see.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--I3tCu1jK--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://cdn-images-1.medium.com/max/3200/0%2Ao8i8jdPgfAqF7hZz.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--I3tCu1jK--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://cdn-images-1.medium.com/max/3200/0%2Ao8i8jdPgfAqF7hZz.png" width="800" height="428"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;While the debugging provided simple insights, it’s very effective in the next line, where we are trying to verify that 5 images exist in the gallery. The debugging screen clearly shows the matched elements by the locator.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--2PsEs3pG--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://cdn-images-1.medium.com/max/3200/0%2ANhh-_KzBICj4mgX3.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--2PsEs3pG--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://cdn-images-1.medium.com/max/3200/0%2ANhh-_KzBICj4mgX3.png" width="800" height="428"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;We can also use the debugging screen to locate other elements on the modal; for example, we want to find the locator for the ‘ADD TO CART’ button. We can click the ‘Pick Locator’ to enable the live locator. Once enabled, we click on the ‘ADD TO CART’ to let Playwright Inspector provide the locator in the text field next to ‘Pick Locator’.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--yoSnmixY--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://cdn-images-1.medium.com/max/3200/0%2AJ8pedog5OWO2jt_2.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--yoSnmixY--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://cdn-images-1.medium.com/max/3200/0%2AJ8pedog5OWO2jt_2.png" width="800" height="296"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;This is a simple yet effective demo of live debugging of tests and how the Playwright Inspector can help make the life of automation teams build and improve the existing tests and test suites.&lt;/p&gt;

&lt;p&gt;However, you can further scale your Playwright testing experience by running tests on the cloud-based grid. Digital experience testing platforms like LambdaTest lets you perform automation testing with Playwright on an &lt;a href="https://www.lambdatest.com/online-browser-farm?utm_source=devto&amp;amp;utm_medium=organic&amp;amp;utm_campaign=nov_28&amp;amp;utm_term=bw&amp;amp;utm_content=webpage" rel="noopener noreferrer"&gt;online browser farm&lt;/a&gt; of 50+ real browsers and OS combinations.&lt;/p&gt;

&lt;p&gt;It also allows you to achieve faster release cycles by executing Playwright tests in parallel.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Check our documentation: &lt;a href="https://www.lambdatest.com/support/docs/playwright-testing/?utm_source=devto&amp;amp;utm_medium=organic&amp;amp;utm_campaign=nov_28&amp;amp;utm_term=bw&amp;amp;utm_content=support_doc" rel="noopener noreferrer"&gt;Getting Started with Playwright Testing&lt;/a&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Looking to showcase your skills as a Playwright automation tester? LambdaTest’s &lt;a href="https://www.lambdatest.com/certifications/playwright-101?utm_source=devto&amp;amp;utm_medium=organic&amp;amp;utm_campaign=nov_28&amp;amp;utm_term=bw&amp;amp;utm_content=certifications" rel="noopener noreferrer"&gt;Playwright 101 certification&lt;/a&gt; program is tailored for developers aiming to demonstrate their expertise in utilizing &lt;a href="https://www.lambdatest.com/blog/playwright-end-to-end-testing/?utm_source=devto&amp;amp;utm_medium=organic&amp;amp;utm_campaign=nov_28&amp;amp;utm_term=bw&amp;amp;utm_content=blog" rel="noopener noreferrer"&gt;Playwright end to end testing&lt;/a&gt; of contemporary web applications.&lt;/p&gt;

&lt;h2&gt;
  
  
  Conclusion
&lt;/h2&gt;

&lt;p&gt;This blog provides a glimpse into the world of test generation and debugging using the Playwright Inspector tool. However, there is much more to explore beyond the surface. Whether you lean towards Python or TypeScript, Playwright Inspector offers seamless integration with these widely used programming languages, empowering you with extensive test automation and debugging capabilities.&lt;/p&gt;

&lt;p&gt;We witnessed the utilization of the Playwright Inspector tool with TypeScript. The versatility of Playwright Inspector, accommodating multiple programming languages according to the expertise and preferences of QA and automation teams, positions Playwright as an exceptional choice for automating applications across the board.&lt;/p&gt;

&lt;p&gt;Daily usage, more experience, and experimenting with the Playwright Inspector tool can improve the ability to build robust and resilient test automation suites for any web application. Playwright Inspector tool also matures with each new version that is released.&lt;/p&gt;

&lt;p&gt;Playwright Inspector is an indispensable tool for developers and QA professionals who must quickly create and debug automated tests. Its user-friendly interface makes it easy to write and run tests and even helps debug them.&lt;/p&gt;

&lt;p&gt;One of the key advantages of Playwright Inspector is its ability to run tests on various devices and viewports. This means we can easily test our web application across different devices, screen sizes, and resolutions, which is crucial for ensuring a seamless user experience.&lt;br&gt;
Additionally, the tool’s live element location feature makes it easy to identify and fix any issues with our website’s elements. So, if you’re not already using Playwright Inspector, it’s worth trying.&lt;/p&gt;

</description>
      <category>debugging</category>
      <category>playwright</category>
      <category>opensource</category>
      <category>testing</category>
    </item>
    <item>
      <title>How To Find Elements Using Playwright Locators</title>
      <dc:creator>Jaydeep Karale</dc:creator>
      <pubDate>Mon, 04 Dec 2023 06:08:56 +0000</pubDate>
      <link>https://dev.to/_jaydeepkarale/how-to-find-elements-using-playwright-locators-16ei</link>
      <guid>https://dev.to/_jaydeepkarale/how-to-find-elements-using-playwright-locators-16ei</guid>
      <description>&lt;p&gt;End-to-end testing is essential for ensuring the quality and usability of a website or web application. And a crucial aspect of &lt;a href="https://www.lambdatest.com/learning-hub/end-to-end-testing?utm_source=devto&amp;amp;utm_medium=organic&amp;amp;utm_campaign=nov_27&amp;amp;utm_term=bw&amp;amp;utm_content=learning_hub" rel="noopener noreferrer"&gt;end-to-end testing&lt;/a&gt; is the ability to reliably, correctly, and easily locate elements on a web page. The process of locating elements involves identifying and interacting with elements on a web page, such as buttons, links, and text fields, to evaluate their behavior and functionality.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://www.lambdatest.com/blog/best-web-development-frameworks/?utm_source=devto&amp;amp;utm_medium=organic&amp;amp;utm_campaign=nov_27&amp;amp;utm_term=bw&amp;amp;utm_content=blog" rel="noopener noreferrer"&gt;Web development frameworks&lt;/a&gt; and technologies are constantly evolving. Single Page Applications(SPA) are perfect examples where a single document is loaded, which receives dynamic updates, behaves differently across different browsers, uses asynchronous requests, and has complex state management.&lt;/p&gt;

&lt;p&gt;The increasingly dynamic nature of web applications makes it paramount to have a robust, reliable, and accurate method for locating and interacting with elements on a web page. Resilient and non-flaky tests can help test every aspect of the website (or app). How can I not mention Playwright when referring to non-flakiness in tests 🙂&lt;/p&gt;

&lt;p&gt;&lt;a href="https://www.lambdatest.com/playwright?utm_source=devto&amp;amp;utm_medium=organic&amp;amp;utm_campaign=nov_27&amp;amp;utm_term=bw&amp;amp;utm_content=webpage" rel="noopener noreferrer"&gt;Playwright&lt;/a&gt; is an open-source &lt;a href="https://www.lambdatest.com/blog/automation-testing-frameworks/?utm_source=devto&amp;amp;utm_medium=organic&amp;amp;utm_campaign=nov_27&amp;amp;utm_term=bw&amp;amp;utm_content=blog" rel="noopener noreferrer"&gt;automation testing framework&lt;/a&gt;, which enables reliable end-to-end testing of web applications. It supports multiple languages, including Python, JavaScript, Java, and .Net, making it flexible for different developer communities. It also works seamlessly across different browsers and operating systems.&lt;/p&gt;

&lt;p&gt;One of the powerful ways to locate elements for &lt;a href="https://www.lambdatest.com/web-testing?utm_source=devto&amp;amp;utm_medium=organic&amp;amp;utm_campaign=nov_27&amp;amp;utm_term=bw&amp;amp;utm_content=webpage" rel="noopener noreferrer"&gt;web testing&lt;/a&gt; is by using Playwright locators. Playwright locators are the central piece of Playwright’s auto-waiting and retry-ability, making it easy for developers and testers to interact with specific elements on a web page, and it enables the creation of test scripts that can interact with those elements.&lt;/p&gt;

&lt;p&gt;To understand the preferences and trends within the testing community, we conducted a LinkedIn poll asking, ‘What is your favorite element locator in test automation? The responses provided a clear preference, that is Xpath among professionals. This interesting revelation highlights the significant role of XPath in test automation.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--bKW_uzvL--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://cdn-images-1.medium.com/max/2000/0%2AVoQ-WCY5nEzaJ3eg.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--bKW_uzvL--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://cdn-images-1.medium.com/max/2000/0%2AVoQ-WCY5nEzaJ3eg.png" width="776" height="420"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;em&gt;&lt;a href="https://www.linkedin.com/feed/update/urn:li:activity:7125005859511795714/" rel="noopener noreferrer"&gt;Source&lt;/a&gt;&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;In this blog on Playwright locators, we will dive deep into various &lt;a href="https://www.lambdatest.com/learning-hub/selenium-locators?utm_source=devto&amp;amp;utm_medium=organic&amp;amp;utm_campaign=nov_27&amp;amp;utm_term=bw&amp;amp;utm_content=learning_hub" rel="noopener noreferrer"&gt;locator strategies&lt;/a&gt; that can be used with Playwright for finding web elements while performing &lt;a href="https://www.lambdatest.com/automation-testing?utm_source=devto&amp;amp;utm_medium=organic&amp;amp;utm_campaign=nov_27&amp;amp;utm_term=bw&amp;amp;utm_content=webpage" rel="noopener noreferrer"&gt;automation testing&lt;/a&gt;. If you are preparing for an interview you can learn more through &lt;a href="https://www.lambdatest.com/learning-hub/playwright-interview-questions?utm_source=devto&amp;amp;utm_medium=organic&amp;amp;utm_campaign=nov_27&amp;amp;utm_term=bw&amp;amp;utm_content=learning_hub" rel="noopener noreferrer"&gt;Playwright interview questions&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;So, let’s get started!&lt;/p&gt;

&lt;h2&gt;
  
  
  Quick Recap about Playwright
&lt;/h2&gt;

&lt;p&gt;Playwright is the latest entrant into the array of frameworks (e.g., Selenium, Cypress, etc.) available for &lt;a href="https://www.lambdatest.com/learning-hub/web-automation?utm_source=devto&amp;amp;utm_medium=organic&amp;amp;utm_campaign=nov_27&amp;amp;utm_term=bw&amp;amp;utm_content=learning_hub" rel="noopener noreferrer"&gt;web automation&lt;/a&gt;. It enables fast and reliable end-to-end testing for modern web apps.&lt;br&gt;
&lt;iframe width="710" height="399" src="https://www.youtube.com/embed/wawbt1cATsk"&gt;
&lt;/iframe&gt;
&lt;br&gt;
At the time of writing this Playwright locators blog, the latest stable version of Playwright is 1.30.0, and Playwright is now consistently hitting the &amp;gt;20K download per day mark, as seen from PyPi Stats.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--lxC3fy8l--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://cdn-images-1.medium.com/max/3010/0%2ATkVxNm5qiYqGOAoJ.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--lxC3fy8l--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://cdn-images-1.medium.com/max/3010/0%2ATkVxNm5qiYqGOAoJ.png" width="800" height="275"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Below are the download trends of Playwright in comparison to a popular alternative, &lt;a href="https://www.lambdatest.com/selenium?utm_source=devto&amp;amp;utm_medium=organic&amp;amp;utm_campaign=nov_27&amp;amp;utm_term=bw&amp;amp;utm_content=webpage" rel="noopener noreferrer"&gt;Selenium&lt;/a&gt;, taken from Pip Trends.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--msyQ3pCd--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://cdn-images-1.medium.com/max/3200/0%2AlMCMZrr7S2WTBbCm.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--msyQ3pCd--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://cdn-images-1.medium.com/max/3200/0%2AlMCMZrr7S2WTBbCm.png" width="800" height="197"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;A key consideration when using any language, tool, or framework is its ease.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Revolutionize your UI testing with AI-powered Smart &lt;a href="https://www.lambdatest.com/smart-visual-ui-testing?utm_source=devto&amp;amp;utm_medium=organic&amp;amp;utm_campaign=nov_27&amp;amp;utm_term=bw&amp;amp;utm_content=webpage" rel="noopener noreferrer"&gt;Visual UI Testing&lt;/a&gt;. Ensure visual perfection across 3000+ environments. Start Testing Now.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;&lt;strong&gt;Key features of Playwright:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;Support for all modern browsers including Chromium, Firefox, Microsoft Edge, and WebKit&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Playwright APIs are available in different languages such as Python, Java, JavaScript, TypeScript, and .NET.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Playwright is cross-platform. Hence tests work seamlessly on Windows, macOS, and Linux in both headless &amp;amp; non-headless modes.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Playwright supports auto-wait, eliminating the need for artificial timeout, making the tests more resilient &amp;amp; less flaky.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Playwright locators provide unique &amp;amp; simple ways to find elements on websites built using modern frameworks.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Playwright offers a unique codegen tool that assists in locating elements&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Playwright locators combined with its rich &amp;amp; easy-to-use APIs make it the perfect choice for end-to-end testing of web applications allowing simpler-than-ever access to elements on websites built using modern web frameworks. You can go through this blog on &lt;a href="https://www.lambdatest.com/blog/playwright-end-to-end-testing/?utm_source=devto&amp;amp;utm_medium=organic&amp;amp;utm_campaign=nov_27&amp;amp;utm_term=bw&amp;amp;utm_content=blog" rel="noopener noreferrer"&gt;Playwright end-to-end testing&lt;/a&gt; to learn more about it.&lt;/p&gt;

&lt;p&gt;In the further sections of this blog on Playwright locators, I will dive deep into the different locators that can be used for locating elements on the AUT.&lt;/p&gt;

&lt;p&gt;To find more use cases of using Playwright, you can refer to this blog on &lt;a href="https://www.lambdatest.com/blog/playwright-for-web-scraping/?utm_source=devto&amp;amp;utm_medium=organic&amp;amp;utm_campaign=nov_27&amp;amp;utm_term=bw&amp;amp;utm_content=blog" rel="noopener noreferrer"&gt;Playwright for web scraping&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--uqoDZ1YY--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://cdn-images-1.medium.com/max/2448/0%2AOQ8fb4D2K6gN8G6n.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--uqoDZ1YY--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://cdn-images-1.medium.com/max/2448/0%2AOQ8fb4D2K6gN8G6n.png" width="800" height="311"&gt;&lt;/a&gt;&lt;/p&gt;
&lt;h2&gt;
  
  
  What are Playwright Locators?
&lt;/h2&gt;

&lt;p&gt;The Playwright locators are at the heart of the Playwright’s ability to automate actions and locate elements on the web page. In layman’s terms, Playwright locators are a way for our scripts to interact with a specific element on a webpage.&lt;/p&gt;

&lt;p&gt;Playwright locators are equipped with auto-wait and retry abilities. This means that the locator will wait for the elements to load &amp;amp; keep retrying automatically before throwing &lt;em&gt;TimeoutError&lt;/em&gt;. You can learn more about it through this blog on &lt;a href="https://www.lambdatest.com/blog/49-common-selenium-exceptions-automation-testing/?utm_source=devto&amp;amp;utm_medium=organic&amp;amp;utm_campaign=nov_27&amp;amp;utm_term=bw&amp;amp;utm_content=blog" rel="noopener noreferrer"&gt;handling exceptions in automation testing&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;There are several ways of getting started with Playwright locators, first using the &lt;em&gt;page.locator()&lt;/em&gt; method to create our locators, which can work with CSS or XPath, and secondly, using the built-in locators.&lt;/p&gt;

&lt;p&gt;While Playwright is fully flexible in allowing us to build our custom locators using &lt;em&gt;page.locator()&lt;/em&gt; the built-in locators are recommended and serve most use cases.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Key Features of Playwright Locators:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;Built-in locators capable of locating elements using ARIA Roles, text, alt-text, placeholders, titles, labels, and custom test-ids.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Ability to chain &amp;amp; filtering locators provides precision to narrow the search for the element.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Fully flexible in working with CSS &amp;amp; XPath if required.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Locators work on the most up-to-date DOM everytime action is performed using them, ensuring reliable tests.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Built-in support for auto-wait and retry, helping write resilient and non-flaky tests.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Built-in methods available to perform various actions on located elements.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;blockquote&gt;
&lt;p&gt;Run Automated Playwright Tests Online. &lt;a href="https://accounts.lambdatest.com/register" rel="noopener noreferrer"&gt;Try LambdaTest Now!&lt;/a&gt;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h2&gt;
  
  
  How to install and set up Playwright?
&lt;/h2&gt;

&lt;p&gt;Before we explore Playwright locators, let’s set up our development environment quickly. Also, if you already have Playwright installed on your machine, please hop on to the &lt;a href="https://www.lambdatest.com/blog/playwright-locators/#sync-vs-async?utm_source=devto&amp;amp;utm_medium=organic&amp;amp;utm_campaign=nov_27&amp;amp;utm_term=bw&amp;amp;utm_content=blog" rel="noopener noreferrer"&gt;Sync vs Async Playwright&lt;/a&gt; section to know why I have used sync APIs for testing.&lt;/p&gt;

&lt;p&gt;Playwright is flexible and allows working with various languages such as Python, NodeJs, Java, .NET, JavaScript, and TypeScript. For this blog on Playwright locators, we will use the Python API of Playwright.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Creating a dedicated virtual environment:&lt;/strong&gt;&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;&lt;p&gt;Create a dedicated folder for our project called &lt;em&gt;playwright locators&lt;/em&gt; (This step is not mandatory but good practice).&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Inside the folder we just created, use the built-in venv module to create a virtual environment named &lt;em&gt;playwright locators&lt;/em&gt;.&lt;/p&gt;&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--qKlh8pMH--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://cdn-images-1.medium.com/max/2132/0%2ANbQUqYN-lk33rMcV.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--qKlh8pMH--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://cdn-images-1.medium.com/max/2132/0%2ANbQUqYN-lk33rMcV.png" width="800" height="296"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;&lt;p&gt;Activate the virtual environment by calling the activate script.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Install the Playwright module using &lt;em&gt;pip install pytest-playwright&lt;/em&gt;.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Lastly, install the required browsers using &lt;em&gt;playwright install&lt;/em&gt;&lt;/p&gt;&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--3wLxLRRQ--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://cdn-images-1.medium.com/max/3200/0%2AWXZh6-XETkQfChJS.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--3wLxLRRQ--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://cdn-images-1.medium.com/max/3200/0%2AWXZh6-XETkQfChJS.png" width="800" height="135"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;All the tests written in this blog have been thoroughly tested on the following versions&lt;br&gt;
Python 3.9.12, pytest 7.2.1, and Playwright 1.29.1.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--dhOPt8Yc--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://cdn-images-1.medium.com/max/2000/0%2A-lhI5JsxW3ZrhJ9t.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--dhOPt8Yc--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://cdn-images-1.medium.com/max/2000/0%2A-lhI5JsxW3ZrhJ9t.png" width="800" height="153"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;a href="https://www.lambdatest.com/smart-visual-ui-testing?utm_source=devto&amp;amp;utm_medium=organic&amp;amp;utm_campaign=nov_27&amp;amp;utm_term=bw&amp;amp;utm_content=webpage" rel="noopener noreferrer"&gt;Automate visual testing&lt;/a&gt; effortlessly with our Smart Visual UI Testing. Achieve flawless UIs on every device and browser. Try it Today.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h2&gt;
  
  
  Sync vs Async Playwright
&lt;/h2&gt;

&lt;p&gt;As mentioned above, we will use the Python API of Playwright for this Playwright locators tutorial and more specifically, the &lt;em&gt;sync_api&lt;/em&gt; version. The reason for choosing sync_api as mentioned in the answer on Stack Overflow is that the sync_api is simply a wrapper around Python’s &lt;em&gt;asyncio_api&lt;/em&gt;. So Playwright makes async calls under the hood.&lt;/p&gt;

&lt;p&gt;When using other languages based on the support, it’s best to choose the appropriate, i.e., sync or async API. Let’s use the &lt;em&gt;Playwright Test Generator&lt;/em&gt; tool to confirm this understanding.&lt;/p&gt;

&lt;p&gt;When running, the test for Python generates code that uses sync_api, as seen from the screenshots below.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--yTpyMMVp--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://cdn-images-1.medium.com/max/3200/0%2ASSnkeSi8MMNb9XKU.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--yTpyMMVp--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://cdn-images-1.medium.com/max/3200/0%2ASSnkeSi8MMNb9XKU.png" width="800" height="429"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;When running, the test for Javascript generates code that uses async_api, as seen from the screenshots below.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--vD_gYMaX--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://cdn-images-1.medium.com/max/3200/0%2AQTblfSmkdqtK0ds0.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--vD_gYMaX--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://cdn-images-1.medium.com/max/3200/0%2AQTblfSmkdqtK0ds0.png" width="800" height="411"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;So, when using &lt;a href="https://www.lambdatest.com/blog/playwright-python-tutorial/" rel="noopener noreferrer"&gt;Playwright with Python&lt;/a&gt;, feel free to use sync_api unless you need fine control over the request behavior in which you can use async_api.&lt;/p&gt;
&lt;h2&gt;
  
  
  Recommended Playwright Locators
&lt;/h2&gt;

&lt;p&gt;Playwright provides flexibility to use the &lt;em&gt;page.locator()&lt;/em&gt; method to locate elements using CSS &amp;amp; XPath, but it’s not recommended. We will discuss why it’s not recommended in the later section of this Playwright locator tutorial.&lt;/p&gt;

&lt;p&gt;Instead, Playwright provides seven built-in locators recommended and sufficient for locating any elements on any web page or website. Let’s now explore in detail the recommended built-in Playwright locators one by one.&lt;/p&gt;

&lt;p&gt;All the tests demonstrated hereafter have been run on the LambdaTest cloud grid. However, the tests are fully compatible with running on local machines. When running locally, just use the correct &lt;a href="https://www.lambdatest.com/blog/end-to-end-tutorial-for-pytest-fixtures-with-examples/?utm_source=devto&amp;amp;utm_medium=organic&amp;amp;utm_campaign=nov_27&amp;amp;utm_term=bw&amp;amp;utm_content=blog" rel="noopener noreferrer"&gt;pytest fixture&lt;/a&gt; mentioned in the section below.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://www.lambdatest.com/blog/benefits-of-cloud-testing/?utm_source=devto&amp;amp;utm_medium=organic&amp;amp;utm_campaign=nov_27&amp;amp;utm_term=bw&amp;amp;utm_content=blog" rel="noopener noreferrer"&gt;Cloud testing&lt;/a&gt; platforms like LambdaTest allow you to perform &lt;a href="https://www.lambdatest.com/blog/playwright-framework/?utm_source=devto&amp;amp;utm_medium=organic&amp;amp;utm_campaign=nov_27&amp;amp;utm_term=bw&amp;amp;utm_content=blog" rel="noopener noreferrer"&gt;Playwright automated testing&lt;/a&gt; at scale, dramatically reducing the time taken to run your &lt;a href="https://www.lambdatest.com/playwright-testing-tool?utm_source=devto&amp;amp;utm_medium=organic&amp;amp;utm_campaign=nov_27&amp;amp;utm_term=bw&amp;amp;utm_content=webpage" rel="noopener noreferrer"&gt;Playwright tests&lt;/a&gt;. You can run your tests on an &lt;a href="https://www.lambdatest.com/online-browser-farm?utm_source=devto&amp;amp;utm_medium=organic&amp;amp;utm_campaign=nov_27&amp;amp;utm_term=bw&amp;amp;utm_content=webpage" rel="noopener noreferrer"&gt;online browser farm&lt;/a&gt; of 50+ browsers and browser versions of Chrome, Chromium, Microsoft Edge, Mozilla Firefox, and even Webkit.&lt;br&gt;
&lt;iframe width="710" height="399" src="https://www.youtube.com/embed/ioJaNCYX7Qc"&gt;
&lt;/iframe&gt;
&lt;br&gt;
You can also subscribe to the &lt;a href="https://www.youtube.com/channel/UCCymWVaTozpEng_ep0mdUyw?sub_confirmation=1" rel="noopener noreferrer"&gt;LambdaTest YouTube Channel&lt;/a&gt; and stay updated with the latest tutorial around &lt;a href="https://www.lambdatest.com/selenium-automation?utm_source=devto&amp;amp;utm_medium=organic&amp;amp;utm_campaign=nov_27&amp;amp;utm_term=bw&amp;amp;utm_content=webpage" rel="noopener noreferrer"&gt;Selenium testing&lt;/a&gt;, &lt;a href="https://www.lambdatest.com/cypress-e2e-testing?utm_source=devto&amp;amp;utm_medium=organic&amp;amp;utm_campaign=nov_27&amp;amp;utm_term=bw&amp;amp;utm_content=webpage" rel="noopener noreferrer"&gt;Cypress E2E testing&lt;/a&gt;, &lt;a href="https://www.lambdatest.com/appium-mobile-testing?utm_source=devto&amp;amp;utm_medium=organic&amp;amp;utm_campaign=nov_27&amp;amp;utm_term=bw&amp;amp;utm_content=webpage" rel="noopener noreferrer"&gt;Appium&lt;/a&gt;, and more.&lt;/p&gt;
&lt;h2&gt;
  
  
  Prerequisites
&lt;/h2&gt;

&lt;p&gt;To use LambdaTest with Playwright, you must set up the necessary imports and dependencies along with the username and access key to run the test on the LambdaTest cloud grid.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Setting up the imports and LambdaTest cloud grid username &amp;amp; access key&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;The &lt;em&gt;load_dotenv(“sample.env”)&lt;/em&gt; reads the username &amp;amp; access key required to access the Playwright on the LambdaTest cloud grid. The username and access key are available on the &lt;a href="https://accounts.lambdatest.com/detail/profile?_gl=1*130n8no*_gcl_au*MTM0NjExNTgyLjE3MDEwOTM2OTI.?utm_source=devto&amp;amp;utm_medium=organic&amp;amp;utm_campaign=nov_27&amp;amp;utm_term=bw&amp;amp;utm_content=profile" rel="noopener noreferrer"&gt;LambdaTest Profile Page&lt;/a&gt;.&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;import json
import os
import re
import subprocess
import sys
import urllib


import pytest
from dotenv import load_dotenv


load_dotenv("sample.env")
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;
&lt;p&gt;&lt;strong&gt;Setting up the Capabilities&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;The capabilities dictionary contains the configuration of our &lt;a href="https://www.lambdatest.com/blog/what-is-test-environment/?utm_source=devto&amp;amp;utm_medium=organic&amp;amp;utm_campaign=nov_27&amp;amp;utm_term=bw&amp;amp;utm_content=blog" rel="noopener noreferrer"&gt;test environment&lt;/a&gt; on the LambdaTest Playwright grid. The configurations encompass various parameters, such as the preferred browser, its specific version, the operating system required to execute the tests, and other relevant settings.&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;capabilities = {
    'browserName': 'Chrome',  # Browsers allowed: `Chrome`, `MicrosoftEdge`, `pw-chromium`, `pw-firefox` and `pw-webkit`
    'browserVersion': 'latest',
    'LT:Options': {
        'platform': 'Windows 10',
        'build': 'Playwright Locators Demo Build',
        'name': 'Playwright Locators Test For Windows 10 &amp;amp; Chrome',
        'user': os.getenv('LT_USERNAME'),
        'accessKey': os.getenv('LT_ACCESS_KEY'),
        'network': True,
        'video': True,
        'visual': True,
        'console': True,
        'tunnel': False,   # Add tunnel configuration if testing locally hosted webpage
        'tunnelName': '',  # Optional
        'geoLocation': '', # country code can be fetched from https://www.lambdatest.com/capabilities-generator/
    }
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;
&lt;p&gt;&lt;strong&gt;Setting up the pytest fixtures:&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;We set up two fixtures, one for our cloud grid and the other for our local grid. So it becomes easy to switch between local &amp;amp; cloud depending on where you want to run the tests.&lt;/p&gt;

&lt;p&gt;Use the &lt;em&gt;local_grid_page&lt;/em&gt; fixture when you run the tests on the local machine.&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;@pytest.fixture(name="local_grid_page")
def playwright_local_grid_page():
    with sync_playwright() as playwright:
        browser = playwright.chromium.launch(headless=True)
        page = browser.new_
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;
&lt;p&gt;Use the &lt;em&gt;cloud_grid_page&lt;/em&gt; fixture when you wish to run the tests on the LambdaTest cloud grid.&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;@pytest.fixture(name="cloud_grid_page")
def playwright_local_grid_page():    
    with sync_playwright() as playwright:
        playwrightVersion = str(subprocess.getoutput('playwright --version')).strip().split(" ")[1]
        capabilities['LT:Options']['playwrightClientVersion'] = playwrightVersion        
        lt_cdp_url = 'wss://cdp.lambdatest.com/playwright?capabilities=' + urllib.parse.quote(json.dumps(capabilities))    
        browser = playwright.chromium.connect(lt_cdp_url)
        page = browser.new_page()    
        yield page
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;
&lt;p&gt;In this pytest Tutorial, learn how to use pytest fixtures with Selenium and how to set up your test using the pytest.fixture() decorator.&lt;br&gt;
&lt;iframe width="710" height="399" src="https://www.youtube.com/embed/1GTBAtCSSco"&gt;
&lt;/iframe&gt;
&lt;/p&gt;

&lt;h2&gt;
  
  
  Locating Elements by Role in Playwright
&lt;/h2&gt;

&lt;p&gt;The locator &lt;em&gt;get_by_role()&lt;/em&gt; allows locating elements by their ARIA role, ARIA attributes, and accessible name. ARIA stands for Accessible Rich Internet Applications. It’s a set of attributes that can be added to HTML elements to help people who use assistive technologies, like screen readers, navigate and understand web content more easily.&lt;/p&gt;

&lt;p&gt;ARIA roles are like labels for different parts of a website, like headings, buttons, and links. These labels help people who use assistive technologies understand the different parts of the website and how to use them.&lt;/p&gt;

&lt;p&gt;Let’s understand the &lt;em&gt;get_by_role()&lt;/em&gt; Playwright locator using the &lt;a href="https://ecommerce-playground.lambdatest.io/?utm_source=devto&amp;amp;utm_medium=organic&amp;amp;utm_campaign=nov_27&amp;amp;utm_term=bw&amp;amp;utm_content=ecommerce" rel="noopener noreferrer"&gt;LambdaTest eCommerce&lt;/a&gt; website and one of the ARIA roles, a &lt;em&gt;button&lt;/em&gt;.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Test Scenario:&lt;/strong&gt;&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;&lt;p&gt;Demo website home page should contain the ‘&lt;em&gt;Search&lt;/em&gt;’ button.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;‘&lt;em&gt;Search&lt;/em&gt;’ button should have a CSS class ‘&lt;em&gt;type-text&lt;/em&gt;’.&lt;/p&gt;&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--iXFbkw2m--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://cdn-images-1.medium.com/max/3200/0%2ABOMsqM0S7rf1MsvY.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--iXFbkw2m--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://cdn-images-1.medium.com/max/3200/0%2ABOMsqM0S7rf1MsvY.png" width="800" height="305"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Sample Implementation:&lt;/strong&gt;&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;# replace cloud_grid_page with local_grid_page while running on local
def test_homepage_contains_search_button(cloud_grid_page):
    cloud_grid_page.goto("https://ecommerce-playground.lambdatest.io/")
    search_button_locator = cloud_grid_page.get_by_role(role="button", name="Search")
    expect(search_button_locator).to_have_class("type-text")
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;&lt;strong&gt;Execution:&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--3EPcMvVB--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://cdn-images-1.medium.com/max/3200/0%2Apxrk5q8yKUH5dqjL.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--3EPcMvVB--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://cdn-images-1.medium.com/max/3200/0%2Apxrk5q8yKUH5dqjL.png" width="800" height="174"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Code Walkthrough:&lt;/strong&gt;&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;&lt;p&gt;Upon inspection of the homepage we see that ‘Search’ is a &lt;em&gt;button&lt;/em&gt; of the type &lt;em&gt;submit&lt;/em&gt; and has a CSS class=’type-text’ assigned to it.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Use the Playwright locator &lt;em&gt;get_by_role()&lt;/em&gt; with arguments as &lt;em&gt;role=‘button’&lt;/em&gt; and &lt;em&gt;name=’Search’&lt;/em&gt;.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;The &lt;em&gt;expect()&lt;/em&gt; &lt;a href="https://www.lambdatest.com/learning-hub/playwright-assertions?utm_source=devto&amp;amp;utm_medium=organic&amp;amp;utm_campaign=nov_27&amp;amp;utm_term=bw&amp;amp;utm_content=learning_hub" rel="noopener noreferrer"&gt;Playwright assertion&lt;/a&gt; then checks if the located element has &lt;em&gt;class=type-text *using the *to_have_class()&lt;/em&gt; method.&lt;/p&gt;&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;It’s worth mentioning that for the &lt;em&gt;get_by_role()&lt;/em&gt; Playwright locator, only the &lt;em&gt;role&lt;/em&gt; argument is mandatory. All other arguments are optional, including the &lt;em&gt;name&lt;/em&gt; we used. An important optional argument is &lt;em&gt;exact&lt;/em&gt;. It can be used to force an exact case-sensitive and whole-string match.&lt;/p&gt;

&lt;p&gt;Let’s now move to the next Playwright locator, where we will demo the exact match argument.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Streamline your app development with &lt;a href="https://www.lambdatest.com/support/docs/app-automation-using-app-center/?utm_source=devto&amp;amp;utm_medium=organic&amp;amp;utm_campaign=nov_27&amp;amp;utm_term=bw&amp;amp;utm_content=support_doc" rel="noopener noreferrer"&gt;Visual Studio App Center&lt;/a&gt; on LambdaTest. Automate your app testing on real devices effortlessly. Learn How.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2&gt;
  
  
  Locating Elements by Text in Playwright
&lt;/h2&gt;

&lt;p&gt;The locator &lt;em&gt;get_by_text()&lt;/em&gt; allows finding elements by the text it contains. This fully versatile Playwright locator can match exact string and substring and also allows using regular expressions.&lt;/p&gt;

&lt;p&gt;This Playwright locator is recommended for finding non-interactive elements such as those contained within HTML tags &lt;em&gt;div, span, and p&lt;/em&gt;. Let’s see this Playwright locator in action using a few scenarios to explore exact matches and regular expressions.&lt;/p&gt;

&lt;p&gt;For this, we again use the &lt;a href="https://ecommerce-playground.lambdatest.io/?utm_source=devto&amp;amp;utm_medium=organic&amp;amp;utm_campaign=nov_27&amp;amp;utm_term=bw&amp;amp;utm_content=ecommerce" rel="noopener noreferrer"&gt;LambdaTest eCommerce&lt;/a&gt; website.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Test Scenario:&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Product page should contain the &lt;strong&gt;exact&lt;/strong&gt; texts for product details:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;Brand:&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Viewed:&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Reward Points:&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Availability:&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--bKRq9-ao--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://cdn-images-1.medium.com/max/3200/0%2AX6tjwDIMYy11nhxR.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--bKRq9-ao--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://cdn-images-1.medium.com/max/3200/0%2AX6tjwDIMYy11nhxR.png" width="800" height="442"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Sample Implementation:&lt;/strong&gt;&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;# replace cloud_grid_page with local_grid_page while running on local
def test_product_details_text_should_be_visible(cloud_grid_page):    
    cloud_grid_page.goto(
        "https://ecommerce-playground.lambdatest.io/index.php?route=product/product&amp;amp;path=57&amp;amp;product_id=28"
    )
    brand_text_locator = cloud_grid_page.get_by_text(text="Brand:", exact=True)
    viewed_text_locator = cloud_grid_page.get_by_text(text="Viewed:", exact=True)
    points_text_locator = cloud_grid_page.get_by_text(text="Reward Points:", exact=True)
    availability_text_locator = cloud_grid_page.get_by_text(text="Availability", exact=True)
    expect(brand_text_locator).to_be_visible()
    expect(viewed_text_locator).to_be_visible()
    expect(points_text_locator).to_be_visible()
    expect(availability_text_locator).not_to_be_visible()
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;&lt;strong&gt;Execution:&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--2YxxE-PW--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://cdn-images-1.medium.com/max/3200/0%2AoRvJoqOijttsZtdT.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--2YxxE-PW--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://cdn-images-1.medium.com/max/3200/0%2AoRvJoqOijttsZtdT.png" width="800" height="110"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Code Walkthrough:&lt;/strong&gt;&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;&lt;p&gt;Upon inspecting the product page, we see the details within the &lt;em&gt;&amp;lt; span &amp;gt;&lt;/em&gt; tag.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;The steps to navigate the product page remain the same as in the previous test case.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;In the next steps, we use the &lt;em&gt;get_by_text()&lt;/em&gt; Playwright locator to match the texts for ‘Brand:’, ‘Viewed:’, and ‘Reward Points:’. We use the &lt;em&gt;expect()&lt;/em&gt; assertion on each of the three locators and call the &lt;em&gt;to_be_visible()&lt;/em&gt; method.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;For the ‘Availability’, we exclude the ‘:’ to demonstrate that the exact argument works. Since we know the exact match, in this case, will fail on the &lt;em&gt;expect()&lt;/em&gt; assertion, we call the &lt;em&gt;not_to_be_visible()&lt;/em&gt; method.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Playwright provides positive and negative assertion methods; we’ve used both in this example.&lt;/p&gt;&lt;/li&gt;
&lt;/ol&gt;

&lt;h2&gt;
  
  
  Locating Multiple Elements by Regular Expressions in Playwright
&lt;/h2&gt;

&lt;p&gt;Whilst we saw exact matches while testing modern web applications, we frequently may need to look for approximate matches, and Playwright locators have us covered.&lt;/p&gt;

&lt;p&gt;We can use regular expressions to locate multiple elements. Let’s quickly look at an example.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Test Scenario:&lt;/strong&gt;&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;&lt;p&gt;Product page multiple occurrences of the brand ‘HTC’.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;There should be 10 matches.&lt;/p&gt;&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--poRMg2iv--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://cdn-images-1.medium.com/max/2000/0%2A6BdiG2uvk9c_EsF-.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--poRMg2iv--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://cdn-images-1.medium.com/max/2000/0%2A6BdiG2uvk9c_EsF-.png" width="800" height="436"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Sample Implementation:&lt;/strong&gt;&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;# replace cloud_grid_page with local_grid_page while running on local
def test_product_name_to_appear_more_than_once(cloud_grid_page):    
    cloud_grid_page.goto(
        "https://ecommerce-playground.lambdatest.io/index.php?route=product/product&amp;amp;path=57&amp;amp;product_id=28"
    )
    brand_name_locator = cloud_grid_page.get_by_text(re.compile("htc", re.IGNORECASE))
    expect(brand_name_locator).to_have_count(10)
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;&lt;strong&gt;Execution:&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--DtxqKViC--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://cdn-images-1.medium.com/max/3200/0%2AQY6FQ8PT0wFfNHFR.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--DtxqKViC--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://cdn-images-1.medium.com/max/3200/0%2AQY6FQ8PT0wFfNHFR.png" width="800" height="104"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Code Walkthrough:&lt;/strong&gt;&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;&lt;p&gt;Upon inspecting the product page, we know that there are 10 occurrences of the brand name ‘htc’.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;The part of opening the product page remains the same.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;In preparing the locator, we use the Python ‘re’ module to match all occurrences and also use IGNORECASE.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;On the &lt;em&gt;expect()&lt;/em&gt; assertion, we call the &lt;em&gt;to_have_count()&lt;/em&gt; method to complete our test.&lt;/p&gt;&lt;/li&gt;
&lt;/ol&gt;

&lt;h2&gt;
  
  
  Locating Form Elements by Label in Playwright
&lt;/h2&gt;

&lt;p&gt;Almost all websites have form fields, and most form controls usually have dedicated labels that could be conveniently used to interact with the form. Playwright locators provide a convenient way of locating form elements using the get_by_label() locator.&lt;/p&gt;

&lt;p&gt;Let’s jump into a demo using the &lt;a href="https://ecommerce-playground.lambdatest.io/?utm_source=devto&amp;amp;utm_medium=organic&amp;amp;utm_campaign=nov_27&amp;amp;utm_term=bw&amp;amp;utm_content=ecommerce" rel="noopener noreferrer"&gt;LambdaTest eCommerce&lt;/a&gt; website.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Test Scenario:&lt;/strong&gt;&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;&lt;p&gt;The login form should have exactly one Email Address label.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;The login form should have exactly one Password label.&lt;/p&gt;&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--tN33M8I_--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://cdn-images-1.medium.com/max/3200/0%2A_MgQvqjdioPEjY3x.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--tN33M8I_--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://cdn-images-1.medium.com/max/3200/0%2A_MgQvqjdioPEjY3x.png" width="800" height="423"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Sample Implementation:&lt;/strong&gt;&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;# replace cloud_grid_page with local_grid_page while running on local
def test_exactly_one_email_and_password_field(cloud_grid_page):    
    cloud_grid_page.goto(
        "https://ecommerce-playground.lambdatest.io/index.php?route=account/login"
    )
    email_address_locator = cloud_grid_page.get_by_label(text="E-Mail Address", exact=True)
    password_locator = cloud_grid_page.get_by_label(text="Password", exact=True)
    expect(email_address_locator).to_have_count(1)
    expect(password_locator).to_have_count(1)
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;&lt;strong&gt;Execution:&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--peRThDKk--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://cdn-images-1.medium.com/max/3200/0%2ALYDrepTrmUeZeDFM.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--peRThDKk--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://cdn-images-1.medium.com/max/3200/0%2ALYDrepTrmUeZeDFM.png" width="800" height="107"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Code Walkthrough:&lt;/strong&gt;&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;&lt;p&gt;The part till we &lt;em&gt;goto()&lt;/em&gt; the login page remains the same.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Initialize two locators using &lt;em&gt;get_by_label()&lt;/em&gt;, one of Email &amp;amp; Password with the exact=True to confirm that only one login form element exists.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;On the &lt;em&gt;expect()&lt;/em&gt; assertion, we call the method &lt;em&gt;to_have_count(1)&lt;/em&gt; to test only one of each email &amp;amp; password field.&lt;/p&gt;&lt;/li&gt;
&lt;/ol&gt;

&lt;h2&gt;
  
  
  Locating Input Elements by Placeholder in Playwright
&lt;/h2&gt;

&lt;p&gt;Most websites have several forms for login, registration, reviews, and customer support. And forms usually have a placeholder text to assist the user in correctly filling forms.&lt;/p&gt;

&lt;p&gt;Playwright locator &lt;em&gt;get_by_placeholder()&lt;/em&gt; comes in handy when locating elements using the placeholder text. Our &lt;a href="https://ecommerce-playground.lambdatest.io/" rel="noopener noreferrer"&gt;LambdaTest eCommerce&lt;/a&gt; demo website has a review form that can be used to explore this locator.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Test Scenario:&lt;/strong&gt;&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;&lt;p&gt;The review form should have one field for the customer name.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;The review form should have one field for customer review.&lt;/p&gt;&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--tYNYFkTZ--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://cdn-images-1.medium.com/max/3200/0%2ADKJIc9rBgQWrh1PA.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--tYNYFkTZ--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://cdn-images-1.medium.com/max/3200/0%2ADKJIc9rBgQWrh1PA.png" width="800" height="434"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Sample Implementation:&lt;/strong&gt;&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;# replace cloud_grid_page with local_grid_page while running on local
def test_review_form_has_customername_customerreview_fields(cloud_grid_page):    
    cloud_grid_page.goto(
        "https://ecommerce-playground.lambdatest.io/index.php?route=product/product&amp;amp;path=25&amp;amp;product_id=28"
    )
    name_locator = cloud_grid_page.get_by_placeholder(text="Your Name", exact=True)
    review_locator = cloud_grid_page.get_by_placeholder(text="Your Review", exact=True)
    expect(name_locator).to_have_count(1)
    expect(review_locator).to_have_count(1)
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;&lt;strong&gt;Execution:&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--V-VoRDwp--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://cdn-images-1.medium.com/max/3200/0%2AuGrk4qnkEAeGXagg.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--V-VoRDwp--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://cdn-images-1.medium.com/max/3200/0%2AuGrk4qnkEAeGXagg.png" width="800" height="109"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Code Walkthrough:&lt;/strong&gt;&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;&lt;p&gt;Upon inspecting the demo website, we can spot the placeholder texts for the review section.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;We use the name of these placeholders as an argument to the &lt;em&gt;get_by_placeholder()&lt;/em&gt; locator. We also only look for an exact matching by setting the argument exact=True.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Calling &lt;em&gt;to_have_count(1)&lt;/em&gt; on the &lt;em&gt;except()&lt;/em&gt; assertion then helps to test our scenario of only one reviewer name field and review input field.&lt;/p&gt;&lt;/li&gt;
&lt;/ol&gt;

&lt;h2&gt;
  
  
  Locating Image Elements by Alt Text in Playwright
&lt;/h2&gt;

&lt;p&gt;All websites have images, and all images must have an alt-text. The reason it’s important to have alt-text is beyond the scope of this blog. Still, to provide a brief explanation, alt-text is important for accessibility, user experience, and image search SEO.&lt;/p&gt;

&lt;p&gt;And Playwright has an efficient &lt;em&gt;get_by_alt_text()&lt;/em&gt; locator to locate image and area elements using the alt-text attribute.&lt;/p&gt;

&lt;p&gt;Below is the homepage of our demo website, and its logo can be used to demonstrate this Playwright locator effectively.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Test Scenario:&lt;/strong&gt;&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;The logo with alt-text=”Poco Electro” should be visible.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--n09vNzsn--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://cdn-images-1.medium.com/max/3200/0%2ABrLcQGxA4egUuPtZ.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--n09vNzsn--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://cdn-images-1.medium.com/max/3200/0%2ABrLcQGxA4egUuPtZ.png" width="800" height="396"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Sample Implementation:&lt;/strong&gt;&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;# replace cloud_grid_page with local_grid_page while running on local
def test_logo_with_alt_text_should_be_visible(cloud_grid_page):
    cloud_grid_page.goto(
        "https://ecommerce-playground.lambdatest.io/index.php?route=common/home"
    )
    logo_locator = cloud_grid_page.get_by_alt_text(text="Poco Electro", exact=True).first
    expect(logo_locator).to_be_visible()
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;&lt;strong&gt;Execution:&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--_zu0ihhv--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://cdn-images-1.medium.com/max/3200/0%2ASqyDkPKvE5nTY-xs.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--_zu0ihhv--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://cdn-images-1.medium.com/max/3200/0%2ASqyDkPKvE5nTY-xs.png" width="800" height="112"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Code Walkthrough:&lt;/strong&gt;&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;&lt;p&gt;This is a rather simple one where we just get the &lt;em&gt;alt-text *from the &lt;a href="https://www.lambdatest.com/blog/top-16-tips-to-use-chrome-dev-tools-for-cross-browser-testing/?utm_source=devto&amp;amp;utm_medium=organic&amp;amp;utm_campaign=nov_27&amp;amp;utm_term=bw&amp;amp;utm_content=blog" rel="noopener noreferrer"&gt;Chrome Developer Tools&lt;/a&gt; and pass it onto the *get_by_alt_text()&lt;/em&gt; locator.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Since there could be multiple uses of the same alt-text, we use the attribute &lt;em&gt;first&lt;/em&gt; to narrow down our results to the first element.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;The &lt;em&gt;expect()&lt;/em&gt; then asserts if the selected element by the locator is visible.&lt;/p&gt;&lt;/li&gt;
&lt;/ol&gt;

&lt;h2&gt;
  
  
  Locating Elements by Title in Playwright
&lt;/h2&gt;

&lt;p&gt;The HTML title attribute specifies additional information about an element. It is most commonly used on the &amp;lt; a &amp;gt; and &amp;lt; img &amp;gt; elements to provide a text description for screen readers and other assistive technologies. The text within the title attribute will typically appear as a tooltip when the mouse pointer hovers over the element.&lt;/p&gt;

&lt;p&gt;Here’s what the title attribute attached to the product image looks like from our demo website. We can use the &lt;em&gt;get_by_title()&lt;/em&gt; Playwright locator to locate elements by title. It’s worth pointing out that our demo website uses the same title text for all images. Hence our locator will return more than one element(20).&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Test Scenario:&lt;/strong&gt;&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;20 elements with title=’HTC Touch HD’ should be present.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--4aSx6THl--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://cdn-images-1.medium.com/max/3200/0%2AquJfte1_UkcC-xNR.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--4aSx6THl--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://cdn-images-1.medium.com/max/3200/0%2AquJfte1_UkcC-xNR.png" width="800" height="442"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Sample Implementation:&lt;/strong&gt;&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;# replace cloud_grid_page with local_grid_page while running on local
def test_display_count_of_all_elements_with_title_htc_touch_hd(cloud_grid_page):    
    cloud_grid_page.goto(
        "https://ecommerce-playground.lambdatest.io/index.php?route=product/product&amp;amp;path=18&amp;amp;product_id=28"
    )
    alt_text_locator = cloud_grid_page.get_by_title(text=re.compile("htc touch hd", re.IGNORECASE))
    expect(alt_text_locator).to_have_count(20)
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;&lt;strong&gt;Execution:&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--W8RqE7qE--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://cdn-images-1.medium.com/max/3200/0%2ABsTOnsJBwB0LvaWk.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--W8RqE7qE--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://cdn-images-1.medium.com/max/3200/0%2ABsTOnsJBwB0LvaWk.png" width="800" height="109"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Code Walkthrough:&lt;/strong&gt;&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;&lt;p&gt;We open the product page, as usual, using the page.goto() method.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Next, we pass the title text we want to locate. Notice the use of the regex Python module to ignore the case.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;The &lt;em&gt;expect()&lt;/em&gt; assertion then verifies if the locator can detect all 20 elements.&lt;/p&gt;&lt;/li&gt;
&lt;/ol&gt;

&lt;h2&gt;
  
  
  Locating Elements by Test ID in Playwright
&lt;/h2&gt;

&lt;p&gt;The Playwright locators, which we have seen so far, are based on HTML tags or attributes assigned to each element. However, the problem with this approach is that these tags, such as roles and attributes may change over time. If this happens, our tests will fail and need to be refactored to account for the changes.&lt;/p&gt;

&lt;p&gt;To avoid this, test-ids are the most resilient way of testing a web application. Developers &amp;amp; QA’s can agree and define explicit test-ids for elements. Using these test-ids to query the elements ensures that our tests continue to work even if the role, text, or title of the elements changes.&lt;/p&gt;

&lt;p&gt;Playwright provides the &lt;em&gt;get_by_test_id()&lt;/em&gt; locator for locating elements by predefined test-ids. The default attribute that the &lt;em&gt;get_by_test_id()&lt;/em&gt; locator looks for is &lt;em&gt;data-testid&lt;/em&gt;. This behavior can be easily changed by setting a custom attribute to look for &lt;em&gt;playwright.selectors.set_test_id_attribute(“data-pw”)&lt;/em&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Playwright Locators using CSS and XPath
&lt;/h2&gt;

&lt;p&gt;Playwright is fully flexible, and while the recommended locators should always be used, there might be scenarios of personal preferences for using &lt;a href="https://www.lambdatest.com/blog/how-pro-testers-use-css-selectors-in-selenium-automation-scripts/?utm_source=devto&amp;amp;utm_medium=organic&amp;amp;utm_campaign=nov_27&amp;amp;utm_term=bw&amp;amp;utm_content=blog" rel="noopener noreferrer"&gt;CSS&lt;/a&gt; or &lt;a href="https://www.lambdatest.com/blog/complete-guide-for-using-xpath-in-selenium-with-examples/?utm_source=devto&amp;amp;utm_medium=organic&amp;amp;utm_campaign=nov_27&amp;amp;utm_term=bw&amp;amp;utm_content=blog" rel="noopener noreferrer"&gt;XPath&lt;/a&gt; to locate elements. For such cases, we need to use the page.locator() that takes a selector describing how to find the element on the page.&lt;/p&gt;

&lt;p&gt;Below are some examples of creating locators using the &lt;em&gt;page.locator()&lt;/em&gt; method using CSS and XPath. We can omit the CSS and XPath prefixes, which will still work fine.&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;# Construction locators using CSS
page.locator("css=button").click()
page.locator("button").click()
page.locator( "#tsf &amp;gt; div:nth-child(2) &amp;gt; div.A8SBwf &amp;gt; div.RNNXgb &amp;gt; div &amp;gt; div.a4bIc &amp;gt; input").click()

# Construction locators Xpath
page.locator("xpath=//button").click()
page.locator("//button").click()    
    page.locator('//*[@id="tsf"]/div[2]/div[1]/div[1]/div/div[2]/input').click()
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;&lt;strong&gt;Shortcomings of CSS and XPath with Playwright&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;As evident from the examples above, CSS &amp;amp; XPath-based locators are tied to the &lt;a href="https://www.lambdatest.com/blog/document-object-model/?utm_source=devto&amp;amp;utm_medium=organic&amp;amp;utm_campaign=nov_27&amp;amp;utm_term=bw&amp;amp;utm_content=blog" rel="noopener noreferrer"&gt;Document Object Model (DOM)&lt;/a&gt; and become complicated to read, maintain, and unreliable very quickly. The DOM is subject to change, and hence the locators using them lead to non-resilient tests.&lt;/p&gt;

&lt;p&gt;So, it’s best to develop tests using the recommended built-in locators such as role, text, or even better define explicit testing contracts using test ids.&lt;/p&gt;

&lt;h2&gt;
  
  
  Filtering and Chaining Playwright Locators
&lt;/h2&gt;

&lt;p&gt;In modern software development, which strongly advocates code reuse and principles like D.R.Y (Don’t Repeat Yourself), there may be instances where we need to refine our results to pinpoint particular elements.&lt;/p&gt;

&lt;p&gt;We ran into this scenario of broad/multiple matches even in the examples we saw for the recommended Playwright locators for locating elements by text in Playwright.&lt;/p&gt;

&lt;p&gt;To handle this narrowing down of elements and to do it efficiently, Playwright has us covered by providing the ability to filter and chain locators.&lt;/p&gt;

&lt;h2&gt;
  
  
  Filtering vs Chaining
&lt;/h2&gt;

&lt;p&gt;Filtering and Chaining are two methods that can be used to narrow down locating elements on a web page.&lt;/p&gt;

&lt;p&gt;Filtering is used to narrow down the search for elements on a web page by specifying additional conditions that must be met. For example, you can use the filter(has_text=) to find an element containing a specific text piece.&lt;/p&gt;

&lt;p&gt;Chaining is used to combine multiple locators in a single search. For example, you can use the get_by_text() and get_by_role() locators to find an element with a specific text and a specific role.&lt;/p&gt;

&lt;p&gt;In summary, Filtering is used to refine a search further after an element has been located, while Chaining combines multiple search criteria to locate an element.&lt;/p&gt;

&lt;h2&gt;
  
  
  Filtering Playwright Locators
&lt;/h2&gt;

&lt;p&gt;Playwright locators can be filtered by text. This search is case-insensitive and can also be done via regular expression. Playwright locators can also be filtered using another locator. For both cases, the method used is the &lt;em&gt;locator.filter()&lt;/em&gt;.&lt;/p&gt;

&lt;p&gt;To understand filtering by another locator, let’s use a new demo website &lt;a href="https://www.lambdatest.com/selenium-playground/?utm_source=devto&amp;amp;utm_medium=organic&amp;amp;utm_campaign=nov_27&amp;amp;utm_term=bw&amp;amp;utm_content=webpage" rel="noopener noreferrer"&gt;LambaTest Playground&lt;/a&gt; which has a nice block containing list items. Here’s the test scenario.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Test Scenario:&lt;/strong&gt;&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;&lt;p&gt;Locate the list containing Input Forms.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Filter &amp;amp; locate the &lt;em&gt;Input Form Submit&lt;/em&gt; list item.&lt;/p&gt;&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--b0mhwrvr--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://cdn-images-1.medium.com/max/3200/0%2An6nzQbild4oebvD8.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--b0mhwrvr--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://cdn-images-1.medium.com/max/3200/0%2An6nzQbild4oebvD8.png" width="800" height="429"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Sample Implementation:&lt;/strong&gt;&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;# replace cloud_grid_page with local_grid_page while running on local
def test_locator_filter_by_another_locator(cloud_grid_page):
    cloud_grid_page.goto(
        "https://www.lambdatest.com/selenium-playground/"
    )
    base_locator = cloud_grid_page.get_by_role("listitem")
    list_heading_locator = base_locator.filter(
        has=cloud_grid_page.get_by_text(text=re.compile("input form submit", re.IGNORECASE))
        )    
    expect(list_heading_locator).to_have_text('Input Form Submit')
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;&lt;strong&gt;Execution:&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--R0GfXAVh--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://cdn-images-1.medium.com/max/3200/0%2AzFSc6rpaKxC8suGT.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--R0GfXAVh--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://cdn-images-1.medium.com/max/3200/0%2AzFSc6rpaKxC8suGT.png" width="800" height="119"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Code Walkthrough:&lt;/strong&gt;&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;&lt;p&gt;Upon inspection of the website using Chrome Developer Tools we see the presence of a list containing all the texts of the several different demos.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;We form the &lt;em&gt;base_locator&lt;/em&gt; using the &lt;em&gt;get_by_role()&lt;/em&gt; locator and the ARIA role listitem.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;This base_locator will contain all the lists on the page, and there are plenty.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;So, we narrow down our lookup by calling the &lt;em&gt;filter()&lt;/em&gt; method on our &lt;em&gt;base_locator&lt;/em&gt;.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;In the filter, we use the recommended &lt;em&gt;get_by_text()&lt;/em&gt; locator and also make sure we handle case sensitivity using regular expressions by searching for the text ‘&lt;em&gt;input for submit&lt;/em&gt;’, this filtered locator is called &lt;em&gt;list_heading_locator&lt;/em&gt;.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;On the filtered &lt;em&gt;list_heading_locator we call the except()&lt;/em&gt; assertion and ensure we only have located the exact list item(for input form submit) that we intended to locate&lt;/p&gt;&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;&lt;strong&gt;Example: Filtering by Text&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;To understand &lt;strong&gt;filtering by text&lt;/strong&gt;, let’s consider the same &lt;a href="https://www.lambdatest.com/selenium-playground/?utm_source=devto&amp;amp;utm_medium=organic&amp;amp;utm_campaign=nov_27&amp;amp;utm_term=bw&amp;amp;utm_content=webpage" rel="noopener noreferrer"&gt;LambaTest Playground&lt;/a&gt; website but a different list, which has a lot of texts with the word ‘Table’.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Test Scenario:&lt;/strong&gt;&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;&lt;p&gt;Locate the list containing the Table.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;The list has 5 items.&lt;/p&gt;&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--AROmvOLo--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://cdn-images-1.medium.com/max/3200/0%2Av10sLye_k6O0D8vS.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--AROmvOLo--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://cdn-images-1.medium.com/max/3200/0%2Av10sLye_k6O0D8vS.png" width="800" height="441"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Sample Implementation:&lt;/strong&gt;&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;# replace cloud_grid_page with local_grid_page while running on local
def test_locator_filter_by_text(cloud_grid_page):
    cloud_grid_page.goto(
        "https://www.lambdatest.com/selenium-playground/"
    )
    base_locator = cloud_grid_page.get_by_role("listitem")
    table_list_locator = base_locator.filter(
        has_text=re.compile("table", re.IGNORECASE)
        )    
    expect(table_list_locator).to_have_count(5)
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;&lt;strong&gt;Execution:&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--sIYrINOg--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://cdn-images-1.medium.com/max/3200/0%2ApyWDzgNmbNe-Fw1U.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--sIYrINOg--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://cdn-images-1.medium.com/max/3200/0%2ApyWDzgNmbNe-Fw1U.png" width="800" height="109"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Code Walkthrough:&lt;/strong&gt;&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;&lt;p&gt;The steps till we set up the base_locator remain the same as above &lt;em&gt;test_locator_filter_by_another_locator()&lt;/em&gt; test.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;From there, instead of using another locator-based filter, we filter based on &lt;em&gt;text=’table’&lt;/em&gt; by passing it to the &lt;em&gt;filter()&lt;/em&gt; method.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;The demo website inspection clearly shows the ‘Table’ list has 5 items, which is what we assert in the &lt;em&gt;except()&lt;/em&gt;.&lt;/p&gt;&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;That is all about filtering locators. In summary, they allow us to narrow down our search, we need to call the &lt;em&gt;filter()&lt;/em&gt; method on a locator to apply to the filter, and the Filtering can be done either by text or using another locator, which is simple and efficient.&lt;/p&gt;

&lt;h2&gt;
  
  
  Chaining Playwright Locators
&lt;/h2&gt;

&lt;p&gt;It’s not always possible for a single locator to locate the exact element on a webpage. In such scenarios, Chaining is the second method to narrow the search to a specific element by combining multiple locators.&lt;/p&gt;

&lt;p&gt;Let’s revisit the same scenario we saw in the filter, i.e., locating an element from a list. Still, we will use locator Chaining to find the element instead of Filtering.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Test Scenario:&lt;/strong&gt;&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;&lt;p&gt;Locate the list containing Input Forms and narrow it down to Input Form Submit.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;The Input Form Submit should be a link. The link href attribute should contain the words input-form-demo.&lt;/p&gt;&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--uFBwCRyh--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://cdn-images-1.medium.com/max/3200/0%2AABOPbFDlXF8lMFil.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--uFBwCRyh--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://cdn-images-1.medium.com/max/3200/0%2AABOPbFDlXF8lMFil.png" width="800" height="444"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Sample Implementation:&lt;/strong&gt;&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;# replace cloud_grid_page with local_grid_page while running on local
def test_locator_chaining(cloud_grid_page):    
    cloud_grid_page.goto(
        "https://www.lambdatest.com/selenium-playground/"
    )
    input_form_locator = cloud_grid_page.get_by_role("listitem").get_by_text(text=re.compile("input form submit", re.IGNORECASE))
    expect(input_form_locator).to_have_attribute(name="href", value=re.compile("input-form-demo", re.IGNORECASE))    
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;&lt;strong&gt;Execution:&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--ovUCVLjp--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://cdn-images-1.medium.com/max/3200/0%2ADYONZ_juceS_05qG.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--ovUCVLjp--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://cdn-images-1.medium.com/max/3200/0%2ADYONZ_juceS_05qG.png" width="800" height="110"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Code Walkthrough:&lt;/strong&gt;&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;&lt;p&gt;The DOM structure upon inspection clearly shows that we have a list &amp;amp; within each list is a link to the individual demo.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Hence, the &lt;em&gt;first locator get_by_role(“listitem”)&lt;/em&gt; will locate all the lists on the page. There are approximately 6.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;We then chain the &lt;em&gt;get_by_text(text=re.compile(“input form submit”, re.IGNORECASE)&lt;/em&gt; to narrow down the result set to exactly match the input form submit list item.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Lastly, the &lt;em&gt;except()&lt;/em&gt; assertion checks if the located element has a link, i.e., href attribute, and does a regular expression match to ensure the link contains certain keywords we know are necessary&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;So, that is about Chaining. Again, Filtering is used to refine a search further after an element has been located, while chaining combines multiple search criteria to locate an element.&lt;/p&gt;&lt;/li&gt;
&lt;/ol&gt;

&lt;h2&gt;
  
  
  Playwright Locator Methods
&lt;/h2&gt;

&lt;p&gt;Throughout this Playwright locators blog, I have touched upon the various methods available in Playwright, such as filter(), first(), get_attribute(), etc. However, that is just the tip of the iceberg. There are numerous methods available to locate and perform desired actions using Playwright locators.&lt;/p&gt;

&lt;p&gt;A complete list of available methods can be found in the official documentation. I strongly urge everyone to go over the Locators API documentation and explore the available methods of this super-powerful library.&lt;/p&gt;

&lt;h2&gt;
  
  
  Constructing Locators using Playwright’s Test Generator
&lt;/h2&gt;

&lt;p&gt;We have been writing the tests manually by inspecting the DOM. But since we are talking about an automation framework, it only makes sense that Playwright has something which can assist and automate part of locating elements on a web page. Playwright has a powerful feature called the Test Generator, which can help you locate the elements and make it easier to write tests.&lt;/p&gt;

&lt;p&gt;My only complaint about this feature is that it’s called &lt;em&gt;Test Generator&lt;/em&gt;. The command to start it has the word codegen, and the subsequent generator that starts has the title Playwright Inspector. I sincerely wish Microsoft would fix this and agree on one name to simplify lives.&lt;/p&gt;

&lt;p&gt;To start the Test Generator, we simply use the command shown below.&lt;/p&gt;

&lt;p&gt;playwright codegen &lt;a href="https://ecommerce-playground.lambdatest.io/" rel="noopener noreferrer"&gt;https://ecommerce-playground.lambdatest.io/&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Feel free to replace the URL with the website you intend to write tests for.&lt;/p&gt;

&lt;h2&gt;
  
  
  Test Generator in Action
&lt;/h2&gt;

&lt;p&gt;Here’s what the Test Generator looks like and the opened Chromium browser where we can perform the actions we want. Based on our actions, the Test Generator will generate the code for us.&lt;/p&gt;

&lt;p&gt;The cool thing about the Test Generator is that you can choose your intended language. For this blog, as you know, I have chosen Python, but Playwright allows you to choose whichever you wish from the drop-down highlighted in red.&lt;/p&gt;

&lt;p&gt;Based on your selection, the Test Generator will write the code for us.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--AJbg7Fk7--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://cdn-images-1.medium.com/max/3200/0%2AsWnXQGKOWbei1g5i.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--AJbg7Fk7--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://cdn-images-1.medium.com/max/3200/0%2AsWnXQGKOWbei1g5i.png" width="800" height="435"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Let’s say we want to locate the search bar, type in mobile, and hit the search button. Here is the snapshot of the actions performed and the code the Test Generator was able to write for us with all the locators. We can then use these locators to run tests using the &lt;em&gt;except()&lt;/em&gt; assertion.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s---8FVTihm--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://cdn-images-1.medium.com/max/3200/0%2AAt4MdXzreE5Bg_jL.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s---8FVTihm--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://cdn-images-1.medium.com/max/3200/0%2AAt4MdXzreE5Bg_jL.png" width="800" height="452"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;from playwright.sync_api import Page, expect


def test_example(page: Page) -&amp;gt; None:
    page.goto("https://ecommerce-playground.lambdatest.io/")
    page.get_by_role("textbox", name="Search For Products").click()
    page.get_by_role("textbox", name="Search For Products").fill("mobile")
    page.get_by_role("button", name="Search").click()
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;The Playwright Test Generator tool also comes with several built-in options to emulate devices, viewport sizes, color schemes, geolocation, timezone, etc.&lt;/p&gt;

&lt;p&gt;Let’s see a quick demo of two of these features, and feel free to experiment with the other by referencing the official documentation.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Emulate Viewport Size:&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Notice how Playwright generates all the boiler-plate code for setting the viewport as a fixture.&lt;/p&gt;

&lt;p&gt;playwright codegen --viewport-size=800,600 &lt;a href="https://ecommerce-playground.lambdatest.io/" rel="noopener noreferrer"&gt;https://ecommerce-playground.lambdatest.io&lt;/a&gt;/&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--pumPqKrH--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://cdn-images-1.medium.com/max/3200/0%2AZ9aUEvHY2slS0Jry.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--pumPqKrH--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://cdn-images-1.medium.com/max/3200/0%2AZ9aUEvHY2slS0Jry.png" width="800" height="398"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Emulate Devices:&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Notice how Playwright generates all the boiler-plate code for setting the device selected for testing as a fixture.&lt;/p&gt;

&lt;p&gt;playwright codegen --device=”iPhone11” &lt;a href="https://ecommerce-playground.lambdatest.io/" rel="noopener noreferrer"&gt;https://ecommerce-playground.lambdatest.io/&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--FluhZJPz--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://cdn-images-1.medium.com/max/2570/0%2AVEuVLNG7aOgqpkPO.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--FluhZJPz--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://cdn-images-1.medium.com/max/2570/0%2AVEuVLNG7aOgqpkPO.png" width="800" height="626"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Want to demonstrate your expertise as a Playwright automation tester? LambdaTest’s Playwright 101 certification program is aimed at developers who want to showcase their proficiency in using Playwright for end-to-end testing of modern web applications.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--8eEt5WHF--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://cdn-images-1.medium.com/max/2000/0%2AdEu7LXZ9QDRt_fIX.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--8eEt5WHF--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://cdn-images-1.medium.com/max/2000/0%2AdEu7LXZ9QDRt_fIX.png" width="800" height="155"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Conclusion
&lt;/h2&gt;

&lt;p&gt;That is it for this blog !!! In conclusion, Playwright locators are powerful tools that allow you to locate and interact with elements on a web page with the ability to locate elements by their attributes, role, text, label, alt-text, etc. Understanding the locators, Filtering, and Chaining and how to use them effectively is essential for writing efficient and reliable automated tests with Playwright.&lt;/p&gt;

&lt;p&gt;The Playwright Test Generator tool can help you quickly and easily create boilerplate code for locating elements. The time and effort saved can be used to focus on more important tasks like writing resilient tests and improving coverage.&lt;/p&gt;

&lt;p&gt;We have explored in great detail the world of Playwright Locators. Even with the level of detail, I’ve tried to cover, there’s much more to explore and experiment with, and I hope this blog serves as a solid foundation for that.&lt;/p&gt;

</description>
      <category>playwright</category>
      <category>opensource</category>
      <category>programming</category>
      <category>testing</category>
    </item>
    <item>
      <title>How To Use Playwright For Web Scraping with Python</title>
      <dc:creator>Jaydeep Karale</dc:creator>
      <pubDate>Thu, 30 Nov 2023 13:14:04 +0000</pubDate>
      <link>https://dev.to/_jaydeepkarale/how-to-use-playwright-how-to-use-playwright-for-web-scraping-with-python-m3</link>
      <guid>https://dev.to/_jaydeepkarale/how-to-use-playwright-how-to-use-playwright-for-web-scraping-with-python-m3</guid>
      <description>&lt;p&gt;In today’s data-driven world, the ability to access and analyze large amounts of data can give researchers, businesses &amp;amp; organizations a competitive edge. One of the most important &amp;amp; free sources of this data is the Internet, which can be accessed and mined through web scraping.&lt;/p&gt;

&lt;p&gt;Web scraping, also known as web data extraction or web harvesting, involves using code to make HTTP requests to a website’s server, download the content of a webpage, and parse that content to extract the desired data from websites and store it in a structured format for further analysis.&lt;/p&gt;

&lt;p&gt;When it comes to data extraction &amp;amp; processing, Python has become the de-facto language in today’s world. In this &lt;a href="https://www.lambdatest.com/blog/playwright-python-tutorial/?utm_source=devto&amp;amp;utm_medium=organic&amp;amp;utm_campaign=nov_27&amp;amp;utm_term=bw&amp;amp;utm_content=blog" rel="noopener noreferrer"&gt;Playwright Python tutorial&lt;/a&gt; on using Playwright for web scraping, we will combine &lt;a href="https://www.lambdatest.com/playwright-testing?utm_source=devto&amp;amp;utm_medium=organic&amp;amp;utm_campaign=nov_27&amp;amp;utm_term=bw&amp;amp;utm_content=webpage" rel="noopener noreferrer"&gt;Playwright&lt;/a&gt;, one of the newest entrants into the world of &lt;a href="https://www.lambdatest.com/learning-hub/web-testing?utm_source=devto&amp;amp;utm_medium=organic&amp;amp;utm_campaign=nov_27&amp;amp;utm_term=bw&amp;amp;utm_content=learning_hub" rel="noopener noreferrer"&gt;web testing&lt;/a&gt; &amp;amp; browser automation with Python to learn techniques for Playwright Python scraping.&lt;/p&gt;

&lt;p&gt;The reasons for choosing Playwright over some popular alternatives are its developer-friendly APIs, automatic waiting feature, which avoids timeouts in case of slow-loading websites, superb documentation with examples covering various use cases, and a very active community.&lt;/p&gt;

&lt;p&gt;So, let’s get started.&lt;/p&gt;

&lt;h2&gt;
  
  
  Key Use Cases of Web Scraping
&lt;/h2&gt;

&lt;p&gt;Now, let’s explore some of the most popular web scraping use cases.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Price &amp;amp; Product Comparison for E-Commerce&lt;/strong&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Extracting prices, products, and images from e-commerce websites to gain insights about pricing &amp;amp; comparison with competitors.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Data for Machine Learning Models&lt;/strong&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Data scientists can use web scraping to get access to or create data sets for various use cases, such as feeding it to machine learning models.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Aggregate Job Listings&lt;/strong&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Web scraping is also useful for downloading job listings from various websites and making them available over a single channel.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Aggregate Discounts From Travel Websites&lt;/strong&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Aggregator websites that display the best hotel or flight ticket prices also use web scraping to get pricing data from various websites.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Master &lt;a href="https://www.lambdatest.com/learning-hub/visual-regression-testing?utm_source=devto&amp;amp;utm_medium=organic&amp;amp;utm_campaign=nov_27&amp;amp;utm_term=bw&amp;amp;utm_content=learning_hub" rel="noopener noreferrer"&gt;visual regression testing tools&lt;/a&gt; with our comprehensive guide. Dive into best practices and elevate your testing strategies.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2&gt;
  
  
  A Word of Caution: Responsible Web Scraping
&lt;/h2&gt;

&lt;p&gt;Whilst web scraping is a powerful tool, there are a few ethical &amp;amp; potentially legal considerations to consider when doing web scraping.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Respect terms of use&lt;/strong&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Many websites have terms of use that prohibit or limit web scraping. It is important to read and understand these terms before beginning any web scraping projects and to obtain permission if necessary.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Sensitive or protected data&lt;/strong&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Many websites have terms of use that prohibit or limit web scraping. It is important to read and understand these terms before beginning any web scraping projects and to obtain permission if necessary.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Don’t burden servers&lt;/strong&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;By following these guidelines and using web scraping responsibly, you can ensure that your web scraping projects are legal and ethical and that you are not causing harm to the websites you are scraping.&lt;/p&gt;

&lt;p&gt;Having seen so many use cases, it’s evident that the market for web scraping is huge. And as the market grows for anything, so do the available tools. In this Playwright for web scraping tutorial, we will explore in-depth web scraping with Playwright in Python and how it can extract data from the web.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fcdn-images-1.medium.com%2Fmax%2F2000%2F0%2ATVGegMFNRDe2VmJh.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fcdn-images-1.medium.com%2Fmax%2F2000%2F0%2ATVGegMFNRDe2VmJh.png"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  What is Playwright?
&lt;/h2&gt;

&lt;p&gt;Playwright is the latest entrant into the array of frameworks (e.g., Selenium, Cypress, etc.) available for &lt;a href="https://www.lambdatest.com/learning-hub/web-automation?utm_source=devto&amp;amp;utm_medium=organic&amp;amp;utm_campaign=nov_27&amp;amp;utm_term=bw&amp;amp;utm_content=learning_hub" rel="noopener noreferrer"&gt;web automation&lt;/a&gt; testing. It enables fast and reliable &lt;a href="https://www.lambdatest.com/learning-hub/end-to-end-testing?utm_source=devto&amp;amp;utm_medium=organic&amp;amp;utm_campaign=nov_27&amp;amp;utm_term=bw&amp;amp;utm_content=learning_hub" rel="noopener noreferrer"&gt;end-to-end testing&lt;/a&gt; for modern web apps.&lt;/p&gt;

&lt;p&gt;At the time of writing this Playwright for web scraping tutorial, the latest stable version of Playwright is 1.28.0, and Playwright is now consistently hitting the &amp;gt;20K download per day mark, as seen from PyPi Stats.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fcdn-images-1.medium.com%2Fmax%2F2918%2F0%2ACjP4qmIhStZD2NYv.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fcdn-images-1.medium.com%2Fmax%2F2918%2F0%2ACjP4qmIhStZD2NYv.png"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Below are the download trends of Playwright in comparison to a popular alternative, Selenium, taken from Pip Trends.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fcdn-images-1.medium.com%2Fmax%2F3200%2F0%2AwAiwEC8Yvs_0NqZG.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fcdn-images-1.medium.com%2Fmax%2F3200%2F0%2AwAiwEC8Yvs_0NqZG.png"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;A key consideration to make when using any language, tool or framework is the ease of its use. Playwright is a perfect choice for web scraping because of its rich &amp;amp; easy-to-use APIs, which allow simpler-than-ever access to elements on websites built using modern web frameworks. You can learn more about it through this blog on &lt;a href="https://www.lambdatest.com/blog/testing-modern-web-applications-with-playwright/?utm_source=devto&amp;amp;utm_medium=organic&amp;amp;utm_campaign=nov_27&amp;amp;utm_term=bw&amp;amp;utm_content=blog" rel="noopener noreferrer"&gt;testing modern web applications with Playwright&lt;/a&gt;.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Enhance your &lt;a href="https://www.lambdatest.com/learning-hub/visual-regression-testing?utm_source=devto&amp;amp;utm_medium=organic&amp;amp;utm_campaign=nov_27&amp;amp;utm_term=bw&amp;amp;utm_content=learning_hub" rel="noopener noreferrer"&gt;visual testing&lt;/a&gt; skills with our in-depth tutorial. Discover the secrets to flawless user interfaces. Explore Now.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2&gt;
  
  
  Features of Playwright
&lt;/h2&gt;

&lt;p&gt;Some unique and key features of Playwright are&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Cross-Browser&lt;/strong&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Playwright supports all modern rendering engines such as Chromium, Firefox, and WebKit, which makes it a good choice when it comes to working &amp;amp; testing across several different browsers.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Cross-Language&lt;/strong&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Playwright APIs are available in different languages such as Python, Java, JavaScript, TypeScript &amp;amp; .NET, making it easier for developers from various language backgrounds to implement web scraping &amp;amp; browser-related tasks.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Cross-Platform&lt;/strong&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Websites built using modern web frameworks must be tested across all operating systems to ensure they render correctly on each of them. Playwright works seamlessly on Windows, macOS, and Linux in both headless &amp;amp; headed modes.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Auto-Wait&lt;/strong&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Playwright supports auto-wait, eliminating the need for artificial timeout, making the tests more resilient &amp;amp; less flaky&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Web-first Assertions&lt;/strong&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Playwright assertions are explicitly created for the dynamic web. Checks are automatically retried until the necessary conditions are met, making it an ideal candidate for testing websites built using modern web frameworks.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Locators&lt;/strong&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Playwright’s locators provide unique &amp;amp; simple ways to find elements on websites built using modern web frameworks. Later in this Playwright for web scraping tutorial, we will deep dive into Playwright’s locators and why they make life so much easier.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Codegen&lt;/strong&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Playwright offers a unique codegen tool that can essentially write the code for you. You can start the codegen using the playwright codegen websitename and perform the browser actions. The codegen will record and provide boilerplate code that can be used or adjusted per our requirements.&lt;br&gt;
&lt;iframe width="710" height="399" src="https://www.youtube.com/embed/wawbt1cATsk"&gt;
&lt;/iframe&gt;
&lt;br&gt;
Now that we know what Playwright is, let’s go ahead and explore how we can leverage its Python API for web scraping, starting firstly with installation &amp;amp; setup.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Discover the top 25 &lt;a href="https://www.lambdatest.com/blog/visual-layout-testing-tools/?utm_source=devto&amp;amp;utm_medium=organic&amp;amp;utm_campaign=nov_27&amp;amp;utm_term=bw&amp;amp;utm_content=blog" rel="noopener noreferrer"&gt;visual testing tools&lt;/a&gt; of 2023 for a seamless user experience. Find your ideal tool for flawless UIs.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2&gt;
  
  
  Why Playwright for Web Scraping with Python?
&lt;/h2&gt;

&lt;p&gt;As mentioned above, it’s possible to use Playwright for web scraping with different languages such as JavaScript, TypeScript, Java, .Net, and Python. So, it is necessary to understand why Python.&lt;/p&gt;

&lt;p&gt;I have been programming for ten years using languages such as C++, Java, JavaScript &amp;amp; Python, but in my experience, Python is the most developer-friendly and productivity-oriented language.&lt;/p&gt;

&lt;p&gt;It abstracts unnecessary complications of certain other programming languages and lets developers focus on writing quality code and shorten the delivery time whilst also enjoying writing the code.&lt;/p&gt;

&lt;p&gt;A quick case for why I love &lt;a href="https://www.lambdatest.com/blog/python-automation-testing/?utm_source=devto&amp;amp;utm_medium=organic&amp;amp;utm_campaign=nov_27&amp;amp;utm_term=bw&amp;amp;utm_content=blog" rel="noopener noreferrer"&gt;Python automation testing&lt;/a&gt; &amp;amp; why we choose Playwright for web scraping, specifically using its Python API.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Easy to learn &amp;amp; use&lt;/strong&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The Zen Of Python, which defines the guiding principle of Python’s design, mentions ‘Simple Is Better Than Complex’. So, Python is a language developed explicitly with productivity, ease of use, and faster delivery in mind. It’s one of the easiest, most fun, and fastest programming languages to learn and use.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;De-facto choice for processing data&lt;/strong&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Python has become the de-facto language for working with data in the modern world. Various packages such as Pandas, Numpy, and PySpark are available and have extensive documentation and a great community to help write code for various use cases around data processing. Since web scraping results in a lot of data being downloaded &amp;amp; processed, Python is a very good choice. You can learn more about it through this blog on &lt;a href="https://www.lambdatest.com/blog/web-scraping-using-selenium-and-python/?utm_source=devto&amp;amp;utm_medium=organic&amp;amp;utm_campaign=nov_27&amp;amp;utm_term=bw&amp;amp;utm_content=blog" rel="noopener noreferrer"&gt;web scraping with Python&lt;/a&gt;.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Widely used in Industry&lt;/strong&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Python is now widely used in almost all areas of the software development lifecycle, including development, testing, DevOps, and support. Its adoption, especially since the release of the 3.X versions, has been phenomenal.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Great community &amp;amp; ecosystem&lt;/strong&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The Python ecosystem is mature and has a vast community and a wide range of packages available in the Python ecosystem for processing data.&lt;/p&gt;

&lt;p&gt;With an understanding of why we chose to work with Playwright for web scraping in Python, let’s now look at Playwright’s Locators. Playwright supports a variety of &lt;a href="https://www.lambdatest.com/blog/locators-in-selenium-webdriver-with-examples/?utm_source=devto&amp;amp;utm_medium=organic&amp;amp;utm_campaign=nov_27&amp;amp;utm_term=bw&amp;amp;utm_content=blog" rel="noopener noreferrer"&gt;locator strategies&lt;/a&gt;, including CSS Selectors, XPath expressions, and text content matching.&lt;/p&gt;

&lt;p&gt;Locators are a critical component of Playwright, making web browser-related tasks possible, easy, reliable, and fun.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Run Automated Playwright Tests Online. &lt;a href="https://accounts.lambdatest.com/register" rel="noopener noreferrer"&gt;Try LambdaTest Now!&lt;/a&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2&gt;
  
  
  Understanding Playwright Locators
&lt;/h2&gt;

&lt;p&gt;Locators are the centerpiece of the Playwright’s ability to automate actions &amp;amp; locate elements on the browser.&lt;/p&gt;

&lt;p&gt;Simply put, locators are a way of identifying a specific element on a webpage so that we can interact with it in our scripts.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fcdn-images-1.medium.com%2Fmax%2F3200%2F0%2ABYgasA2Mr-xz4orI.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fcdn-images-1.medium.com%2Fmax%2F3200%2F0%2ABYgasA2Mr-xz4orI.png"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fcdn-images-1.medium.com%2Fmax%2F2800%2F0%2A_0kVAYqx-qVtaEtC.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fcdn-images-1.medium.com%2Fmax%2F2800%2F0%2A_0kVAYqx-qVtaEtC.png"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Key Features of Playwright Locators
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Precision&lt;/strong&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Locators allow you to specify exactly which element you want to interact with on a web page. This can be useful when there are multiple elements on the page with similar attributes or characteristics. You can use a locator to select a specific element rather than just the first one that matches the criteria.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Flexibility&lt;/strong&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Playwright provides several different types of locators, including &lt;a href="https://www.lambdatest.com/blog/how-pro-testers-use-css-selectors-in-selenium-automation-scripts/?utm_source=devto&amp;amp;utm_medium=organic&amp;amp;utm_campaign=nov_27&amp;amp;utm_term=bw&amp;amp;utm_content=blog" rel="noopener noreferrer"&gt;CSS Selectors&lt;/a&gt;, &lt;a href="https://www.lambdatest.com/blog/complete-guide-for-using-xpath-in-selenium-with-examples/?utm_source=devto&amp;amp;utm_medium=organic&amp;amp;utm_campaign=nov_27&amp;amp;utm_term=bw&amp;amp;utm_content=blog" rel="noopener noreferrer"&gt;XPath&lt;/a&gt;, and text content, so you can choose the one that best suits your needs. This allows you to tailor your locators to the specific structure and content of the web page you are working with.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Up-to-date DOM Elements&lt;/strong&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Every time a locator is used for an action, an up-to-date DOM element is located on the page. If the DOM changes between the calls due to re-render, the new element corresponding to the locator will be used.&lt;/p&gt;

&lt;p&gt;In the below example snippet, the button element will be located twice: once for the hover() action and once for the click() action to ensure we always have the latest data. This feature is available out of the box without needing additional code.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fcdn-images-1.medium.com%2Fmax%2F2180%2F0%2AZNLki-Vo14u84VWg.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fcdn-images-1.medium.com%2Fmax%2F2180%2F0%2AZNLki-Vo14u84VWg.png"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;em&gt;**Note&lt;/em&gt;&lt;em&gt;: In Playwright, waits are unnecessary because it automatically waits for elements to be available before interacting with them. This means you do not have to manually add delays or sleep in your test code to wait for elements to load.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Additionally, Playwright includes many built-in retry mechanisms that make it more resilient to flaky tests. For example, if an element is not found on the page, Playwright will automatically retry for a certain amount before giving up and throwing an error. This can help to reduce the need for explicit waits in your tests.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;You can learn more about it through this blog on &lt;a href="https://www.lambdatest.com/blog/types-of-waits-in-selenium/?utm_source=devto&amp;amp;utm_medium=organic&amp;amp;utm_campaign=nov_27&amp;amp;utm_term=bw&amp;amp;utm_content=blog" rel="noopener noreferrer"&gt;types of waits&lt;/a&gt;.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Support for multiple browsers&lt;/strong&gt;&lt;br&gt;
Playwright supports all modern rendering engines, including WebKit, Chromium, and Firefox, so you can use the same locators and code across different browsers.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Test Mobile Web&lt;/strong&gt;&lt;br&gt;
Native mobile emulation of Google Chrome for Android and Mobile Safari. The same rendering engine works locally or if you wish to choose so, in the Cloud.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The below table summarizes some built-in locators available as part of the Playwright&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fcdn-images-1.medium.com%2Fmax%2F2000%2F1%2ApMyNuDFo4y8dIVfEl5ci9w.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fcdn-images-1.medium.com%2Fmax%2F2000%2F1%2ApMyNuDFo4y8dIVfEl5ci9w.png"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Playwright Setup &amp;amp; Installation
&lt;/h2&gt;

&lt;p&gt;When programming in Python it’s a de-facto approach to have a separate virtual environment for each project. This helps us manage dependencies better without disturbing our base Python installation.&lt;/p&gt;

&lt;h2&gt;
  
  
  Virtual Environment &amp;amp; Playwright Installation
&lt;/h2&gt;

&lt;ol&gt;
&lt;li&gt;&lt;p&gt;Create a dedicated folder for the project called &lt;em&gt;playwrightwebscraping&lt;/em&gt;. (This step is not mandatory but is good practice).&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Next, using Python’s built-in venv module, let’s create a virtual environment named &lt;em&gt;playwrightplayground&lt;/em&gt; and activate it by calling the activate script.&lt;/p&gt;&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fcdn-images-1.medium.com%2Fmax%2F2000%2F0%2A_WAYziHSn5W5lg4S.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fcdn-images-1.medium.com%2Fmax%2F2000%2F0%2A_WAYziHSn5W5lg4S.png"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;3.Lastly, install the Playwright module from the Python package index PyPi using the command &lt;em&gt;pip install playwright&lt;/em&gt;.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fcdn-images-1.medium.com%2Fmax%2F2000%2F0%2A51fVZguWmJYKvX7O.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fcdn-images-1.medium.com%2Fmax%2F2000%2F0%2A51fVZguWmJYKvX7O.png"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Downloading Browsers for Playwright
&lt;/h2&gt;

&lt;p&gt;After completing the installation of the Playwright Python package, we need to download &amp;amp; install browser binaries for Playwright to work with.&lt;/p&gt;

&lt;p&gt;By default, Playwright will download binaries for Chromium, WebKit &amp;amp; Firefox from Microsoft CDN, but this behavior is configurable. The available configurations are as below.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Skip Downloads of Browser Binaries&lt;/strong&gt;&lt;br&gt;
Skip the download of binaries altogether in case testing needs to be performed on a cloud-based grid rather than locally. In such cases, we may not need the browsers downloaded on local systems. This can be done by setting the environment variable &lt;em&gt;PLAYWRIGHT_SKIP_BROWSER_DOWNLOAD&lt;/em&gt;.&lt;/p&gt;

&lt;p&gt;PLAYWRIGHT_SKIP_BROWSER_DOWNLOAD=1 python -m playwright install&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Downloads of Browser Binaries from Different Repositories&lt;/strong&gt;&lt;br&gt;
As a good practice and for security reasons, organizations may not allow connections to Microsoft CDN repositories, which Playwright downloads the browsers from. In such cases, the environment variable PLAYWRIGHT_DOWNLOAD_HOST can be set to supply a link to a different repository.&lt;/p&gt;

&lt;p&gt;PLAYWRIGHT_DOWNLOAD_HOST= playwright install&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Downloads of Browser Binaries Via Proxy&lt;/strong&gt;&lt;br&gt;
Most organizations route requests to the internet via a Proxy for better security. In such cases, Playwright browser download can be done by setting the HTTPS_PROXY environment variable before installation.&lt;/p&gt;

&lt;p&gt;HTTPS_PROXY= playwright install&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Downloads of Single Browser Binary&lt;/strong&gt;&lt;br&gt;
Download only a single browser binary depending on your needs is also possible, in which case the install commands needed should be supplied with the browser name, for example, &lt;em&gt;playwright install firefox&lt;/em&gt;&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;To keep things simple we install all browsers by using the command playwright install. This step can be skipped entirely if you run your code on cloud Playwright Grid, but we will look at both scenarios, i.e., using Playwright for web scraping locally and on a &lt;a href="https://www.lambdatest.com/?utm_source=devto&amp;amp;utm_medium=organic&amp;amp;utm_campaign=nov_27&amp;amp;utm_term=bw&amp;amp;utm_content=webpage" rel="noopener noreferrer"&gt;cloud Playwright Grid&lt;/a&gt; provided by LambdaTest.&lt;/p&gt;

&lt;h2&gt;
  
  
  Setup Playwright Project in Visual Studio Code
&lt;/h2&gt;

&lt;p&gt;If you have Visual Studio Code(VS Code) already installed the easiest way to start it up is to navigate to the newly created project folder and type &lt;strong&gt;code&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fcdn-images-1.medium.com%2Fmax%2F2000%2F0%2AS2DrmJ5d6AGAEMFa.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fcdn-images-1.medium.com%2Fmax%2F2000%2F0%2AS2DrmJ5d6AGAEMFa.png"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;This is how the VS Code will look when it opens. We now need to select the interpreter, i.e., the Python installation, from our newly created virtual environment.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fcdn-images-1.medium.com%2Fmax%2F2266%2F0%2AaHxnr6_0OTfMi8qP.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fcdn-images-1.medium.com%2Fmax%2F2266%2F0%2AaHxnr6_0OTfMi8qP.png"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Press Ctrl + Shift + P to open up the command palette in VS Code and type Python: Select Interpreter and click it.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fcdn-images-1.medium.com%2Fmax%2F2000%2F0%2ARBIP72mCV0i9kTVb.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fcdn-images-1.medium.com%2Fmax%2F2000%2F0%2ARBIP72mCV0i9kTVb.png"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;VS Code will automatically detect the virtual environment we created earlier and recommend it at the top, select it. If VS Code does not auto-detect, click on the ‘Enter interpreter path..’ and navigate to playwrightplayground &amp;gt; Scripts &amp;gt; Python and select it.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fcdn-images-1.medium.com%2Fmax%2F2000%2F0%2A9EAenFeT3NQXNjKN.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fcdn-images-1.medium.com%2Fmax%2F2000%2F0%2A9EAenFeT3NQXNjKN.png"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;You can verify the virtual environment by pressing the ctrl + (tilt), which will open up the VS Code terminal &amp;amp; activate the virtual environment. An active environment is indicated by its name before the path within round brackets.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fcdn-images-1.medium.com%2Fmax%2F2000%2F0%2AHWAm9lcy1OJ2NBUH.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fcdn-images-1.medium.com%2Fmax%2F2000%2F0%2AHWAm9lcy1OJ2NBUH.png"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;With the setup and installation out of the way, we are now ready to use Playwright for web scraping with Python.&lt;/p&gt;

&lt;h2&gt;
  
  
  Demonstration: Using Playwright for Web Scraping with Python
&lt;/h2&gt;

&lt;p&gt;For demonstration, I will be scraping information from two different websites. I will be using the XPath locator in the second scenario. This will help you in choosing the best-suited locator for automating the tests.&lt;/p&gt;

&lt;p&gt;So, let’s get started…&lt;/p&gt;

&lt;h2&gt;
  
  
  Test Scenario 1: Web Scraping E-Commerce Website
&lt;/h2&gt;

&lt;p&gt;In the &lt;strong&gt;first demo&lt;/strong&gt;, we are going to scrape a demo E-Commerce website provided by LambdaTest for the following data:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;Product Name&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Product Price&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Product Image URL&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fcdn-images-1.medium.com%2Fmax%2F2878%2F0%2AT0VsJfZYnRp70AbS.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fcdn-images-1.medium.com%2Fmax%2F2878%2F0%2AT0VsJfZYnRp70AbS.png"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Here is the Playwright for web scraping scenario that will be executed on Chrome on Windows 10 using Playwright Version 1.28.0. We will run the &lt;a href="https://www.lambdatest.com/playwright-testing?utm_source=devto&amp;amp;utm_medium=organic&amp;amp;utm_campaign=nov_27&amp;amp;utm_term=bw&amp;amp;utm_content=webpage" rel="noopener noreferrer"&gt;Playwright test&lt;/a&gt; on &lt;a href="https://www.lambdatest.com/blog/benefits-of-cloud-testing/?utm_source=devto&amp;amp;utm_medium=organic&amp;amp;utm_campaign=nov_27&amp;amp;utm_term=bw&amp;amp;utm_content=blog" rel="noopener noreferrer"&gt;cloud testing&lt;/a&gt; platforms like LambdaTest.&lt;/p&gt;

&lt;p&gt;By utilizing LambdaTest, you can significantly reduce the time required to run your Playwright Python tests by leveraging an &lt;a href="https://www.lambdatest.com/online-browser-farm?utm_source=devto&amp;amp;utm_medium=organic&amp;amp;utm_campaign=nov_27&amp;amp;utm_term=bw&amp;amp;utm_content=webpage" rel="noopener noreferrer"&gt;online browser farm&lt;/a&gt; that includes more than 50 different browser versions, including Chrome, Chromium, Microsoft Edge, Mozilla Firefox, and Webkit.&lt;br&gt;
&lt;iframe width="710" height="399" src="https://www.youtube.com/embed/ioJaNCYX7Qc"&gt;
&lt;/iframe&gt;
&lt;br&gt;
You can also subscribe to the &lt;a href="https://www.youtube.com/channel/UCCymWVaTozpEng_ep0mdUyw?sub_confirmation=1" rel="noopener noreferrer"&gt;LambdaTest YouTube Channel&lt;/a&gt; and stay updated with the latest tutorial around &lt;a href="https://www.lambdatest.com/blog/playwright-framework?utm_source=devto&amp;amp;utm_medium=organic&amp;amp;utm_campaign=nov_27&amp;amp;utm_term=bw&amp;amp;utm_content=blog" rel="noopener noreferrer"&gt;Playwright browser testing&lt;/a&gt;, &lt;a href="https://www.lambdatest.com/cypress-e2e-testing?utm_source=devto&amp;amp;utm_medium=organic&amp;amp;utm_campaign=nov_27&amp;amp;utm_term=bw&amp;amp;utm_content=webpage" rel="noopener noreferrer"&gt;Cypress E2E testing&lt;/a&gt;, &lt;a href="https://www.lambdatest.com/learning-hub/mobile-app-testing?utm_source=devto&amp;amp;utm_medium=organic&amp;amp;utm_campaign=nov_27&amp;amp;utm_term=bw&amp;amp;utm_content=learning_hub" rel="noopener noreferrer"&gt;Mobile App Testing&lt;/a&gt;, and more.&lt;/p&gt;

&lt;p&gt;However, the core logic remains unchanged even if the web scraping has to be done using a local machine/grid.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Test Scenario:&lt;/strong&gt;&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;&lt;p&gt;Go to &lt;a href="https://ecommerce-playground.lambdatest.io/" rel="noopener noreferrer"&gt;https://ecommerce-playground.lambdatest.io/&lt;/a&gt;.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Click on ‘Shop by Category’ &amp;amp; select ‘Software’.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Adjust the ‘Show’ filter to display 75 results.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Scrape the product name, price &amp;amp; image URL.&lt;/p&gt;&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;&lt;strong&gt;Version Check:&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;When writing this blog on using Playwright for web scraping, the version of Playwright is 1.28.0, and the version of Python is 3.9.12. The code is fully tested and working on these versions.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Implementation:&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;You can clone the repo by clicking on the button below.&lt;br&gt;
&lt;a href="https://github.com/jaydeepkarale/python_playwright_webscraping" rel="noopener noreferrer"&gt;&lt;strong&gt;GitHub - jaydeepkarale/python_playwright_webscraping&lt;/strong&gt;&lt;/a&gt;&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;import json
import logging
import os
import subprocess
import sys
import time
import urllib
from logging import getLogger

from dotenv import load_dotenv
from playwright.sync_api import sync_playwright

# setup basic logging for our project which will display the time, log level &amp;amp; log message
logger = getLogger("webscapper.py")
logging.basicConfig(
    stream=sys.stdout,  # uncomment this line to redirect output to console
    format="%(message)s",
    level=logging.DEBUG,
)

# LambdaTest username &amp;amp; access key are stored in an env file &amp;amp; we fetch it from there using python dotenv module
load_dotenv("sample.env")

capabilities = {
    "browserName": "Chrome",  # Browsers allowed: `Chrome`, `MicrosoftEdge`, `pw-chromium`, `pw-firefox` and `pw-webkit`
    "browserVersion": "latest",
    "LT:Options": {
        "platform": "Windows 10",
        "build": "E Commerce Scrape Build",
        "name": "Scrape Lambda Software Product",
        "user": os.getenv("LT_USERNAME"),
        "accessKey": os.getenv("LT_ACCESS_KEY"),
        "network": False,
        "video": True,
        "console": True,
        "tunnel": False,  # Add tunnel configuration if testing locally hosted webpage
        "tunnelName": "",  # Optional
        "geoLocation": "",  # country code can be fetched from https://www.lambdatest.com/capabilities-generator/
    },
}


def main():
    with sync_playwright() as playwright:
        playwright_version = (
            str(subprocess.getoutput("playwright --version")).strip().split(" ")[1]
        )
        capabilities["LT:Options"]["playwrightClientVersion"] = playwright_version
        lt_cdp_url = (
            "wss://cdp.lambdatest.com/playwright?capabilities="
            + urllib.parse.quote(json.dumps(capabilities))
        )
        logger.info(f"Initiating connection to cloud playwright grid")
        browser = playwright.chromium.connect(lt_cdp_url)        
        # comment above line &amp;amp; uncomment below line to test on local grid
        # browser = playwright.chromium.launch(headless=False)
        page = browser.new_page()
        try:            
            # section to navigate to software category  
            page.goto("https://ecommerce-playground.lambdatest.io/")
            page.get_by_role("button", name="Shop by Category").click()
            page.get_by_role("link", name="Software").click()
            page_to_be_scrapped = page.get_by_role(
                "combobox", name="Show:"
            ).select_option(
                "https://ecommerce-playground.lambdatest.io/index.php?route=product/category&amp;amp;path=17&amp;amp;limit=75"
            )
            page.goto(page_to_be_scrapped[0])

            # Since image are lazy-loaded scroll to bottom of page
            # the range is dynamically decided based on the number of items i.e. we take the range from limit
            # https://ecommerce-playground.lambdatest.io/index.php?route=product/category&amp;amp;path=17&amp;amp;limit=75
            for i in range(int(page_to_be_scrapped[0].split("=")[-1])):
                page.mouse.wheel(0, 300)
                i += 1
                time.sleep(0.1)
            # Construct locators to identify name, price &amp;amp; image
            base_product_row_locator = page.locator("#entry_212408").locator(".row").locator(".product-grid")
            product_name = base_product_row_locator.get_by_role("heading")
            product_price = base_product_row_locator.locator(".price-new")
            product_image = (
                base_product_row_locator.locator(".carousel-inner")
                .locator(".active")
                .get_by_role("img")
            )            
            total_products = base_product_row_locator.count()
            for product in range(total_products):
                logger.info(
                    f"\n**** PRODUCT {product+1} ****\n"
                    f"Product Name = {product_name.nth(product).all_inner_texts()[0]}\n"
                    f"Price = {product_price.nth(product).all_inner_texts()[0]}\n"
                    f"Image = {product_image.nth(product).get_attribute('src')}\n"
                )
            status = 'status'
            remark = 'Scraping Completed'
            page.evaluate("_ =&amp;gt; {}","lambdatest_action: {\"action\": \"setTestStatus\", \"arguments\": {\"status\":\"" + status + "\", \"remark\": \"" + remark + "\"}}")            
        except Exception as ex:
            logger.error(str(ex))


if __name__ == "__main__":
    main()
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;&lt;strong&gt;Code Walkthrough:&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Let’s now do a step-by-step walkthrough to understand the code.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Step 1 — Setting up imports&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;The most noteworthy imports are&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;1 from dotenv import load_dotenv
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;The reason for using the load_dotenv library is that it reads key-value pairs from a .env file(in our case sample.env) and can set them as environment variables automatically. In our case, we use it to read the access key &amp;amp; username from a sample.env required to access the cloud-based Playwright Grid.&lt;/p&gt;

&lt;p&gt;It saves the trouble of setting environment variables manually &amp;amp; hence the same code can seamlessly be tested on different environments without any manual intervention.&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;1 from playwright.sync_api import sync_playwright
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;Playwright provides both sync &amp;amp; async API to interact with web apps, but for this blog on using Playwright for web scraping, we are going to use the sync_api, which is simply a wrapper around the asyncio_api that abstracts away the need to implement async functionality.&lt;/p&gt;

&lt;p&gt;For more complicated scenarios where there is a need for fine-grained control when dealing with specific scenarios on websites built using modern web frameworks, we can choose to use the async_api.&lt;/p&gt;

&lt;p&gt;For most use cases, the sync_api should suffice, but it’s a bonus that the async_api does exist and can be leveraged when needed.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fcdn-images-1.medium.com%2Fmax%2F3200%2F0%2Ae_GjJGhBJwhUzlGN.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fcdn-images-1.medium.com%2Fmax%2F3200%2F0%2Ae_GjJGhBJwhUzlGN.png"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Step 2 — Setting up logging &amp;amp; reading username &amp;amp; access key&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;In the next step, we set up logging to see the execution of our code &amp;amp; also print out the product name, price &amp;amp; link to image. Logging is the recommended practice and should be preferred to print() statements almost always.&lt;br&gt;
The load_dotenv(“sample.env”) reads the username &amp;amp; access key required to access our cloud-based Playwright grid. The username and access key are available on the &lt;a href="https://accounts.lambdatest.com/detail/profile?_gl=1*19nkkj5*_gcl_au*MTM0NjExNTgyLjE3MDEwOTM2OTI.?utm_source=devto&amp;amp;utm_medium=organic&amp;amp;utm_campaign=nov_27&amp;amp;utm_term=bw&amp;amp;utm_content=accounts" rel="noopener noreferrer"&gt;LambdaTest Profile Page&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fcdn-images-1.medium.com%2Fmax%2F3200%2F0%2AjYwpyVcmzy3zvBXf.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fcdn-images-1.medium.com%2Fmax%2F3200%2F0%2AjYwpyVcmzy3zvBXf.png"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fcdn-images-1.medium.com%2Fmax%2F2080%2F0%2ACFPSX-kxFH8wrYfO.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fcdn-images-1.medium.com%2Fmax%2F2080%2F0%2ACFPSX-kxFH8wrYfO.png"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Step 3 — Setting up desired browser capabilities&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;We set up the browser capabilities required by the cloud-based &lt;a href="https://www.lambdatest.com/blog/playwright-framework/?utm_source=devto&amp;amp;utm_medium=organic&amp;amp;utm_campaign=nov_27&amp;amp;utm_term=bw&amp;amp;utm_content=blog" rel="noopener noreferrer"&gt;Playwright automated testing&lt;/a&gt; grid in a Python dictionary. Let us understand what each line of this configuration means.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fcdn-images-1.medium.com%2Fmax%2F3200%2F0%2AHa2XUn1S_aoitEdU.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fcdn-images-1.medium.com%2Fmax%2F3200%2F0%2AHa2XUn1S_aoitEdU.png"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;browserName&lt;/strong&gt; is used to specify the browser the test/code should run on. All modern browsers, such as Chrome, Firefox, Microsoft Edge &amp;amp; WebKit.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;browserVersion&lt;/strong&gt; is used to specify the version of the browser.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;platform&lt;/strong&gt; is used to specify the operating system the test should run on such as Windows, macOS, or Linux.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;build&lt;/strong&gt; is used to specify the name under which the tests will be grouped.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;name&lt;/strong&gt; is used to specify the name of the test.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;user &amp;amp; access key&lt;/strong&gt; are our credentials to connect to the cloud Playwright grid.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;network&lt;/strong&gt; is used to specify whether the cloud Playwright grid should capture network logs.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;video&lt;/strong&gt; is used to specify if the cloud Playwright grid should capture video of the entire test execution.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;tunnel &amp;amp; tunnelName&lt;/strong&gt; are used to specify tunnel details in case the website we use isn’t publicly hosted over the internet.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Step 4 — Initializing browser context &amp;amp; connecting to cloud Playwright Grid&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fcdn-images-1.medium.com%2Fmax%2F3200%2F0%2ANG779G_UMyHt2pOK.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fcdn-images-1.medium.com%2Fmax%2F3200%2F0%2ANG779G_UMyHt2pOK.png"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;&lt;p&gt;Create a &lt;strong&gt;&lt;em&gt;ContextManager&lt;/em&gt;&lt;/strong&gt; for the &lt;strong&gt;&lt;em&gt;sync_playwright()&lt;/em&gt;&lt;/strong&gt; function provided by Playwright.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Use Python’s built-in &lt;strong&gt;&lt;em&gt;subprocess&lt;/em&gt;&lt;/strong&gt; module to run the &lt;strong&gt;&lt;em&gt;playwright –version&lt;/em&gt;&lt;/strong&gt; command to extract the Playwright version.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Add playwright_version to the config dictionary of our cloud Playwright grid.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Prepare the WebSocket URL for our cloud Playwright grid so that we can leverage their infrastructure to run the script.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Use the standard &lt;strong&gt;&lt;em&gt;connect&lt;/em&gt;&lt;/strong&gt; method, which uses the Playwright’s built-in browser server to handle the connection. This is a faster and more fully-featured method since it supports most Playwright parameters (such as using a proxy if working behind a firewall).&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Create a page in the browser context.&lt;/p&gt;&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;&lt;strong&gt;Step 5 — Open the website to scrape.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Open the website to scrape using the page context.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fcdn-images-1.medium.com%2Fmax%2F2328%2F0%2AFKGONXMcov6Dfv48.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fcdn-images-1.medium.com%2Fmax%2F2328%2F0%2AFKGONXMcov6Dfv48.png"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Step 6 — Click on ‘Shop by Category’.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;&lt;em&gt;‘Shop By Category’&lt;/em&gt;&lt;/strong&gt; is a link with the &lt;strong&gt;&lt;em&gt;‘button’&lt;/em&gt;&lt;/strong&gt; role assigned to it. Hence, we use Playwright‘s &lt;strong&gt;&lt;em&gt;get_by_role()&lt;/em&gt;&lt;/strong&gt; locator to navigate to it &amp;amp; perform the &lt;strong&gt;&lt;em&gt;click()&lt;/em&gt;&lt;/strong&gt; action.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fcdn-images-1.medium.com%2Fmax%2F2000%2F0%2AL98pjxVGR9HYBAdV.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fcdn-images-1.medium.com%2Fmax%2F2000%2F0%2AL98pjxVGR9HYBAdV.png"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fcdn-images-1.medium.com%2Fmax%2F2436%2F0%2Av1e2CfCfDeTtsAza.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fcdn-images-1.medium.com%2Fmax%2F2436%2F0%2Av1e2CfCfDeTtsAza.png"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Step 7 — Click on the ‘Software’.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Inspection of the &lt;strong&gt;&lt;em&gt;‘Software’&lt;/em&gt;&lt;/strong&gt; element shows that it’s a &lt;strong&gt;&lt;em&gt;‘link’&lt;/em&gt;&lt;/strong&gt; with name in a span. So, we use the built-in locator &lt;strong&gt;&lt;em&gt;get_by_role()&lt;/em&gt;&lt;/strong&gt; again and perform &lt;strong&gt;&lt;em&gt;click()&lt;/em&gt;&lt;/strong&gt; action.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fcdn-images-1.medium.com%2Fmax%2F2312%2F0%2AL8sjTizP8RXL4c_8.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fcdn-images-1.medium.com%2Fmax%2F2312%2F0%2AL8sjTizP8RXL4c_8.png"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fcdn-images-1.medium.com%2Fmax%2F2080%2F0%2AdbqoghVdh_dIzN_E.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fcdn-images-1.medium.com%2Fmax%2F2080%2F0%2AdbqoghVdh_dIzN_E.png"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Step 8 — Adjust the product drop down to get more products.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;By default, the &lt;strong&gt;&lt;em&gt;‘Software’&lt;/em&gt;&lt;/strong&gt; page displays 15 items, we can easily change it to 75, which is nothing but a link to the same page with a different limit. Get that link and call the &lt;strong&gt;&lt;em&gt;page.goto()&lt;/em&gt;&lt;/strong&gt; method.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fcdn-images-1.medium.com%2Fmax%2F2442%2F0%2AbvjXsbjBvw24ZQAE.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fcdn-images-1.medium.com%2Fmax%2F2442%2F0%2AbvjXsbjBvw24ZQAE.png"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fcdn-images-1.medium.com%2Fmax%2F3200%2F0%2AuRBuE8T7483A-IyZ.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fcdn-images-1.medium.com%2Fmax%2F3200%2F0%2AuRBuE8T7483A-IyZ.png"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Step 9 — Loading Images.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;The images on the website are &lt;strong&gt;lazy-loaded,&lt;/strong&gt; i.e., only loaded as we bring them into focus or simply scroll down to them. To learn more about it, you can go through this blog on a &lt;a href="https://www.lambdatest.com/blog/how-to-lazy-load-images-javascript/?utm_source=devto&amp;amp;utm_medium=organic&amp;amp;utm_campaign=nov_27&amp;amp;utm_term=bw&amp;amp;utm_content=blog" rel="noopener noreferrer"&gt;complete guide to lazy load images&lt;/a&gt;.&lt;br&gt;
We use Playwright’s mouse wheel function to simulate a mouse scroll.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Step 10 — Preparing base locator.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Notice that all the products are contained within two divs with &lt;strong&gt;&lt;em&gt;id=entry_212408&lt;/em&gt;&lt;/strong&gt; and &lt;strong&gt;&lt;em&gt;class=row&lt;/em&gt;&lt;/strong&gt;. Each product then has a &lt;strong&gt;&lt;em&gt;class=product-grid&lt;/em&gt;&lt;/strong&gt;. We use this knowledge to form our base locator.&lt;/p&gt;

&lt;p&gt;The base locator will then be used to find elements, such as name, price &amp;amp; image.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fcdn-images-1.medium.com%2Fmax%2F2698%2F0%2AevDC5fGNUBRMiJ6g.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fcdn-images-1.medium.com%2Fmax%2F2698%2F0%2AevDC5fGNUBRMiJ6g.png"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fcdn-images-1.medium.com%2Fmax%2F2908%2F0%2An0pR2gzImuhrDlsG.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fcdn-images-1.medium.com%2Fmax%2F2908%2F0%2An0pR2gzImuhrDlsG.png"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Step 11 — Locating product name, price &amp;amp; image.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;With respect to the base locator, the location of other elements becomes easy to capture.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;Product Name is contained within an &lt;strong&gt;&lt;em&gt;h4&lt;/em&gt;&lt;/strong&gt; tag.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Product Price is contained in a div with &lt;strong&gt;&lt;em&gt;class=price-new&lt;/em&gt;&lt;/strong&gt;.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Image is present in &lt;strong&gt;&lt;em&gt;class=carousel-inner active&lt;/em&gt;&lt;/strong&gt;.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fcdn-images-1.medium.com%2Fmax%2F2150%2F0%2AgF8za9cIpP97xo1r.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fcdn-images-1.medium.com%2Fmax%2F2150%2F0%2AgF8za9cIpP97xo1r.png"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fcdn-images-1.medium.com%2Fmax%2F2544%2F0%2APbOMjvXBQ0Hk4ONh.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fcdn-images-1.medium.com%2Fmax%2F2544%2F0%2APbOMjvXBQ0Hk4ONh.png"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Step 12 — Scraping the data.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Now that we have all the elements located, we simply iterate over the total products &amp;amp; scrape them one by one using the &lt;strong&gt;&lt;em&gt;nth()&lt;/em&gt;&lt;/strong&gt; method.&lt;/p&gt;

&lt;p&gt;The total number of products is obtained by calling the &lt;strong&gt;&lt;em&gt;count()&lt;/em&gt;&lt;/strong&gt; method on &lt;strong&gt;&lt;em&gt;base_product_row_locator&lt;/em&gt;&lt;/strong&gt;. For product name &amp;amp; price, we fetch text using the &lt;strong&gt;&lt;em&gt;all_inner_texts()&lt;/em&gt;&lt;/strong&gt;, and image URL is retrieved using the &lt;strong&gt;&lt;em&gt;get_attribute(‘src’)&lt;/em&gt;&lt;/strong&gt; element handle.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fcdn-images-1.medium.com%2Fmax%2F2908%2F0%2A3nWogl0ZurgwYpoa.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fcdn-images-1.medium.com%2Fmax%2F2908%2F0%2A3nWogl0ZurgwYpoa.png"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Execution:&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Et voilà!&lt;/em&gt; Here is the truncated execution snapshot from the VS Code, which shows data of the first 5 products.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fcdn-images-1.medium.com%2Fmax%2F2208%2F0%2AFOFIKdJ0C2rjbJde.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fcdn-images-1.medium.com%2Fmax%2F2208%2F0%2AFOFIKdJ0C2rjbJde.png"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Here is a snapshot from the &lt;a href="https://accounts.lambdatest.com/dashboard/?_gl=1*hku5hb*_gcl_au*MTM0NjExNTgyLjE3MDEwOTM2OTI.?utm_source=devto&amp;amp;utm_medium=organic&amp;amp;utm_campaign=nov_27&amp;amp;utm_term=bw&amp;amp;utm_content=accounts" rel="noopener noreferrer"&gt;LambdaTest dashboard&lt;/a&gt;, which shows all the detailed execution, video capture &amp;amp; logs of the entire process.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fcdn-images-1.medium.com%2Fmax%2F3200%2F0%2AFyzpezeoyIUxnZGp.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fcdn-images-1.medium.com%2Fmax%2F3200%2F0%2AFyzpezeoyIUxnZGp.png"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fcdn-images-1.medium.com%2Fmax%2F2000%2F0%2A0rBSi6rkWVebaqJO.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fcdn-images-1.medium.com%2Fmax%2F2000%2F0%2A0rBSi6rkWVebaqJO.png"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Test Scenario 2: Web Scraping Selenium Playground
&lt;/h2&gt;

&lt;p&gt;Let’s take another example where I will be scraping information from the LambdaTest Selenium Playground.&lt;br&gt;
In this demonstration, we scrape the following data from the &lt;a href="https://lambdatest.com/selenium-playground?utm_source=devto&amp;amp;utm_medium=organic&amp;amp;utm_campaign=nov_27&amp;amp;utm_term=bw&amp;amp;utm_content=webpage" rel="noopener noreferrer"&gt;LambdaTest Selenium Playground&lt;/a&gt;:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;Heading of section&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Name of individual demo&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Link to individual demo&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fcdn-images-1.medium.com%2Fmax%2F2968%2F0%2AYU40ebh-Ih0YCpaM.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fcdn-images-1.medium.com%2Fmax%2F2968%2F0%2AYU40ebh-Ih0YCpaM.png"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;One important change we are going to make in this demo to show the versatility of Playwright for web scraping using Python is&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Use of ‘XPath’ in combination with locators to find elements.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Here is the scenario for using Playwright for web scraping, which will be executed on Chrome on Windows 10 using Playwright Version 1.28.0.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Test Scenario:&lt;/strong&gt;&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;&lt;p&gt;Go to ‘&lt;a href="https://www.lambdatest.com/selenium-playground/?utm_source=devto&amp;amp;utm_medium=organic&amp;amp;utm_campaign=nov_27&amp;amp;utm_term=bw&amp;amp;utm_content=webpage" rel="noopener noreferrer"&gt;https://www.lambdatest.com/selenium-playground/&lt;/a&gt;’&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Scrape the product heading, demo name &amp;amp; link of demo.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Print the scraped data.&lt;/p&gt;&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;&lt;strong&gt;Version Check:&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;At the time of writing this blog on using Playwright for web scraping, the version of Playwright is 1.28.0, and the version of Python is 3.9.12.&lt;/p&gt;

&lt;p&gt;The code is fully tested and working on these versions.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Implementation:&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Clone the Playwright Python WebScraping Demo GitHub repository to follow the steps mentioned further in the blog on using Playwright for web scraping&lt;br&gt;
&lt;a href="https://github.com/jaydeepkarale/python_playwright_webscraping/" rel="noopener noreferrer"&gt;&lt;strong&gt;GitHub - jaydeepkarale/python_playwright_webscraping&lt;/strong&gt;&lt;/a&gt;&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;import json
import logging
import os
import subprocess
import sys
import time
import urllib
from logging import getLogger

from dotenv import load_dotenv
from playwright.sync_api import sync_playwright

logger = getLogger("seleniumplaygroundscrapper.py")
logging.basicConfig(
    stream=sys.stdout,
    format="%(message)s",
    level=logging.DEBUG,
)

# Read LambdaTest username &amp;amp; access key from env file
load_dotenv("sample.env")

capabilities = {
    "browserName": "Chrome",
    "browserVersion": "latest",
    "LT:Options": {
        "platform": "Windows 10",
        "build": "Selenium Playground Scraping",
        "name": "Scrape LambdaTest Selenium Playground",
        "user": os.getenv("LT_USERNAME"),
        "accessKey": os.getenv("LT_ACCESS_KEY"),
        "network": False,
        "video": True,
        "console": True,
        "tunnel": False,  
        "tunnelName": "",  
        "geoLocation": "",
    },
}


def main():
    with sync_playwright() as playwright:
        playwright_version = (
            str(subprocess.getoutput("playwright --version")).strip().split(" ")[1]
        )
        capabilities["LT:Options"]["playwrightClientVersion"] = playwright_version
        lt_cdp_url = (
            "wss://cdp.lambdatest.com/playwright?capabilities="
            + urllib.parse.quote(json.dumps(capabilities))
        )
        logger.info(f"Initiating connection to cloud playwright grid")
        browser = playwright.chromium.connect(lt_cdp_url)
        # comment above line &amp;amp; uncomment below line to test on local grid
        # browser = playwright.chromium.launch()
        page = browser.new_page()
        try:                        
            page.goto("https://www.lambdatest.com/selenium-playground/")
            # Construct base locator section
            base_container_locator = page.locator("//*[@id='__next']/div/section[2]/div/div/div")
            for item in range(1, base_container_locator.count()+1):                
                # Find section, demo name &amp;amp; demo link with respect to base locator &amp;amp; print them
                locator_row = base_container_locator.locator(f"//div[{item}]")                            
                for inner_item in range(0, locator_row.count()):
                    logger.info(f"*-*-"*28)
                    logger.info(f'Section: {locator_row.nth(inner_item).locator("//h2").all_inner_texts()[0]}\n')
                    for list_item in range(0,locator_row.nth(inner_item).locator("//ul/li").count()):
                        logger.info(f'Demo Name: {locator_row.nth(inner_item).locator("//ul/li").nth(list_item).all_inner_texts()[0]}')
                        logger.info(f'Demo Link: {locator_row.nth(inner_item).locator("//ul/li/a").nth(list_item).get_attribute("href")}\n')
            status = 'status'
            remark = 'Scraping Completed'
            page.evaluate("_ =&amp;gt; {}","lambdatest_action: {\"action\": \"setTestStatus\", \"arguments\": {\"status\":\"" + status + "\", \"remark\": \"" + remark + "\"}}")

        except Exception as ex:
            logger.error(str(ex))        




if __name__ == "__main__":
    main()
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;&lt;strong&gt;Code Walkthrough:&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Let’s now do a step-by-step walkthrough to understand the code.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Step 1 — Step 4&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Include the imports, logging, cloud Playwright testing grid &amp;amp; browser context setup that remain the same as the previous demonstration; hence, refer to them above.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Step 5 — Open the LambdaTest Selenium Playground Website&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Use the page created in the browser context to open the LambdaTest Selenium Playground website, which we will scrape.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fcdn-images-1.medium.com%2Fmax%2F2472%2F0%2A08f8a9TqTAAgDipk.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fcdn-images-1.medium.com%2Fmax%2F2472%2F0%2A08f8a9TqTAAgDipk.png"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Step 6 — Construct the base locator&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Inspecting the website in Chrome Developer Tools, we see that there is a master container that holds all the sub-blocks. We will use this to construct our base locator by copying the XPath.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fcdn-images-1.medium.com%2Fmax%2F3138%2F0%2Al6aWcPMxqBUD0gNy.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fcdn-images-1.medium.com%2Fmax%2F3138%2F0%2Al6aWcPMxqBUD0gNy.png"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fcdn-images-1.medium.com%2Fmax%2F3200%2F0%2Aif_WUaJo7oi60lR4.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fcdn-images-1.medium.com%2Fmax%2F3200%2F0%2Aif_WUaJo7oi60lR4.png"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Step 7 — Locate the section, demo name, and demo link&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;We repeat the inspection using Chrome Developer Tools, and it’s easy to spot that data is presented in 3 divs. Within each div, there are then separate sections for each item.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fcdn-images-1.medium.com%2Fmax%2F2682%2F0%2AycXjhbXCRHSIx-GG.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fcdn-images-1.medium.com%2Fmax%2F2682%2F0%2AycXjhbXCRHSIx-GG.png"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fcdn-images-1.medium.com%2Fmax%2F2752%2F0%2A9V1FehPxDHofmaoF.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fcdn-images-1.medium.com%2Fmax%2F2752%2F0%2A9V1FehPxDHofmaoF.png"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Based on the inspection, we iterate over the base locator and for each div&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;get the heading using its relative XPath &lt;strong&gt;//h2&lt;/strong&gt;.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;get the text from the link using Xpath &lt;strong&gt;//ul/li&lt;/strong&gt; and &lt;strong&gt;all_inner_texts()&lt;/strong&gt; method.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;get the link url using Xpath /&lt;strong&gt;/ul/li/a&lt;/strong&gt; and &lt;strong&gt;get_attribute(‘href’)&lt;/strong&gt; element handle.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The outermost for loop iterates over the outer container, the inner for loop gets the section title &amp;amp; the innermost for loop is for iterating over the list.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fcdn-images-1.medium.com%2Fmax%2F2000%2F0%2ATlhU1QmJbgphyZuR.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fcdn-images-1.medium.com%2Fmax%2F2000%2F0%2ATlhU1QmJbgphyZuR.png"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Execution:&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Hey presto !!!&lt;/strong&gt; Here is the truncated execution snapshot from the VS Code. We now have a collection of tutorials to visit whenever we wish to.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fcdn-images-1.medium.com%2Fmax%2F2180%2F0%2ANgUBBuLifrK6Cq3m.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fcdn-images-1.medium.com%2Fmax%2F2180%2F0%2ANgUBBuLifrK6Cq3m.png"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Here is a snapshot from the LambdaTest dashboard, which shows all the detailed execution along with video capture &amp;amp; logs of the entire process.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fcdn-images-1.medium.com%2Fmax%2F3200%2F0%2Ae1Yjl172FlecQ8Vy.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fcdn-images-1.medium.com%2Fmax%2F3200%2F0%2Ae1Yjl172FlecQ8Vy.png"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fcdn-images-1.medium.com%2Fmax%2F3200%2F0%2A75N41nEkHErm57Qx.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fcdn-images-1.medium.com%2Fmax%2F3200%2F0%2A75N41nEkHErm57Qx.png"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The Playwright 101 certification by LambdaTest is designed for developers who want to demonstrate their expertise in using Playwright for end-to-end testing of modern web applications. It is the ideal way to showcase your skills as a &lt;a href="https://www.lambdatest.com/blog/playwright-framework?utm_source=devto&amp;amp;utm_medium=organic&amp;amp;utm_campaign=nov_27&amp;amp;utm_term=bw&amp;amp;utm_content=blog" rel="noopener noreferrer"&gt;Playwright automation&lt;/a&gt; tester.&lt;/p&gt;

&lt;h2&gt;
  
  
  Conclusion
&lt;/h2&gt;

&lt;p&gt;Python Playwright is a new but powerful tool for web scraping that allows developers to easily automate and control the behavior of web browsers. Its wide range of capabilities &amp;amp; ability to support different browsers, operating systems, and languages makes it a compelling choice for any browser related task.&lt;/p&gt;

&lt;p&gt;In this blog on using Playwright for web scraping, we learned in detail how to set up Python and use it with Playwright for web scraping using its powerful built-in locators using both XPath &amp;amp; CSS Selectors.&lt;/p&gt;

&lt;p&gt;It is well worth considering Python with Playwright for web scraping in your next scraping project, as it is a valuable addition to the web scraping toolkit.&lt;/p&gt;

</description>
      <category>python</category>
      <category>playwright</category>
      <category>webdev</category>
      <category>programming</category>
    </item>
  </channel>
</rss>
