<?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>Forem: himanshuseth004</title>
    <description>The latest articles on Forem by himanshuseth004 (@himanshusheth004).</description>
    <link>https://forem.com/himanshusheth004</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%2F134342%2F40a30609-797b-4b72-b21a-d776df2fba14.png</url>
      <title>Forem: himanshuseth004</title>
      <link>https://forem.com/himanshusheth004</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://forem.com/feed/himanshusheth004"/>
    <language>en</language>
    <item>
      <title>How to Use Selenium and Cypress for Canvas Automation</title>
      <dc:creator>himanshuseth004</dc:creator>
      <pubDate>Tue, 04 Feb 2025 09:39:01 +0000</pubDate>
      <link>https://forem.com/himanshusheth004/how-to-use-selenium-and-cypress-for-canvas-automation-1g8p</link>
      <guid>https://forem.com/himanshusheth004/how-to-use-selenium-and-cypress-for-canvas-automation-1g8p</guid>
      <description>&lt;p&gt;The Canvas element in HTML (or &lt;em&gt;&lt;/em&gt;) is majorly used for rendering graphics, animations, and other interactive content on a web page. Though the Canvas element is a part of the DOM, content inside the Canvas is not a part of it! This essentially means that the traditional DOM-based validations do not hold well for the automated handling of Canvas elements.&lt;/p&gt;

&lt;p&gt;Over &amp;amp; above, automating Canvas interactions with Cypress is a tad easier than Selenium. This is because Cypress runs within the browser’s context, providing you seamless access to JavaScript APIs like the Canvas API. In this blog, we will be covering using Selenium (Python) and Cypress for Canvas element automation with hands-on examples.&lt;/p&gt;

&lt;h2&gt;
  
  
  What Is Canvas Element in HTML?
&lt;/h2&gt;

&lt;p&gt;The Canvas (&lt;em&gt;&lt;/em&gt;) element is used for drawing graphics or other visual content on-the-fly on a web page. Along with animations, the Canvas element can also be used for drawing charts, animations, and other custom visualizations on the page.&lt;/p&gt;

&lt;p&gt;The Canvas element was introduced as a part of the HTML5 standard for reducing the dependencies on plugins like Adobe Flash and Microsoft SilverLight, both of which have already reached EOL (End Of Life).&lt;/p&gt;

&lt;p&gt;The element, which is essentially a blank container, heavily relies on JavaScript for drawing any graphics and performing interactive actions. As stated in the Canvas API Documentation, the Canvas APIs primarily focus on 2D graphics. WebGL (Web Graphics Library), a JavaScript API for rendering high-performance interactive 2D &amp;amp; 3D graphics, also makes use of the &lt;em&gt;&lt;/em&gt; element.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Example&lt;/strong&gt; &lt;strong&gt;1:&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;As shown below, graphics in the &lt;a href="https://www.lambdatest.com/bug-smasher" rel="noopener noreferrer"&gt;LambdaTest Bug Smasher&lt;/a&gt; Game are housed in the &lt;em&gt; *element. After locating the Canvas element using the ID — *document.getElementById(“unity-canvas”)&lt;/em&gt;, we get the details (i.e., dimensions/size) of the said element.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Forikmxl67hkb9pg79xa7.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Forikmxl67hkb9pg79xa7.png" width="800" height="463"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The above command, when run on the Browser console, provides the following output:&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;&amp;lt;canvas id="unity-canvas" width="1920" height="1200" style="width: 960px; height: 600px; cursor: default;"&amp;gt;&amp;lt;/canvas&amp;gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;The Canvas element is identified using the ID attribute (i.e., unity-canvas)&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;The &lt;em&gt;width=”1920″&lt;/em&gt; and *height=”1200″ *attributes define the actual size of the Canvas element used for drawing/rendering&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;em&gt;style=”width: 960px; height: 600px;” *defines the display size of the Canvas element on the page. In this case, a scaling factor of 0.5 (*width = 960/1920&lt;/em&gt; and &lt;em&gt;height = 600/1200&lt;/em&gt;) is used when locating elements in the Canvas via its coordinates&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Example&lt;/strong&gt; &lt;strong&gt;2:&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Another example of the Canvas element is a rendering of a Bar Graph in CanvasJS. After locating the Canvas element using &lt;a href="https://www.lambdatest.com/learning-hub/selenium-locators" rel="noopener noreferrer"&gt;Selenium locators&lt;/a&gt; &lt;em&gt;document.getElementById(“themes-chart”)&lt;/em&gt;, we get the details (i.e., dimensions/size) of the said element.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fwr6ee9mdsayi6tri8mxe.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fwr6ee9mdsayi6tri8mxe.png" width="800" height="454"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The above command, when run on the browser console provides the following output:&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;&amp;lt;div id="themes-chart" style="width: 100%; height: 441px;"&amp;gt;&amp;lt;div class="canvasjs-chart-container" style="position: relative; text-align: left; cursor: auto; direction: ltr;"&amp;gt;&amp;lt;canvas class="canvasjs-chart-canvas" width="1892" height="882" style="width: 946px; height: 441px; position: absolute; user-select: none;"&amp;gt;&amp;lt;/canvas&amp;gt;...&amp;lt;/div&amp;gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;The Canvas element is identified using the ID attribute (i.e., themes-cart)&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;The &lt;em&gt;width=”1892″&lt;/em&gt; and *height=”882″ *attributes define the actual size of the Canvas element used for drawing/rendering&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;em&gt;style=”width: 946px; height: 441px;” *defines the display size of the Canvas element on the page. In this case, a scaling factor of 0.5 (*width = 946/1892&lt;/em&gt; and &lt;em&gt;height = 441/882&lt;/em&gt;) is used when locating elements in the Canvas via its coordinates&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;em&gt;As seen from the above examples, the internal resolution and display size difference are different (i.e., scaling — 0.5) and can affect the quality of the rendering.&lt;/em&gt; &lt;em&gt;When automating interactions with Canvas elements, it is important to take the scaling into account to ensure the accuracy of mouse movement actions.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;In this blog, I will automate Canvas elements on the local Chrome browser and/or Chrome installed on the LambdaTest cloud grid.&lt;/p&gt;

&lt;p&gt;LambdaTest is an AI-based test orchestration and execution platform that allows automated tests to be run at scale in more than 3,000 environments. It also supports automating Canvas elements on multiple browsers using popular frameworks like Selenium and Cypress.&lt;/p&gt;

&lt;h2&gt;
  
  
  The getBoundingClientRect() Method
&lt;/h2&gt;

&lt;p&gt;Though the &lt;em&gt;&lt;/em&gt; element is a part of the DOM, the elements rendered inside it are not represented in the DOM. This makes automating Canvas challenging, as the elements within the DOM have to be located using the co-ordinates.&lt;/p&gt;

&lt;p&gt;This is where the &lt;em&gt;getBoundingClientRect()&lt;/em&gt; method of the &lt;em&gt;Element&lt;/em&gt; class can be leveraged, as it provides the size of the element and its properties relative to the viewport. The *getBoundingClientRect() *method returns a *DOMRect *object that contains the following properties (in pixels):&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F3sizt3setcoeqrlnfi72.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F3sizt3setcoeqrlnfi72.png" width="800" height="411"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Shown below is the pictorial view of the &lt;em&gt;BoundingRect()&lt;/em&gt; method that deep dives in the approach used for calculating the properties:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fp4ww8w5d1px4oljgaubn.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fp4ww8w5d1px4oljgaubn.png" alt="*BoundingRect()*" width="800" height="599"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;When automating interactions with the Canvas element, set a consistent viewport size (e.g., 1024 * 768) and maintain a constant zoom-level (100% preferred) for reliable automation results.&lt;/p&gt;

&lt;p&gt;Let’s fetch the &lt;em&gt;DOMRect&lt;/em&gt; properties, by executing the *document. getElementById(“unity-canvas”).getBoundingClientRect *command on the browser console. Shown below is the command in action for the LambdaTest BugSmasher game:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fl57g4m3ielkc7crph05w.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fl57g4m3ielkc7crph05w.png" width="800" height="483"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F8l5tswsgx0kmw54doxcn.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F8l5tswsgx0kmw54doxcn.png" alt="*Chrome (Maximized) + Zoom-level (100%)*" width="800" height="260"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Now, let’s resize the browser window and check the properties contained in the &lt;em&gt;DOMRect&lt;/em&gt; object:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F22yx7onzyuawo1o53hgh.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F22yx7onzyuawo1o53hgh.png" width="800" height="530"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F24skho1ikz56bjtlf00r.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F24skho1ikz56bjtlf00r.png" alt="*Chrome (Resized) + Zoom-level (100%)*" width="800" height="251"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;As seen from the above screenshots, certain &lt;em&gt;DOMRect&lt;/em&gt; properties, namely &lt;em&gt;X&lt;/em&gt;, &lt;em&gt;Y&lt;/em&gt;, &lt;em&gt;top&lt;/em&gt;, &lt;em&gt;right&lt;/em&gt;, &lt;em&gt;bottom&lt;/em&gt;, and &lt;em&gt;left,&lt;/em&gt; change with a resized browser window. Like other &lt;a href="https://www.lambdatest.com/blog/selenium-best-practices-for-web-testing/?utm_source=devto&amp;amp;utm_medium=organic&amp;amp;utm_campaign=feb_04&amp;amp;utm_term=rj&amp;amp;utm_content=blog" rel="noopener noreferrer"&gt;Selenium best practices&lt;/a&gt;, it is recommended to instantiate the respective browser in maximized mode so that the &lt;em&gt;DOMRect *properties (i.e., *top&lt;/em&gt;, &lt;em&gt;right&lt;/em&gt;, &lt;em&gt;bottom&lt;/em&gt;, and &lt;em&gt;left&lt;/em&gt;) are consistent across subsequent runs.&lt;/p&gt;

&lt;p&gt;To summarize, the &lt;em&gt;getBoundingClientRect()&lt;/em&gt; method plays a pivotal role in handling Canvas elements by providing accurate size and coordinates relative to the viewport.&lt;/p&gt;

&lt;h2&gt;
  
  
  Tracking Mouse Movements in Canvas Elements
&lt;/h2&gt;

&lt;p&gt;As seen so far, interaction with Canvas content is based on precise coordinates rather than element-based tracking (which is normally used with DOM-based interactions). This is precisely why tracking mouse positions becomes essential to ensure that automation mimics real user behavior.&lt;/p&gt;

&lt;p&gt;During the process of writing this blog, I tried automating different Canvas element scenarios with Selenium as well as Cypress. Since Cypress tests run directly in the browser, I found it relatively easy to use Cypress for Canvas automation.&lt;/p&gt;

&lt;p&gt;As far as automation with Selenium is concerned, this insightful &lt;a href="https://stackoverflow.com/a/54467617" rel="noopener noreferrer"&gt;StackoverFlow thread&lt;/a&gt; helped a lot in calculating Canvas element coordinates more robustly. As stated in the W3C specification, the offset of action commands is calculated from the center of a WebElement.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fcdn-images-1.medium.com%2Fmax%2F2912%2F0%2AHAekCZXguOrSoA-z.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fcdn-images-1.medium.com%2Fmax%2F2912%2F0%2AHAekCZXguOrSoA-z.png" alt="[*moveToElement offset calculation on Chrome &amp;amp; Firefox browsers](https://stackoverflow.com/questions/54340636/element-does-not-move-within-canvas)*" width="800" height="550"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Regardless of the automation framework used, such as Selenium or Cypress for Canvas automation, it is imperative to track mouse movements so that coordinates can be adjusted as needed.&lt;/p&gt;

&lt;p&gt;I used the prompt “*Show a dot wherever the mouse pointer is.” *This code will be used for Selenium and Cypress for Canvas automation and fed into the AI tool that assisted me with code development.&lt;/p&gt;

&lt;p&gt;Here is the AI-generated code that will show a Red (&lt;em&gt;#FF0000&lt;/em&gt;) dot with every mouse movement.&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;/* Customized command to show the click on the element */
Cypress.Commands.add('showClickCoordinates', (element, x, y) =&amp;gt; {
 cy.wrap(element).then(($el) =&amp;gt; {
   // Reference - https://developer.mozilla.org/en-US/docs/Web/API/Element/getBoundingClientRect
   const rect = $el[0].getBoundingClientRect();


   cy.window().then((win) =&amp;gt; {
     // Create a new dot element
     const dot = win.document.createElement('div');
     dot.style.position = 'absolute';
     dot.style.width = '10px';
     dot.style.height = '10px';
     dot.style.backgroundColor = 'red';
     dot.style.borderRadius = '50%';
     dot.style.zIndex = '9999';


     // Adjust the top and left coordinates to be relative to the element
     dot.style.top = `${rect.top + y}px`;
     dot.style.left = `${rect.left + x}px`;


     // Append the dot to the body
     win.document.body.appendChild(dot);


     // Remove the dot after 3 seconds
     setTimeout(() =&amp;gt; {
       dot.remove();
     }, 3000);
   });
 });
});
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;Here is a short gist of the above AI-generated snippet, the primary purpose of which is to position a Red (&lt;em&gt;#FF0000&lt;/em&gt;) colored dot relative to an element in the Canvas. Let’s dive deep into the essential aspects of the snippet:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Step 1&lt;/strong&gt;. &lt;em&gt;Cypress.Commands.add&lt;/em&gt; adds a &lt;a href="https://www.lambdatest.com/video/what-are-cypress-custom-commands?utm_source=devto&amp;amp;utm_medium=organic&amp;amp;utm_campaign=feb_04&amp;amp;utm_term=rj&amp;amp;utm_content=blog" rel="noopener noreferrer"&gt;Cypress custom command&lt;/a&gt; (named &lt;em&gt;showClickCoordinates&lt;/em&gt;) that can be reused throughout the tests. It takes three input parameters:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;element: DOM element where the click needs to be highlighted&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;x: X-coordinate calculated relative to the element’s bounding box&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;y: Y-coordinate calculated relative to the element’s bounding box&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Step 2:&lt;/strong&gt; &lt;em&gt;getBoundingClientRect()&lt;/em&gt; provides the element’s size and position relative to the viewport. For the current use case, we extract the &lt;em&gt;top&lt;/em&gt;, &lt;em&gt;left&lt;/em&gt;, &lt;em&gt;width&lt;/em&gt;, and &lt;em&gt;height&lt;/em&gt; properties of the said element.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Step 3&lt;/strong&gt;: &lt;em&gt;cy.window()&lt;/em&gt; helps provide access to the browser’s window object (i.e., &lt;em&gt;win&lt;/em&gt;)&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Step 4&lt;/strong&gt;: This is the crux of the code where &lt;em&gt;win.document.createElement&lt;/em&gt; helps in creating a new &lt;em&gt;div&lt;/em&gt; element in the DOM (to act as a &lt;em&gt;dot&lt;/em&gt;). Here is the styling of the dot:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;&lt;em&gt;position: absolute&lt;/em&gt;&lt;/strong&gt;: Places the dot at an exact mouse position&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;&lt;em&gt;width *and *height&lt;/em&gt;&lt;/strong&gt;: Defines the size of the dot&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;&lt;em&gt;backgroundColor: ‘red’&lt;/em&gt;&lt;/strong&gt;: Makes the dot visible as &lt;em&gt;red (#FF0000)&lt;/em&gt;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;&lt;em&gt;borderRadius: ‘50%’&lt;/em&gt;&lt;/strong&gt;: Makes the dot a circle&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;&lt;em&gt;zIndex: ‘9999’&lt;/em&gt;&lt;/strong&gt;: Ensures the dot appears above other elements&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Step 5&lt;/strong&gt;: The &lt;em&gt;top (rect.top)&lt;/em&gt; and &lt;em&gt;left (rect.left)&lt;/em&gt; styles are calculated to position the dot relative to the element in the viewport. Here, &lt;em&gt;x&lt;/em&gt; and &lt;em&gt;y&lt;/em&gt; are the coordinates offset inside the element.&lt;/p&gt;

&lt;p&gt;The dot is finally appended to the &lt;em&gt;body&lt;/em&gt; of the document, making it visible on the page.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Step 6&lt;/strong&gt;: &lt;em&gt;dot.remove()&lt;/em&gt; removes the displayed dot to clean up the DOM after a set timeout of 3 seconds.&lt;/p&gt;

&lt;p&gt;Shown below is a sample use of the Mouse Movement tracking code when used for automating Canvas elements in the LambdaTest Bug Smasher game:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fbqrqovq8f5cavf9zs2t8.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fbqrqovq8f5cavf9zs2t8.png" alt="*Mouse Movement Tracking Example*" width="800" height="463"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;We will use the above AI-generated code with Selenium and Cypress for Canvas automation examples, which will be demonstrated in later sections of the blog. AI can help speed up code generation and streamline workflows, allowing QA’s to leverage its benefits by generating tests using AI. Since Canvas interactions are heavily based on coordinates, you could also use Chrome/Brave extensions like Page Ruler to measure distances (in pixels) on a webpage.&lt;/p&gt;

&lt;p&gt;Now, let’s get our hands dirty with some hands-on experiments with Selenium and Cypress or Canvas automation!&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F9ewlzi1bii24i2xb8ss8.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F9ewlzi1bii24i2xb8ss8.png" width="198" height="66"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Using Cypress for Canvas Automation
&lt;/h2&gt;

&lt;p&gt;For this demonstration, we will automate the play of LambdaTest Bug Smasher. If you are new to Cypress on the LambdaTest cloud, you can check out the blog on &lt;a href="https://www.lambdatest.com/blog/cypress-test/#how-to-automate-your-first-cypress-test-on-cloud" rel="noopener noreferrer"&gt;how to write your first Cypress test&lt;/a&gt;, which dives into the nuances of Cypress execution on LambdaTest.&lt;/p&gt;

&lt;p&gt;Canvas automation demonstration should work seamlessly on Cypress LambdaTest cloud as well as on Cypress installed on your local machine. In both cases, Chrome is chosen as the browser for the test. The configuration for executing the Canvas tests on LambdaTest is available in lambdatest-config.json. You need to add your LambdaTest user name and access key, details of which can be fetched from the Account Settings Page.&lt;/p&gt;

&lt;p&gt;Now that the setup is ready, let’s look at how we automated gameplay using Cypress; the entire implementation is available in &lt;a href="https://github.com/hjsblogger/canvas-automation-selenium-cypress/blob/main/cypress-demo/cypress/e2e/canvas-element-handling.cy.js" rel="noopener noreferrer"&gt;canvas-element-handling.cy.js&lt;/a&gt;&lt;/p&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;: To get started, set the viewport to 1024 * 768 using the &lt;em&gt;cy.viewport()&lt;/em&gt; method. Post that, and navigate to the LambdaTest Bug Smasher game page. It is important to note that the coordinate values used in the tests might need to be changed if the viewport size changes.&lt;/p&gt;

&lt;p&gt;*&lt;em&gt;Step 2: **Next, the *cy.document()&lt;/em&gt; method yields the &lt;em&gt;window.document&lt;/em&gt; object (i.e.,, &lt;em&gt;doc&lt;/em&gt;). The &lt;em&gt;doc.readyState&lt;/em&gt; property indicates the document’s current loading state.&lt;/p&gt;

&lt;p&gt;The &lt;em&gt;doc.readyState *property value is checked if it is *complete&lt;/em&gt;, which indicates that the document and its corresponding resources (e.g., images, stylesheets, etc.) are fully loaded.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F7br3v8aln22v4fl34p4z.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F7br3v8aln22v4fl34p4z.png" width="800" height="655"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;**Step 3 (Optional): **This step simply retrieves the dimensions of the viewport (in pixels). In our case, we set the viewport size to 1024 (width) and 768 (height).&lt;/p&gt;

&lt;p&gt;Once the window object is retrieved with the &lt;em&gt;cy.window()&lt;/em&gt; method, the &lt;em&gt;innerWidth&lt;/em&gt; and &lt;em&gt;innerHeight&lt;/em&gt; properties are obtained to fetch the width &amp;amp; height of the viewpor*t,* respectively.&lt;/p&gt;

&lt;p&gt;*&lt;em&gt;Step 4: **The Canvas element is located using the ID locator (i.e., *#unity-canvas&lt;/em&gt;), which is passed to the &lt;em&gt;cy.get()&lt;/em&gt; method.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F858n0hx5adiougymnkqp.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F858n0hx5adiougymnkqp.png" width="800" height="477"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Once the &lt;em&gt;&lt;/em&gt; element is located, its &lt;em&gt;width&lt;/em&gt; &amp;amp; &lt;em&gt;height&lt;/em&gt; (when rendered on the page) properties are read using &lt;em&gt;$canvas[0].width&lt;/em&gt; &amp;amp; &lt;em&gt;$canvas[0].height&lt;/em&gt; respectively. The intrinsic resolution of the Canvas in pixels is set to &lt;em&gt;width: 1920px;&lt;/em&gt; &lt;em&gt;height: 1200px;&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;It is important to note that these properties indicate the CSS-rendered size of the Canvas on the document (i.e., &lt;em&gt;width: 960px;&lt;/em&gt; &lt;em&gt;height: 600px;&lt;/em&gt;).&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fx0jpvehkjm1ooc05wcnw.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fx0jpvehkjm1ooc05wcnw.png" width="800" height="277"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Step 5: *&lt;em&gt;The remaining Canvas properties (*X&lt;/em&gt;, &lt;em&gt;Y&lt;/em&gt;, &lt;em&gt;top&lt;/em&gt;, &lt;em&gt;bottom&lt;/em&gt;, &lt;em&gt;left&lt;/em&gt;, &lt;em&gt;right&lt;/em&gt;) are also **&lt;/strong&gt;read using the &lt;em&gt;getBoundingClientRect()&lt;/em&gt; method, which we discussed in earlier sections of the blog.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F2s7m88yqwkmpx2xy29uj.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F2s7m88yqwkmpx2xy29uj.png" alt="*BoundingRect()*" width="800" height="599"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The CSS-rendered Canvas width &amp;amp; height are assigned to &lt;em&gt;canvasDisplayedWidth&lt;/em&gt; &amp;amp; &lt;em&gt;canvasDisplayedHeight&lt;/em&gt; variables, respectively.&lt;/p&gt;

&lt;p&gt;*&lt;em&gt;Step 6: **The scaleFactor is set to 0.5 since the CSS-rendered Canvas width is half of the Canvas displayed width. *centerX&lt;/em&gt; &amp;amp; &lt;em&gt;centerY&lt;/em&gt; of the Canvas are calculated for operations to be performed on the Canvas.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Step 7: *&lt;em&gt;When calculating the offset, I played around with a few values after *&lt;/em&gt;&lt;/strong&gt;getting the property values using the *getBoundingClientRect() *method.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fz2b9v05iqxmumuomyzgm.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fz2b9v05iqxmumuomyzgm.png" width="800" height="576"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The variables &lt;em&gt;buttonXInCanvas&lt;/em&gt; and &lt;em&gt;buttonYInCanvas&lt;/em&gt; are set to twice of Canvas center values.&lt;/p&gt;

&lt;p&gt;**Step 8: **The X coordinate from the earlier step is multiplied by the scale factor (i.e., 0.5 in this case).&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Ffv7j9z6az97ypwbv4ei4.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Ffv7j9z6az97ypwbv4ei4.png" width="800" height="277"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;*&lt;em&gt;Step 9: **The *cy.wrap()&lt;/em&gt; wraps a DOM element (i.e., &lt;em&gt;&lt;/em&gt; element) into a Cypress chainable object.&lt;/p&gt;

&lt;p&gt;Here, the Cypress custom command (e.g., &lt;em&gt;showClickCoordinates&lt;/em&gt;), which shows a red-colored dot in the center of the Canvas (e.g., &lt;em&gt;canvasCenterX&lt;/em&gt; &amp;amp; &lt;em&gt;canvasCenterY&lt;/em&gt;), is wrapped in the &lt;em&gt;cy.wrap()&lt;/em&gt; method.&lt;/p&gt;

&lt;p&gt;*&lt;em&gt;Step 10: **The next important step is to locate the *Play Now&lt;/em&gt; button in the Canvas. As seen in the screenshot below, the position of the &lt;em&gt;Play Now&lt;/em&gt; button can be computed by negating a couple of pixel values from &lt;em&gt;buttonYInCanvasScaled&lt;/em&gt; (which is set to &lt;em&gt;canvasCenterY * 2&lt;/em&gt;).&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fv0v6x6u850cmu6nzgxay.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fv0v6x6u850cmu6nzgxay.png" width="800" height="464"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;In our case, subtracting &lt;em&gt;Top/Y&lt;/em&gt; (i.e., 89.5 pixels) from &lt;em&gt;buttonYInCanvasScaled *provided the exact coordinates of the *Play Now&lt;/em&gt; button. Since the value of the Top/Y property might change with the zoom level and viewport size, it is always recommended to keep the zoom level to 100% and set the viewport size (e.g., 1024 * 768 in this case) before triggering the tests.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fku3xt24rw51wa32ooxm8.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fku3xt24rw51wa32ooxm8.png" width="800" height="270"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;*&lt;em&gt;Step 11: **Cypress invokes the *click()&lt;/em&gt; method to click on the &lt;em&gt;Play Now&lt;/em&gt; button. The &lt;em&gt;force&lt;/em&gt; option in the &lt;em&gt;click()&lt;/em&gt; method is set to *true *since it bypasses certain default checks (e.g., element visibility, interactability, etc.) that Cypress performs before interacting with elements.&lt;/p&gt;

&lt;p&gt;The option might not be needed in our case since the &lt;em&gt;Play Now&lt;/em&gt; button is also visible and interactable.&lt;/p&gt;

&lt;p&gt;**Step 12 (Final Game Play — 1): **This takes us inside the game arena! As mentioned so far, it would be time-consuming to locate the objects (e.g., bugs) and then act on them.&lt;/p&gt;

&lt;p&gt;An easier approach would be to create random X and Y coordinate value pairs, taking into consideration the required properties returned by the &lt;em&gt;getBoundingClientRect()&lt;/em&gt; method.&lt;/p&gt;

&lt;p&gt;The &lt;em&gt;getRandomCoordinates() *function provides randomized X and Y coordinates using the following calculation: a float random number between 0 and 1 is generated using the *Math.random()&lt;/em&gt; function in JavaScript.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Flakl1yfefge3wn6s9kye.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Flakl1yfefge3wn6s9kye.png" alt="*BoundingRect()*" width="800" height="599"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The generated random number is multiplied by the Canvas width (for X) and height (for Y). The resultant is then added to the &lt;em&gt;top&lt;/em&gt; property (for X) and &lt;em&gt;left&lt;/em&gt; property (for Y). A buffer of 200 pixels is added to the final result to ensure that the generated X-Y coordinates are located within the game area (marked in red in the screenshot).&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;let randomX = Math.floor(Math.random() * (canvasWidth)) + left + 200;
let randomY = Math.floor(Math.random() * (canvasHeight)) + top + 200;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fu5icocndhpq6m2rxs9lv.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fu5icocndhpq6m2rxs9lv.png" alt="*Game Area*" width="800" height="477"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;If the generated values are greater than &lt;em&gt;canvasWidth&lt;/em&gt; and/or &lt;em&gt;canvasHeight&lt;/em&gt;, they are set to &lt;em&gt;canvasCenterX&lt;/em&gt; and &lt;em&gt;canvasCenterY&lt;/em&gt;, respectively.&lt;/p&gt;

&lt;p&gt;*&lt;em&gt;Step 13 (Final Game Play — 2): **Now that we have the coordinates handy, a click is generated every 500 ms using the *cy.click()&lt;/em&gt; method. Since no object model detection is implemented in the code, there is a possibility of clicking on a bomb object, which results in the end of the game.&lt;/p&gt;

&lt;p&gt;**Step 14 (Clicking the LinkedIn Button — Outside Canvas): **In the LambdaTest Bug Smasher game, the social media icons (e.g., LinkedIn and Twitter) are placed outside the Canvas.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fwsedbgj2pf0r2651b51f.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fwsedbgj2pf0r2651b51f.png" width="800" height="477"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;First, the LinkedIn button is located with the CSS Selector locator. The window.open() method is stubbed with the &lt;em&gt;cy.stub(win, ‘open’).as(‘windowOpen’),&lt;/em&gt; where &lt;em&gt;win&lt;/em&gt; is the window handle.&lt;/p&gt;

&lt;p&gt;Since the window is stubbed, it does not open an actual window or tab. This &lt;a href="https://www.lambdatest.com/learning-hub/cypress-tutorial?utm_source=devto&amp;amp;utm_medium=organic&amp;amp;utm_campaign=feb_04&amp;amp;utm_term=rj&amp;amp;utm_content=blog" rel="noopener noreferrer"&gt;Cypress tutorial&lt;/a&gt; explains Cypress stubs and other important concepts related to the framework.&lt;/p&gt;

&lt;p&gt;Finally, a click operation is performed on the window, and &lt;em&gt;Cypress.sinon.match()&lt;/em&gt; is called to ensure that the string contains the substring &lt;em&gt;linkedin.com&lt;/em&gt;.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Ffcro5i65lozmnxfges68.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Ffcro5i65lozmnxfges68.png" width="800" height="477"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Now, let’s execute the tests on Cypress on the LambdaTest cloud grid and Cypress installed on a local machine!&lt;/p&gt;

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

&lt;p&gt;Before we start the tests, let’s set up the virtual environment (&lt;em&gt;venv&lt;/em&gt;), which will help us better manage the environment and dependencies.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;venv&lt;/em&gt; plays an instrumental role in providing isolation from the packages installed in the base environment. Run the commands &lt;em&gt;virtualenv venv&lt;/em&gt; and &lt;em&gt;source venv/bin/activate&lt;/em&gt; on the terminal to create the virtual environment.&lt;/p&gt;

&lt;p&gt;Next, export the environment variables LT_USERNAME and LT_ACCESS_KEY on the terminal.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;export LT_USERNAME=&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;&lt;em&gt;export LT_ACCESS_KEY=&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;You can get these details from the &lt;a href="https://accounts.lambdatest.com/security" rel="noopener noreferrer"&gt;Password &amp;amp; Security&lt;/a&gt; section. For execution on LambdaTest, first, install the dependencies by triggering the command &lt;em&gt;make install&lt;/em&gt; from the root folder of the project.&lt;/p&gt;

&lt;p&gt;With the dependencies installed &amp;amp; environment variables exported, you can trigger the command &lt;em&gt;make cloud-canvas-automation-cypress&lt;/em&gt; from the root folder on the terminal. This makes the command internally execute the command &lt;em&gt;lambdatest-cypress run&lt;/em&gt; that triggers the Canvas element test on the LambdaTest grid.&lt;/p&gt;

&lt;p&gt;You can check out the support documentation on getting started with Cypress testing in case you encounter any issues with the execution. Once the tests are triggered, you can check the status on the LambdaTest Automation Dashboard.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fvv33cuw8jraxojrdgm0d.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fvv33cuw8jraxojrdgm0d.png" width="800" height="459"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F8yfc7ax0q9caaq8gmms4.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F8yfc7ax0q9caaq8gmms4.png" alt="*LambdaTest Automation Dashboard*" width="800" height="473"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;As seen above, we automated interactions with the Canvas element and also achieved a score of 85 🔥&lt;/p&gt;

&lt;p&gt;The same test can also be executed on Canvas (on the local machine) by triggering the command &lt;em&gt;make local-canvas-automation-cypress&lt;/em&gt;, which internally triggers &lt;em&gt;npx cypress run-browser chrome-headed&lt;/em&gt; to the terminal.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fw2zv1xlfp3j0xpojxm0t.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fw2zv1xlfp3j0xpojxm0t.png" width="800" height="392"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;With this, we have learned to use Cypress for Canvas automation! However, regardless of the Canvas element type (e.g., bar graphs, pie charts, etc.), you can still use the learnings from this section to automate Canvas interactions!&lt;/p&gt;

&lt;h2&gt;
  
  
  Using Selenium for Canvas Automation
&lt;/h2&gt;

&lt;p&gt;There are scenarios where you might need to extract stats from graphs (e.g., pie charts, bar graphs, etc.) and report them to your organization’s higher stakeholders. For example, a bar graph can showcase an organization’s monthly sales data. Similar to this, the data can also be projected in the form of pie charts, line charts, and more!&lt;/p&gt;

&lt;p&gt;Here is another example of a pie-chart deep diving into &lt;em&gt;Employee Distribution by location&lt;/em&gt;. An automation script can be written to extract the tool-tip information from each quadrant in the &lt;em&gt;&lt;/em&gt; element.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.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%2AlAUAuUPAD63uqQBG.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.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%2AlAUAuUPAD63uqQBG.png" alt="[*Pie Chart Canvas Example — OrangeHRM](https://opensource-demo.orangehrmlive.com/web/index.php/dashboard/index)*" width="800" height="478"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;To demonstrate how Selenium handles Canvas elements, we will extract monthly sales data from a Canvas element in CanvasJS.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.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%2AYn1UZqmQIIUVj85-.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.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%2AYn1UZqmQIIUVj85-.png" alt="[*Sample Sales Data — CanvasJS](https://canvasjs.com/)*" width="800" height="364"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;You can check out my &lt;a href="https://www.lambdatest.com/blog/getting-started-with-selenium-python/?utm_source=devto&amp;amp;utm_medium=organic&amp;amp;utm_campaign=feb_04&amp;amp;utm_term=rj&amp;amp;utm_content=blog" rel="noopener noreferrer"&gt;Selenium Python&lt;/a&gt; tutorial if you want a quick refresher on automation with Selenium and PyUnit (&amp;amp; Pytest). In this &lt;a href="https://github.com/hjsblogger/canvas-automation-selenium-cypress/blob/main/selenium-python-demo/canvas_bar_graph.py" rel="noopener noreferrer"&gt;Canvas bar graph example&lt;/a&gt;, we have used the PyUnit/unittest framework. However, the test can be easily ported for testing with the pytest framework.&lt;/p&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; Since we are using the &lt;em&gt;PyUnit&lt;/em&gt; (or &lt;em&gt;unittest&lt;/em&gt;) framework for test automation, the browser &amp;amp; platform capabilities are set in the method implemented under the &lt;em&gt;setUp()&lt;/em&gt; annotation.&lt;/p&gt;

&lt;p&gt;The capabilities are generated using the &lt;a href="https://www.lambdatest.com/capabilities-generator" rel="noopener noreferrer"&gt;LambdaTest Capabilities Generator&lt;/a&gt;, and the username and access key are added to helpers/utils.py.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Ftqaac4t9fhekrbni4veo.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Ftqaac4t9fhekrbni4veo.png" width="800" height="323"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Step 2:&lt;/strong&gt; Since the test URL has dynamic content, scrolling vertically to the bottom of the page (and starting) ensures that all the required assets are available.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;document.body.scrollHeight&lt;/em&gt; returns the total height of the page content. To allow the scrolling to complete and give the page content enough time to load, a blocking sleep of 2 seconds (i.e., &lt;em&gt;time. sleep (2)&lt;/em&gt;) is added.&lt;/p&gt;

&lt;p&gt;Lastly, &lt;em&gt;window.scrollTo(0, 0)&lt;/em&gt; scrolls to the start of the page and a sleep of 2 seconds ensures that any UI changes triggered by scrolling are complete.&lt;/p&gt;

&lt;p&gt;*&lt;em&gt;Step 3: **Now that the page is loaded, the Canvas element is located using the ID locator in Selenium (i.e., *themes-cart&lt;/em&gt;). The &lt;em&gt;presence_of_element_located&lt;/em&gt; ExpectedCondition in Selenium is added to check if the Canvas element is present in the DOM.&lt;/p&gt;

&lt;p&gt;The presence is checked with a WebDriverWait of 30 seconds. If the element is not located within this time, an exception is raised, and the test is considered &lt;em&gt;failed&lt;/em&gt;.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fuj1dbqp2gtneo5yhmw7i.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fuj1dbqp2gtneo5yhmw7i.png" width="800" height="467"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;*&lt;em&gt;Step 4: **Akin to using Cypress for Canvas automation, the Canvas properties are extracted with the *getBoundingClientRect()&lt;/em&gt; method in JavaScript.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fi15j7wjb2xnnrt53vwju.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fi15j7wjb2xnnrt53vwju.png" alt="*BoundingRect()*" width="800" height="599"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Here, the &lt;a href="https://www.lambdatest.com/blog/how-to-use-javascriptexecutor-in-selenium-webdriver/" rel="noopener noreferrer"&gt;JavaScriptExecutor in Selenium&lt;/a&gt; is leveraged with the *execute_script *method to execute JS code directly in the browser’s context. The implementation of this can be found in &lt;a href="https://github.com/hjsblogger/canvas-automation-selenium-cypress/blob/main/selenium-python-demo/helpers/utils.py" rel="noopener noreferrer"&gt;utils.py&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;*&lt;em&gt;Step 5: **Once the Canvas properties are derived, the center (X &amp;amp; Y) of the Canvas element is deduced by halving the display size of the *&lt;/em&gt; element on the web page (i.e., &lt;em&gt;946 * 441&lt;/em&gt; pixels). The X &amp;amp; Y coordinates offset is set to 100, we will touch upon the reasons in the further section of this blog.&lt;/p&gt;

&lt;p&gt;**Step 6: **To compute the month-on-month sales data, we first need to hover over the corresponding bar (for the respective month) and then read the tooltip. For this, the absolute X, Y value pair is read using the Mouse Coordinates Chrome extension.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F7x4322ggpowj0ew6jjcb.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F7x4322ggpowj0ew6jjcb.png" alt="*X &amp;amp; Y coordinates of data marked as December*" width="800" height="396"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fibeiek0vvv693o2blnd7.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fibeiek0vvv693o2blnd7.png" alt="*X &amp;amp; Y coordinates of data marked as November*" width="800" height="396"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;As seen above, hovering over (1230, 389) should provide the sales data for December. On similar lines, hovering over (1130, 389) should provide the sales data for November. This means that the X offset can be set to 100 pixels. The coordinate value of the Y-axis can be set to Canvas center height (i.e. &lt;em&gt;221 +- 100 pixels&lt;/em&gt;). The logic mentioned for this test needs changes if the data is read from a pie chart or some other form of representation.&lt;/p&gt;

&lt;p&gt;Since we need to find sales data for 12 months (January ~ December), a descending &lt;em&gt;For loop&lt;/em&gt; from (12:0) is executed in the code. The &lt;em&gt;move_to_element_with_offset()&lt;/em&gt; method which is a part of the Actions Class in Selenium, helps in moving to the respective X, Y pair in the Canvas.&lt;/p&gt;

&lt;p&gt;The video below delves into the essential fundamentals of the &lt;em&gt;move_to_element()&lt;/em&gt; method, including how to click on a specific area of an element.&lt;/p&gt;

&lt;p&gt;Post the movement of the mouse pointer to a specific position relative to the center of the &lt;em&gt;&lt;/em&gt; element, a &lt;em&gt;click&lt;/em&gt; operation is simulated at the current position.&lt;/p&gt;

&lt;p&gt;**Step 7: **The sales data for the corresponding month are displayed as tooltip text on every bar. In Selenium, the tooltip element (inside the Canvas element) is located using the Class Name locator.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fettfd4gkos9rsprmoh8r.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fettfd4gkos9rsprmoh8r.png" width="800" height="456"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Next, the visible text content of the &lt;em&gt;tooltip_elem&lt;/em&gt; is printed on the console using &lt;em&gt;print(tooltip_elem.text)&lt;/em&gt;.&lt;/p&gt;

&lt;p&gt;After executing the descending For loop (from 1 to 12), the sales data for all 12 months should be printed on the terminal.&lt;/p&gt;

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

&lt;p&gt;First up, export the environment variables LT_USERNAME and LT_ACCESS_KEY on the terminal.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;export LT_USERNAME=LT_USERNAME&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;&lt;em&gt;export LT_ACCESS_KEY=LT_ACCESS_KEY&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;You can get these details from the profile section on LambdaTest. In case you have not installed the dependencies, you can do the same by triggering the command &lt;em&gt;make install&lt;/em&gt; from the root folder of the project.&lt;/p&gt;

&lt;p&gt;Run the command &lt;em&gt;make cloud-canvas-automation-python&lt;/em&gt; on the terminal, post which the tests would be executed on the LambdaTest grid and sales data is printed on the terminal.&lt;/p&gt;

&lt;p&gt;Like before, you can also check the test execution status by visiting the LambdaTest Automation Dashboard.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fwq3wmkgegf5l8nj6rnjc.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fwq3wmkgegf5l8nj6rnjc.png" width="800" height="466"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fg7e6pf1xb7kabehp36h1.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fg7e6pf1xb7kabehp36h1.png" width="800" height="466"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F3r3d2tqbd5tdptx4h002.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F3r3d2tqbd5tdptx4h002.png" alt="*LambdaTest Automation Dashboard Snapshot*" width="800" height="474"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Many more Canvas element scenarios can be automated using Selenium/Cypress, but covering all of them is beyond the scope of this blog.&lt;/p&gt;

&lt;h2&gt;
  
  
  Bonus: Automating Tasks Using PyAutoGUI
&lt;/h2&gt;

&lt;p&gt;Until now, we have used Selenium and Cypress to automate interactions with the Canvas element on a webpage or document. Though the above-demonstrated tests can be executed seamlessly on local and LambdaTest cloud grids, the PyAutoGUI module can also be used to automate interactions on local machines.&lt;/p&gt;

&lt;p&gt;PyAutoGUI is a cross-platform Python module for GUI automation. It is designed to programmatically control the mouse and keyboard. To install it, trigger &lt;em&gt;pip3 install pyautogui&lt;/em&gt; (or &lt;em&gt;pip install pyautogui&lt;/em&gt;) from the terminal.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.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%2AfujeC6Sq-IThUvY9.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.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%2AfujeC6Sq-IThUvY9.png" alt="[GitHub](https://github.com/asweigart/pyautogui)" width="800" height="194"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;As stated in the official documentation, the x, y coordinates used by PyAutoGUI have the (0, 0) origin coordinates that start from the top left corner of the screen. On a screen that is 1920 * 1080 pixels in size, coordinates (0, 0) are for the top left while (1919, 1079) is for the bottom right.&lt;/p&gt;

&lt;p&gt;It is important to note that PyAutoGUI interacts with the GUI directly by simulating mouse and keyboard events. Understandably, PyAutoGUI tests might fail or produce unexpected results if the window is switched while the execution is in progress.&lt;/p&gt;

&lt;p&gt;For demonstration, we have automated LambdaTest Bug Smasher on Chrome &lt;em&gt;(v131.0.6778.205) *installed on *macOS (Sequoia 15.2)&lt;/em&gt;. Like before, the viewport size is set to (&lt;em&gt;1024 * 768&lt;/em&gt;). The coordinates of the &lt;em&gt;Play Now&lt;/em&gt; button and logic associated with the random generation of coordinates are similar to the Cypress Canvas Automation demo discussed in the earlier section of this blog.&lt;/p&gt;

&lt;p&gt;The entire implementation is available in canvas_autogui.py.&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;# Inspiration - https://www.youtube.com/watch?v=lfk_T6VKhTE


import pyautogui
import unittest
from selenium import webdriver
from selenium.webdriver.common.by import By
from selenium.webdriver.chrome.options import Options as ChromeOptions
from selenium import webdriver
import time
from selenium import webdriver
import unittest
from selenium.webdriver.common.action_chains import ActionChains
from selenium.webdriver.support.ui import WebDriverWait
from selenium.webdriver.support import expected_conditions as EC
from selenium.webdriver.remote.webelement import WebElement
from helpers.utils import get_canvas_properties, show_click_coordinates, get_random_coordinates
# This is for the variables
from helpers.utils import *


class TestCanvasGraphAutomation(unittest.TestCase):
   def setUp(self):
       # Initialize the remote WebDriver session
       self.driver = webdriver.Chrome()
       self.driver.set_page_load_timeout(iWaitTime)
       self.driver.set_window_size(1024, 768)
       self.driver.maximize_window()
       self.driver.get(home_page_url_2)


       # Wait until the page is fully loaded
       WebDriverWait(self.driver, iWaitTime).until(lambda d:
               d.execute_script("return document.readyState") == "complete")


   def test_canvas_automation(self):
       driver = self.driver
       try:
           # Locate the graph
           canvas_elem = WebDriverWait(driver, iFrameWaitTime).until(
               EC.presence_of_element_located((By.ID, "unity-canvas"))
           )
           print("Canvas Element found")
           # Scroll to the element
           driver.execute_script("arguments[0].scrollIntoView({behavior: 'smooth'});", canvas_elem)
           time.sleep(iWaitTime)
       except Exception as e:
           print("Element not found:", e)
           status = "failed"
           self.update_lambdatest_status(status=status)


       # Get the canvas properties
       canvas_properties = get_canvas_properties(driver=driver, canvas_elem=canvas_elem)
       # Extracting each property from the returned dictionary for printing or usage
       canvas_left = int(canvas_properties["left"])
       canvas_x = int(canvas_properties["x"])
       canvas_y = int(canvas_properties["y"])
       canvas_top = int(canvas_properties["top"])
       canvas_width = int(canvas_properties["width"])
       canvas_height = int(canvas_properties["height"])
       canvas_right = int(canvas_properties["right"])
       canvas_bottom = int(canvas_properties["bottom"])


       # Print retrieved properties
       print(f"Canvas width: {canvas_width}")
       print(f"Canvas height: {canvas_height}")
       print(f"Canvas top: {canvas_top}")
       print(f"Canvas left: {canvas_left}")
       print(f"Canvas X: {canvas_x}")
       print(f"Canvas Y: {canvas_y}")
       print(f"Canvas right: {canvas_right}")
       print(f"Canvas bottom: {canvas_bottom}")


       time.sleep(iWaitTime)


       # Calculate the center coordinates of the canvas
       canvas_center_x = canvas_width / 2
       canvas_center_y = canvas_height / 2


       print(f"Canvas center width: {canvas_center_x}")
       print(f"Canvas center height: {canvas_center_y}")


       # Coordinates for the button within the canvas (substitute with actual button coordinates)
       button_x_in_canvas = 600  # example x position within canvas
       button_y_in_canvas = 620  # example y position within canvas


       # Calculate the absolute coordinates for the button
       absolute_x = canvas_x + button_x_in_canvas
       absolute_y = canvas_y + button_y_in_canvas


       show_click_coordinates(self.driver, canvas_elem, absolute_x, absolute_y)


       # Perform the click with PyAutoGUI at the calculated coordinates
       pyautogui.click(absolute_x, absolute_y)


       # Perform 60 random clicks inside the canvas
       for i in range(60):
           random_x, random_y = get_random_coordinates(canvas_properties, canvas_width,
                   canvas_height, canvas_center_x, canvas_center_y)

           # Log the coordinates to verify
           print(f"Clicking at X: {random_x}, Y: {random_y}")

           # Move to the coordinates and click using PyAutoGUI
           pyautogui.moveTo(random_x, random_y)
           show_click_coordinates(self.driver, canvas_elem, random_x, random_y)
           pyautogui.click()


           # Add a delay to see the clicks happen in real-time
           time.sleep(0.5)  # Adjust delay as needed


       print("Canvas Game Completed")


       status = "passed"


       # Update test status
       self.update_test_status(status=status)


       time.sleep(2)


   def update_test_status(self, status):
       self.driver.quit()


# Run the test
if __name__ == "__main__":
   unittest.main()
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;The only difference is the use of the &lt;em&gt;pyautogui.click()&lt;/em&gt; method, which moves to the X and Y coordinates and clicks the left mouse button.&lt;/p&gt;

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

&lt;p&gt;Run the command &lt;em&gt;make local-canvas-automation-python&lt;/em&gt; on the terminal. The Chrome browser will then be instantiated, and the PyAutoGUI module will automate gameplay interactions.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F5g72lrfgxcj9dili5ufk.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F5g72lrfgxcj9dili5ufk.png" width="705" height="461"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fcvat0hjzeclb34j2x1t2.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fcvat0hjzeclb34j2x1t2.png" width="598" height="435"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The Python tests showcased as a part of this blog can be easily ported to make it work with the pytest framework.&lt;/p&gt;

&lt;h2&gt;
  
  
  Challenges in Automating Canvas Interactions
&lt;/h2&gt;

&lt;p&gt;The &lt;em&gt;&lt;/em&gt; element differs a lot from traditional HTML elements, primarily in how content within the Canvas is laid out and accessed. Unlike HTML elements that are a part of the DOM, Canvas content is outside the DOM structure.&lt;/p&gt;

&lt;p&gt;Here are some of the major challenges when automating Canvas elements with frameworks like Selenium/Cypress:&lt;/p&gt;

&lt;h2&gt;
  
  
  Reliance on Pixel-Based Automation
&lt;/h2&gt;

&lt;p&gt;While test automation frameworks like Selenium and Cypress rely on locator strategies and built-in APIs to realize element interactions, the automation of the Canvas element combines coordinate-based actions with the use of JavaScript APIs.&lt;/p&gt;

&lt;p&gt;For example, we can automate actions on &lt;a href="https://ecommerce-playground.lambdatest.io/" rel="noopener noreferrer"&gt;LambdaTest E-Commerce Playground&lt;/a&gt; by locating elements and using appropriate Selenium WebDriver APIs. The same can also be achieved with the help of the Cypress framework.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fxu4g9s1h1kctsqdu0j9h.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fxu4g9s1h1kctsqdu0j9h.png" alt="*Example of DOM-based interaction*" width="800" height="473"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;In case of a Canvas element, you first need to locate the Canvas element using the best-suited locators. Post that, move to the respective coordinates (or at an offset from the Canvas) and then perform respective actions (e.g., clicks, send keys, etc.) using built-in framework APIs or JavaScript injection.&lt;/p&gt;

&lt;p&gt;As stated earlier, Canvas automation can be flaky because it relies on pixel-based rendering rather than a DOM-based structure.&lt;/p&gt;

&lt;h2&gt;
  
  
  Scaling, Zoom, and ViewPort Dependencies
&lt;/h2&gt;

&lt;p&gt;Automation tests with Selenium can become flaky if the scaling, viewport size, and zoom levels are not set to constant values across tests. For instance, if the zoom level is changed from 100% to 200%, an ElementNotInteracable exception or an element not in the viewport may occur.&lt;/p&gt;

&lt;p&gt;These pointers are valid, but you also need to ensure that the automation code can adapt to changes in viewport size, browser zoom level, and Device Pixel Ratio (DPR).&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fm768ap96g3tccpnvct8i.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fm768ap96g3tccpnvct8i.png" alt="*Canvas Example: LambdaTest Bug Smasher Game (Zoom — 100%)*" width="800" height="482"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Shown below is the browser snapshot that shows the change in the X &amp;amp; Y coordinates of the “&lt;em&gt;Play Now&lt;/em&gt;” button once the zoom level is set to 200%&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Ffjz9y2lyvtwmalzs5zj3.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Ffjz9y2lyvtwmalzs5zj3.png" alt="*Canvas Example: LambdaTest Bug Smasher Game (Zoom — 200%)*" width="800" height="482"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Canvas automation code is more vulnerable to these variable factors than DOM-based Selenium tests, as element interactions are based on pixel positions within the Canvas.&lt;/p&gt;

&lt;h2&gt;
  
  
  Cross Browser Differences
&lt;/h2&gt;

&lt;p&gt;The &lt;em&gt;&lt;/em&gt; element is compatible with widely-used browsers — Chrome, Firefox, Safari, and Edge. Since every browser uses its rendering engine, Canvas scaling and rendering will differ based on browser zoom levels, viewport settings, and devicePixelRatio.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.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%2Aw2HdjbWCX3I000m6.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.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%2Aw2HdjbWCX3I000m6.png" alt="[*Source](https://caniuse.com/?search=canvas)*" width="800" height="386"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The underlying rendering engine of browsers can impact the layout and rendering of Canvas elements. This is because the implementation of the HTML5 Canvas API can differ from one engine to another.&lt;/p&gt;

&lt;p&gt;In summary, subtle differences in the visual output can make tests that automate interactions with the Canvas elements flaky.&lt;/p&gt;

&lt;h2&gt;
  
  
  Complex Element Interactions
&lt;/h2&gt;

&lt;p&gt;As elements in the Canvas are not a part of the DOM, interacting with frequently used elements like text boxes, buttons, sliders, etc., becomes a huge challenge. Operations like scrolling, moving by an offset, drag &amp;amp; drops, mouse actions etc., require a JavaScript-based approach or usage of &lt;a href="https://www.lambdatest.com/blog/what-is-actions-class-in-selenium/?utm_source=devto&amp;amp;utm_medium=organic&amp;amp;utm_campaign=feb_04&amp;amp;utm_term=rj&amp;amp;utm_content=blog" rel="noopener noreferrer"&gt;Actions Class in Selenium&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Example&lt;/em&gt; — Entering personal information in text-boxes of the LambdaTest Bug Smasher involves locating the elements using &lt;em&gt;move_to_element_with_offset()&lt;/em&gt; method (in Selenium) and respective its co-ordinates. This approach can be more cumbersome if there are more text-boxes in the Canvas 🙁&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fv5wxmwb45yb6k1izz396.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fv5wxmwb45yb6k1izz396.png" width="800" height="449"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Over &amp;amp; above, you might need to put significant effort into finding coordinate offset if you have to test &lt;em&gt;&lt;/em&gt; interactions across different browsers, devices, and OS combinations!&lt;/p&gt;

&lt;h2&gt;
  
  
  Handling Dynamic Elements
&lt;/h2&gt;

&lt;p&gt;Selenium Wait comes in handy when you are automating websites that have dynamic elements (or dynamic content). It helps in improving test reliability and addresses timing issues that arise due to factors like network throttling, WebElement visibility, and more.&lt;/p&gt;

&lt;p&gt;However, dynamic content in Canvas that involves gestures, drawings, or animations cannot be automated in the way it is done in Selenium. Content in a &lt;em&gt;&lt;/em&gt; element is a single graphics block, owing to which it is difficult to predict when the said element will appear in the Canvas, even in non-throttling (or stable) conditions!&lt;/p&gt;

&lt;p&gt;Over &amp;amp; above, Canvas content does not have a layered (or hierarchical structure) like the &lt;a href="https://www.lambdatest.com/blog/document-object-model/?utm_source=devto&amp;amp;utm_medium=organic&amp;amp;utm_campaign=feb_04&amp;amp;utm_term=rj&amp;amp;utm_content=blog" rel="noopener noreferrer"&gt;Document Object Model (DOM)&lt;/a&gt;. Layers (or z-index-style ordering) must be simulated programmatically.&lt;/p&gt;

&lt;p&gt;Example — Selenium/Cypress is not extensively used for game automation (that contains &lt;em&gt;&lt;/em&gt; element). However, we took up the LambdaTest BugSmasher example to showcase the potential challenges that come with automating dynamic content in Canvas.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fcc3e7b05j7staqbom4cd.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fcc3e7b05j7staqbom4cd.png" alt="*LambdaTest Bug Smasher*" width="800" height="476"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;As seen above, it is difficult to predict the presence of bugs placed at random locations in the Canvas. In such cases, the best possible solution is to randomize the X and Y coordinate pairs so that the click falls within the Canvas.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Other issues associated with automated testing of Canvas elements include accessibility issues, the need for image-based verification, and limited debugging capabilities.&lt;/em&gt;&lt;/p&gt;

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

&lt;p&gt;Unlike DOM-based automation, automating Canvas elements can be challenging due to their reliance on pixel-based graphics. Test automation frameworks like Selenium, Cypress, Playwright, etc., can be leveraged to automate interactions with Canvas elements on a web page.&lt;/p&gt;

&lt;p&gt;It is essential to adhere to Selenium best practices to minimize test flakiness and ensure consistency in test execution when automating Canvas elements. The learnings of this blog can help you get started with using Selenium and Cypress for Canvas automation — a choice that you should make based on the project &amp;amp; usecase requirements.&lt;/p&gt;

&lt;p&gt;Happy Testing!&lt;/p&gt;

</description>
      <category>automation</category>
      <category>testing</category>
      <category>selenium</category>
      <category>cypress</category>
    </item>
    <item>
      <title>16 Best Practices Of CI/CD Pipelines To Follow In 2024</title>
      <dc:creator>himanshuseth004</dc:creator>
      <pubDate>Tue, 02 Apr 2024 06:21:31 +0000</pubDate>
      <link>https://forem.com/testmuai/16-best-practices-of-cicd-pipelines-to-follow-in-2024-3dkb</link>
      <guid>https://forem.com/testmuai/16-best-practices-of-cicd-pipelines-to-follow-in-2024-3dkb</guid>
      <description>&lt;p&gt;Every software project involves some kind of ‘processes’ &amp;amp; ‘practices’ for successful execution &amp;amp; deployment of the project. As the size &amp;amp; scale of the project increases, the degree of complications also increases in an exponential manner. The leadership team should make every possible effort to develop, test, and release the software in a manner so that the release is done in an incremental manner thereby having minimal (or no) impact on the software already available with the customer.&lt;/p&gt;

&lt;p&gt;In this article, I will learn the best practices of CI/CD pipelines with respect to &lt;a href="https://www.lambdatest.com/automation-testing?utm_source=devto&amp;amp;utm_medium=organic&amp;amp;utm_campaign=apr_02&amp;amp;utm_term=bh&amp;amp;utm_content=webpage" rel="noopener noreferrer"&gt;test automation&lt;/a&gt; to help you pace up your go-to market launch.&lt;/p&gt;

&lt;p&gt;We have already covered CI/CD in one of our previous article on What is &lt;a href="https://www.lambdatest.com/blog/what-is-continuous-integration-and-continuous-delivery/?utm_source=devto&amp;amp;utm_medium=organic&amp;amp;utm_campaign=apr_02&amp;amp;utm_term=bh&amp;amp;utm_content=blog" rel="noopener noreferrer"&gt;Continuous Integrations and Continuous Delivery&lt;/a&gt;? Where we discussed what they are about? Tools of trade involved in the CI/ CD pipeline. Every bug that is uncovered during the testing/verification stage goes through the rigorous process of development, integration, testing &amp;amp; closure. In case this activity is done manually, it could cost a significant amount of man-hours and verification effort, hence it is ideal to have &lt;strong&gt;‘test automation’&lt;/strong&gt; in CI/CD.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Run your &lt;a href="https://www.lambdatest.com/playwright?utm_source=devto&amp;amp;utm_medium=organic&amp;amp;utm_campaign=apr_02&amp;amp;utm_term=bh&amp;amp;utm_content=webpage" rel="noopener noreferrer"&gt;Playwright automatuion&lt;/a&gt; test scripts instantly on 50+ browser and OS combinations using the LambdaTest cloud. &lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2&gt;
  
  
  ‘Deployment Pipeline’ &amp;amp; Role Of Automated Testing in CI/CD
&lt;/h2&gt;

&lt;p&gt;An automated system, called as &lt;strong&gt;Deployment Pipeline&lt;/strong&gt; in CI/CD does the job of automating the testing of the incremental builds that are available on the server. Since the entire process is automated, the overall Turn-around time (TAT) would be significantly lesser when compared to manual testing.&lt;/p&gt;

&lt;p&gt;In most of the scenarios, it is not possible to completely automate the testing. There could be some test scenarios where there is manual intervention required or scenarios where manual observation is required to decide whether the test has passed or not. Though automation is an integral best practice for CI/CD pipeline, identifying test scenarios that would fetch better results if they are automated is considered a pivotal best practice for CI/CD.&lt;/p&gt;

&lt;p&gt;Though there are several benefits of using automation testing, some of the key benefits of using test automation in CI/CD pipeline are below:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;Faster bug closure — Issue Detection &amp;gt; Issue Fix &amp;gt; Issue Closure.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Effective utilization of overall resources in hand i.e. testers, testing infrastructure, etc.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Ability to execute tests in parallel.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Consistency in test planning &amp;amp; execution.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Minimal requirement of technical skills required for automated test-case execution.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  How To Make The Best Out Of Test Automation In CI/CD Pipeline?
&lt;/h2&gt;

&lt;p&gt;While the scope of test automation in CI/CD pipeline may vary from one project to another, there are certain best practices for CI/CD that can be applied to any project, irrespective of its scale &amp;amp; size.&lt;/p&gt;

&lt;h2&gt;
  
  
  Incremental Changes &amp;amp; Timely Communication
&lt;/h2&gt;

&lt;p&gt;Developers can follow a big-bang approach to develop a new feature or fix an issue reported by the test team. This means that the developer will use the option where he will push the feature implementation at one shot. Though the implementation job is done, there are issues with this approach. The major drawback is that it becomes extremely difficult to isolate an issue (if there is a problem with the implementation&lt;/p&gt;

&lt;p&gt;A wiser approach would be to break down the feature into different sub-features and make use of &lt;strong&gt;unique feature flags&lt;/strong&gt; for each feature. This technique not only helps in isolating potential issues but can be instrumental in making incremental feature-builds whenever the need arises. It also reduces the probability of having integration problems when the final feature (which is a combination of many sub-features) is pushed to the main-line/production branch.&lt;/p&gt;

&lt;p&gt;There could be scenarios where there are inter-dependencies between features i.e. feature ‘A’ is dependent on feature ‘B’, in which case either developer has to wait for 100% completion of the feature on which there is a dependency. By making proper use of stubs &amp;amp; feature flags, both the feature developers can avoid the deadlock situation since stubs would be used for implementation which is planned/under progress. Once the required feature is ready, the developer has to get rid of stub-based/dummy implementation which was only a placeholder for implementation of a known interface.&lt;/p&gt;

&lt;p&gt;This development activity requires careful planning and timely interaction between the development teams to make the most out of test automation in CI/CD pipeline. Any kind of indiscipline in following this approach could impact your overall test schedule.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Execute your &lt;a href="https://www.lambdatest.com/playwright?utm_source=devto&amp;amp;utm_medium=organic&amp;amp;utm_campaign=apr_02&amp;amp;utm_term=bh&amp;amp;utm_content=webpage" rel="noopener noreferrer"&gt;playwright automated testing&lt;/a&gt; scripts instantly across over 50 browser and operating system combinations using the LambdaTest cloud platform.&lt;/strong&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2&gt;
  
  
  Identification Of Tests That Can Be Automated
&lt;/h2&gt;

&lt;p&gt;As mentioned earlier, it is unlikely that 100% of the tests can be automated since there would at least be some tests where manual testing would be more effective when compared to automation tests. Since test automation is core to CI/CD pipeline, realizing those &lt;a href="https://www.lambdatest.com/learning-hub/test-case?utm_source=devto&amp;amp;utm_medium=organic&amp;amp;utm_campaign=apr_02&amp;amp;utm_term=bh&amp;amp;utm_content=learning_hub" rel="noopener noreferrer"&gt;test cases&lt;/a&gt; which can be automated is a crucial best practice for CI/CD.&lt;/p&gt;

&lt;p&gt;Two broad categories of tests that can be automated are:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Tests that are executed on a frequent basis:&lt;/strong&gt; If such tests are executed ‘manually’ by testers, it may be prone to errors since the productivity may reduce as the testers have to execute the same test multiple times in a day. Take the case of a web product that has to be tested for &lt;a href="https://www.lambdatest.com/learning-hub/cross-browser-compatibility?utm_source=devto&amp;amp;utm_medium=organic&amp;amp;utm_campaign=apr_02&amp;amp;utm_term=bh&amp;amp;utm_content=learning_hub" rel="noopener noreferrer"&gt;browser compatibility testing&lt;/a&gt;. Some test-cases may involve capturing the screenshot of your web-app while it is being tested on different combinations of browsers/devices/operating systems. Taking a screenshot for each of these combinations could be a tedious task. Hence, automated &lt;a href="https://www.lambdatest.com/online-browser-testing?utm_source=devto&amp;amp;utm_medium=organic&amp;amp;utm_campaign=apr_02&amp;amp;utm_term=bh&amp;amp;utm_content=webpage" rel="noopener noreferrer"&gt;cross browser testing&lt;/a&gt; could free your testers for &lt;a href="https://www.lambdatest.com/blog/17-lessons-i-learned-for-writing-effective-test-cases/?utm_source=devto&amp;amp;utm_medium=organic&amp;amp;utm_campaign=apr_02&amp;amp;utm_term=bh&amp;amp;utm_content=blog" rel="noopener noreferrer"&gt;writing effective test cases&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Tests that require knowledge &amp;amp; have the dependency on specific set of testers:&lt;/strong&gt; Dependency on resources (developers/testers) that only have the domain knowledge required for test execution can be risky if that resource is not available during a critical phase of the project. As only that tester is aware of the pre-requisites and procedure for testing, his absence can impact the overall project deliverables. Such situations can be avoided by incorporating test automation in CI/CD pipeline, so that project deadlines are not adversely affected&lt;/p&gt;

&lt;p&gt;There could be many other scenarios where automation testing can be used and the intent should be to make use of tools &amp;amp; test platforms (which are scalable) that can help you to achieve that audacious goal.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Instantly run your &lt;a href="https://www.lambdatest.com/playwright?utm_source=devto&amp;amp;utm_medium=organic&amp;amp;utm_campaign=apr_02&amp;amp;utm_term=bh&amp;amp;utm_content=webpage" rel="noopener noreferrer"&gt;playwright browser testing&lt;/a&gt; scripts on over 50 browser and operating system combinations using the LambdaTest cloud platform.&lt;/strong&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2&gt;
  
  
  One-Click Migration
&lt;/h2&gt;

&lt;p&gt;The effort to move the code changes to the production environment can be greatly reduced if there is a one-click feature for migrating the code from one application environment to another.&lt;/p&gt;

&lt;p&gt;A well-architected CI/CD pipeline should have one-click migration since it reduces the friction (in code migration) between different operations. Opting for good cloud infrastructure that provides this feature and efficient &amp;amp; elegant usage of test automation could optimize the development &amp;amp; operation process. Pushing your development through a single click is great to aim as a best practice for CI/CD.&lt;/p&gt;

&lt;h2&gt;
  
  
  Exploiting Parallel Testing As A Best Practice For CI/CD Pipeline
&lt;/h2&gt;

&lt;p&gt;Another best practice for CI/CD would be the parallel test execution. Once you have identified the tests that need to be automated, the next step should be to incorporate the factor of ‘parallel execution’ in your test approach. Having automated testing as a best practice for CI/CD pipeline already accelerates the entire process but the results would be much better if it is coupled with Parallel testing. You can have number of automation tests being executed, simultaneously, which can yield results much faster.&lt;/p&gt;

&lt;p&gt;You cannot get the best throughput out of parallel testing if the tests are executed on one single machine. Such a scenario would hog the critical resources on the machine e.g. CPU, GPU, etc. which may slow down the execution of the other tests running on the machine.&lt;/p&gt;

&lt;p&gt;Hence, the infrastructure on which the tests are executed matter a lot. For testing a web-app/website, having an in-house infrastructure for test deployment &amp;amp; execution might not be an ideal solution (in terms of cost &amp;amp; scalability). This is where LambdaTest comes in to help you perform &lt;a href="https://www.lambdatest.com/blog/speed-up-automated-parallel-testing-in-selenium-with-testng/?utm_source=devto&amp;amp;utm_medium=organic&amp;amp;utm_campaign=apr_02&amp;amp;utm_term=bh&amp;amp;utm_content=blog" rel="noopener noreferrer"&gt;parallel testing&lt;/a&gt; with automated testing frameworks such as &lt;a href="https://www.lambdatest.com/learning-hub/testng?utm_source=devto&amp;amp;utm_medium=organic&amp;amp;utm_campaign=apr_02&amp;amp;utm_term=bh&amp;amp;utm_content=learning_hub" rel="noopener noreferrer"&gt;TestNG&lt;/a&gt;, pytest and so on, over an on-cloud online &lt;a href="https://www.lambdatest.com/learning-hub/selenium-grid?utm_source=devto&amp;amp;utm_medium=organic&amp;amp;utm_campaign=apr_02&amp;amp;utm_term=bh&amp;amp;utm_content=learning_hub" rel="noopener noreferrer"&gt;Selenium Grid&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;LambdaTest is an AI-powered test orchestration and execution platform that lets you run manual and automated tests at scale with over 3000+ real devices, browsers and OS combinations.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Debugging websites for Safari before pushing them live is crucial. In this article, learn how to debug websites using &lt;a href="https://www.lambdatest.com/blog/debug-websites-using-safari-developer-tools/?utm_source=devto&amp;amp;utm_medium=organic&amp;amp;utm_campaign=apr_02&amp;amp;utm_term=bh&amp;amp;utm_content=blog" rel="noopener noreferrer"&gt;dev tools in Safari&lt;/a&gt;.&lt;/strong&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2&gt;
  
  
  Learnings From Previously Executed Projects
&lt;/h2&gt;

&lt;p&gt;Every software project has different phases namely — project planning, requirement gathering, implementation, testing, product deployment, etc. There is learning involved at every phase and these learnings can be used for better planning &amp;amp; implementation of the next project. Even if there is a difference in the nature of the projects, you can still make use of some best practices from the past projects so that you can accelerate every phase of the current project. Also, to avoid repeating the mistakes that were committed in other projects.&lt;/p&gt;

&lt;p&gt;You should make a note about the test automation techniques being used for accelerating the process as a best practice to CI/CD. You should involve your team members i.e. developers, testers to look into a possibility where best practices for test automation can be reused so that you &amp;amp; your team do not have to reinvent the wheel. For example, there could be certain test frameworks that were used by them which helped me achieve better test results in a shorter amount of time. Document these learnings as it may help in devising a sound &lt;a href="https://www.lambdatest.com/blog/why-you-need-to-understand-test-management-strategy-to-become-pro/?utm_source=devto&amp;amp;utm_medium=organic&amp;amp;utm_campaign=apr_02&amp;amp;utm_term=bh&amp;amp;utm_content=blog" rel="noopener noreferrer"&gt;test management strategy&lt;/a&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Why Documentation Is An Absolute Necessity?
&lt;/h2&gt;

&lt;p&gt;There are cases where project requirements change/evolve along with the course of project execution. On similar lines, the test automation strategy should be devised in such a manner where you plan keeping the short-term goals &amp;amp; long-term goals in account.&lt;/p&gt;

&lt;p&gt;Though there may be changes to the &lt;a href="https://www.lambdatest.com/learning-hub/test-plan?utm_source=devto&amp;amp;utm_medium=organic&amp;amp;utm_campaign=apr_02&amp;amp;utm_term=bh&amp;amp;utm_content=learning_hub" rel="noopener noreferrer"&gt;test plan&lt;/a&gt; or &lt;a href="https://www.lambdatest.com/learning-hub/test-strategy?utm_source=devto&amp;amp;utm_medium=organic&amp;amp;utm_campaign=apr_02&amp;amp;utm_term=bh&amp;amp;utm_content=learning_hub" rel="noopener noreferrer"&gt;test strategy&lt;/a&gt; over a period of time, the team should at least standardize certain processes. Standardizing could mean short-listing a particular test framework, identifying the cloud infrastructure that suits your budget &amp;amp; requirements, building a competent automation team that work on test scripts (in Python/C#/Java/other programming languages). You should also have a backup plan (Plan-B) in case there are any changes in the schedule or overall business within the organization.&lt;/p&gt;

&lt;p&gt;It is important to have these things documented so that it can be referred at any point in time. Along with the pointers that have been mentioned above, the document should also include a section which highlights the ‘Risks &amp;amp; Assumptions’ associated in the execution of the test strategy.&lt;/p&gt;

&lt;p&gt;For example, the test plan/test strategy would have been created with 4 automation engineers as the resource count; but you may need to scale down the team due to some unforeseen circumstances. Hence, create a &lt;a href="https://www.lambdatest.com/learning-hub/future-proof-testing?utm_source=devto&amp;amp;utm_medium=organic&amp;amp;utm_campaign=apr_02&amp;amp;utm_term=bh&amp;amp;utm_content=learning_hub" rel="noopener noreferrer"&gt;future-proof test&lt;/a&gt; strategy by looking at all different parameters associated with the project.&lt;/p&gt;

&lt;p&gt;The document highlighting the test strategy should be a free-flowing document i.e. updated with important timelines &amp;amp; milestones and should be version-controlled so that revisit the document to track the progress.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Inspect web elements to help developers and testers to debug UI flaws or make modifications in HTML or CSS files. Learn &lt;a href="https://www.lambdatest.com/software-testing-questions/how-to-inspect-on-macbook?utm_source=devto&amp;amp;utm_medium=organic&amp;amp;utm_campaign=apr_02&amp;amp;utm_term=bh&amp;amp;utm_content=stq" rel="noopener noreferrer"&gt;how to inspect on mac&lt;/a&gt;.&lt;/strong&gt; &lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2&gt;
  
  
  Central Repository For Code Development &amp;amp; Maintenance
&lt;/h2&gt;

&lt;p&gt;In any project, there would be number of developers in the team who will push their code which could be a feature implementation or a bug-fix. On the same lines, developers would be executing a pull request to get the latest code changes from the server. Maintaining the source code on central repository is of utmost importance &amp;amp; is considered one of the best practices for CI/CD pipeline. So that the developers can keep their changes up-to-date with the latest source code available on the production server.&lt;/p&gt;

&lt;p&gt;A revision/version control system is also important to track changes, identify differences, and maintaining an environment that eases the task to keep track of the application builds.&lt;/p&gt;

&lt;h2&gt;
  
  
  Rollback With Version Control
&lt;/h2&gt;

&lt;p&gt;There are scenarios where the test team could come across some issues which were not observed in the previous software release; a probable reason could be a side-effect of a fix that is pushed in the release software which is under test. Performing RCA(Root Cause Analysis) could sometimes be time consuming, and you can’t afford to lose a valuable feature for a long time in your production environment.&lt;/p&gt;

&lt;p&gt;In such cases, the developer who pushed that fix should be able to roll-back his changes so that the release is not stalled and he also gets some more time to re-look at his implementation. Without version control system, such a seamless roll-back is not possible. The roll-back is not limited to source code; it can be extended to documents, presentations, flow diagrams, etc.&lt;/p&gt;

&lt;h2&gt;
  
  
  Staging Environment Loosely Mimicking The Production Environment
&lt;/h2&gt;

&lt;p&gt;Irrespective of the version-control tool being used for tracking code changes, usage of development &amp;amp; testing environments is common in all of them. The development team can make use of different ‘development branches’ for a different set of customers, but the code would still be pushed to the Production server/Staging Server. For improved effectiveness, the staging environment should ideally mirror the production environment.&lt;/p&gt;

&lt;p&gt;A common mistake I have observed is related to live traffic. Often Staging environment misses out on live traffic that your production environment has to go through for handling the load. there are many reasons that for staging environment to be identify some of the reasons &lt;a href="https://www.lambdatest.com/blog/13-reasons-why-staging-environment-is-failing-for-your-organization/?utm_source=devto&amp;amp;utm_medium=organic&amp;amp;utm_campaign=apr_02&amp;amp;utm_term=bh&amp;amp;utm_content=blog" rel="noopener noreferrer"&gt;why your staging environment is failing&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;This approach makes it easy for deploying working code from staging server to production server. In fact, developers should also have the flexibility where they can create &amp;amp; setup new environments on the click of a button. There are tools like &lt;a href="https://www.lambdatest.com/learning-hub/jenkins?utm_source=devto&amp;amp;utm_medium=organic&amp;amp;utm_campaign=apr_02&amp;amp;utm_term=bh&amp;amp;utm_content=learning_hub" rel="noopener noreferrer"&gt;Jenkins&lt;/a&gt; which can help to achieve the same. Usage of tools common to the development team &amp;amp; DevOps team would be vital in streamlining the best practices of CI/CD process.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Perform manual or automated cross &lt;a href="https://www.lambdatest.com/?utm_source=devto&amp;amp;utm_medium=organic&amp;amp;utm_campaign=apr_02&amp;amp;utm_term=bh&amp;amp;utm_content=webpage" rel="noopener noreferrer"&gt;browser test&lt;/a&gt; on 3000+ browsers online. Deploy and scale faster with the most powerful cross browser testing tool online.&lt;/strong&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2&gt;
  
  
  Involving Relevant Stakeholders In Test Code Development
&lt;/h2&gt;

&lt;p&gt;It becomes extremely critical to involve the right stakeholders in the test automation planning, development &amp;amp; execution. Since developers in the team can add more value to the test code that is implemented by the test engineers, it would be ideal to involve them in the test case implementation. Developers have a better understanding of the architecture, coding techniques, best practices in software development.&lt;/p&gt;

&lt;p&gt;Experienced QA engineers may have a good understanding of test infrastructure, test frameworks, etc. Hence, developers collaborating with the QA engineers can reduce the effort &amp;amp; time involved in test case development to speed up test automation for CI/CD pipeline.&lt;/p&gt;

&lt;p&gt;In crunch situations, the development team can work on the automation code without involving the test team so that there is no delay in the implementation of automation tests. However, it is advisable to involve the relevant stakeholders, especially QA engineers &amp;amp; developers for the test case implementation.&lt;/p&gt;

&lt;h2&gt;
  
  
  Incorporate Feedback For A Robust Automated Testing Strategy In CI/CD Pipeline
&lt;/h2&gt;

&lt;p&gt;Test strategy document charts a fair plan on how the test automation &amp;amp; other test-related activities would be planned &amp;amp; executed. Along with updating the test strategy (as and when it is required), the feedback loop should also be used for timely updation of code for test automation in CI/CD pipeline.&lt;/p&gt;

&lt;p&gt;The feedback can be used to understand the user’s pain-points &amp;amp; to get finer details about the overall usage of the infrastructure that was provided for automation testing. For example, test automation engineers &amp;amp; DevOps team might have some observations about the infrastructure over a brief period of testing. The observations could be related to methodologies used for capturing logs/porting automation test code implemented in Python &amp;amp; Selenium to the Cloud infrastructure used for testing/performance related to the execution of tests, etc.&lt;/p&gt;

&lt;p&gt;All this feedback should be captured in a document and relevant feedback should be incorporated as a best practice to CI/CD automation pipeline, so that the test code is in-sync with the current requirements.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Explore the top &lt;a href="https://www.lambdatest.com/blog/top-ui-automated-testing-tools/?utm_source=devto&amp;amp;utm_medium=organic&amp;amp;utm_campaign=apr_02&amp;amp;utm_term=bh&amp;amp;utm_content=blog" rel="noopener noreferrer"&gt;UI automation testing&lt;/a&gt; tools, learn what is automation UI testing, the challenges in UI testing, and discover effective solutions in this guide.&lt;/strong&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2&gt;
  
  
  Frequent Code Commits For Micro-Agility In Test Automation For CI/CD Pipeline
&lt;/h2&gt;

&lt;p&gt;Developers begin the development based on the requirements mentioned in the specifications. Once the implementation is complete, the developer performs a unit test on the code and fixes the issues encountered during this round of testing. He should be able to fix issues that are independent of integration since the tests are stand-alone tests. Once the local testing is complete, the developer will push the code to a code repository. In most of the development environments, the code is normally pushed to a ‘development branch’ and once that is approved &amp;amp; tested, it can be pushed to the ‘production branch’.&lt;/p&gt;

&lt;p&gt;Hence, developers should be encouraged &amp;amp; motivated to push the code changes more frequently so that it becomes easy for keeping a track of the changes. This important best practice for CI/CD should be followed diligently by the developers in the team so that you are able to achieve micro-agility for activities related to development &amp;amp; test automation in CI/CD pipeline.&lt;/p&gt;

&lt;h2&gt;
  
  
  Seamless Coordination! Remember, it is CI + CD!
&lt;/h2&gt;

&lt;p&gt;CI &amp;amp; CD are two separate processes but they cannot be termed as inseparable. Any delay or lapse in executing the CI process could hamper the output of the CD process. Though automation can be used in improving the efficiency of the CI/CD process, there are many aspects where automation may not be effective.&lt;/p&gt;

&lt;p&gt;Communication &amp;amp; Collaboration are key pillars if we refer to best practices for CI/CD process since there are multiple teams — product planning, development team, verification team, DevOps team, etc. that have to work in harmony for the success of the project. Hence, benchmarking your CI/CD process with the leaders who are best in those areas is important. And is considered as a critical best practices for CI/CD pipeline to provide keen inputs and suggestions for optimization.&lt;/p&gt;

&lt;p&gt;Also, remember that in any project, there is no activity that can happen in silo since there would always be someone who is dependent on the results of your activity. For this to happen, there needs to be proper communication between stakeholders of the project. For example, your QA engineers might be working on some test case implementation which not be covering all the requirements. If such tests are executed, you may not achieve good test coverage since important scenarios are missed out in the test case/test suite implementation.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Discover the leading &lt;a href="https://www.lambdatest.com/blog/top-ui-automated-testing-tools/?utm_source=devto&amp;amp;utm_medium=organic&amp;amp;utm_campaign=apr_02&amp;amp;utm_term=bh&amp;amp;utm_content=blog" rel="noopener noreferrer"&gt;ui automation testing tools&lt;/a&gt;, unravel the essence of UI testing automation, tackle the challenges it presents, and find pragmatic solutions — all in one comprehensive guide.&lt;/strong&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2&gt;
  
  
  Keep It Simple &amp;amp; Organized By Executing The Smaller Test Cases First!
&lt;/h2&gt;

&lt;p&gt;I have witnessed in many projects that when it comes to testing automation for CI/CD pipeline, we often forget to prioritize our test case in a systematic approach. Here is what I mean! As a best practice of CI/CD, it is always recommended to execute your smaller and simple test cases before your long and complex ones.&lt;/p&gt;

&lt;p&gt;This way you could be sure of individual test case coverage and performance with respect to functionality testing in an isolated manner. Complex test cases where you design test cases to check the interaction between multiple modules can come later!&lt;/p&gt;

&lt;h2&gt;
  
  
  Transparency Within The Team &amp;amp; Across The Teams
&lt;/h2&gt;

&lt;p&gt;Many projects have development &amp;amp; QA teams that are located in different regions. Apart from intense coordination that is required to make sure that every team member is in-sync, better transparency can make things even better. This is where CI can be very effective since it brings the much-needed level of transparency within the key stakeholders in the team.&lt;/p&gt;

&lt;p&gt;With CI in place, the team gets a much better idea about the overall status of the tests and what can be done to improve the efficiency of the tests. This is how it fuels the usage of collective intelligence for the betterment of the team &amp;amp; project. It is imperative to have everyone working on the same page, especially if you are working remotely. Project management tools could be highly effective as a best practice for CI/CD process.&lt;/p&gt;

&lt;p&gt;Using project management tools everyone would vary of the activities performed by other team members, also the deadline by which a task has to be completed. Here are some of the &lt;a href="https://www.lambdatest.com/blog/top-19-collaboration-tools-for-your-software-testing-team/?utm_source=devto&amp;amp;utm_medium=organic&amp;amp;utm_campaign=apr_02&amp;amp;utm_term=bh&amp;amp;utm_content=blog" rel="noopener noreferrer"&gt;collaboration tools&lt;/a&gt; for your software testing team.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;This article is intended for learners, innovators &amp;amp; entrepreneurs who want to learn &lt;a href="https://www.lambdatest.com/blog/all-you-need-to-know-about-automation-testing-life-cycle/?utm_source=devto&amp;amp;utm_medium=organic&amp;amp;utm_campaign=apr_02&amp;amp;utm_term=bh&amp;amp;utm_content=blog" rel="noopener noreferrer"&gt;automation testing life cycle&lt;/a&gt; to enhance the quality of software products.&lt;/strong&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2&gt;
  
  
  Choosing The Right Tool for CI/CD Process
&lt;/h2&gt;

&lt;p&gt;All the above-mentioned tips and actionable insights would help you pace up your test automation using best practices for CI/CD pipeline. Although, in the end, pacing CI/CD pipeline is highly tool dependent. There is a number of tools available for CI/CD, but you should choose the right tool based on your budget, requirements, and experience. Some of the commonly used CI/CD tools are Jenkins, Travis CI, Gitlab, TeamCity, Codeship, Circle CI, etc.&lt;/p&gt;

&lt;p&gt;Before zeroing on any particular tool, we highly recommend that you look at the pros &amp;amp; cons of the tool as any change in the CI tool during the course of development could hamper your deliverables &amp;amp; deadlines.&lt;/p&gt;

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

&lt;p&gt;&lt;em&gt;CI/CD Tools To Integrate With Online Selenium Grid at LambdaTest&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;Talking about CI/CD tools, here is a bonus tip, LambdaTest offers a Selenium grid which is compatible with every CI/CD tool that you may be using, so you could fasten your cross browser testing effort.&lt;/p&gt;

&lt;p&gt;To learn more about the various CI/CD tools follow this guide on &lt;a href="https://www.lambdatest.com/blog/best-ci-cd-tools/?utm_source=devto&amp;amp;utm_medium=organic&amp;amp;utm_campaign=apr_02&amp;amp;utm_term=bh&amp;amp;utm_content=blog" rel="noopener noreferrer"&gt;best CI/CD tools&lt;/a&gt; that will provide you valuable information on selecting the best tool to make your development and testing process better.&lt;/p&gt;

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

&lt;p&gt;Test automation is an integral part of the overall test strategy, but it has to be planned &amp;amp; executed in a careful manner. There are many aspects of test automation (particularly related to infrastructure) where you can smartly use resources in hand without investing in in-house testing infrastructure. Cloud-based testing could simplify your overall test automation strategy due to the reliability &amp;amp; scalability aspects. These enhancements not only motivate the team members, but also let you get the best out of the best practices for CI/CD pipeline.&lt;/p&gt;

</description>
    </item>
    <item>
      <title>Top 28 Selenium WebDriver Commands in NUnit For Test Automation</title>
      <dc:creator>himanshuseth004</dc:creator>
      <pubDate>Tue, 02 Apr 2024 06:17:58 +0000</pubDate>
      <link>https://forem.com/testmuai/top-28-selenium-webdriver-commands-in-nunit-for-test-automation-15o7</link>
      <guid>https://forem.com/testmuai/top-28-selenium-webdriver-commands-in-nunit-for-test-automation-15o7</guid>
      <description>&lt;p&gt;Selenium Framework interacts with the web browser via the Selenium WebDriver commands. It does so with the help of certain Selenium WebDriver commands to automate browser actions such as open, close or maximise the browser window. Also, you can handle different WebElements or drop down elements browser to perform &lt;a href="https://www.lambdatest.com/selenium-automation?utm_source=devto&amp;amp;utm_medium=organic&amp;amp;utm_campaign=apr_02&amp;amp;utm_term=bh&amp;amp;utm_content=webpage" rel="noopener noreferrer"&gt;Selenium test automation&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Different programming languages such as Python, Java,C# etc uses an interface to interact with the browser. In this Nunit tutorial, I’ll be exploring Selenium commands for NUnit, which is a Selenium &lt;a href="https://www.lambdatest.com/automation-testing" rel="noopener noreferrer"&gt;test automation&lt;/a&gt; framework for C# , the interface used through which the browser is controlled is the &lt;em&gt;IWebDriver&lt;/em&gt;.&lt;/p&gt;

&lt;p&gt;In this NUnit tutorial on Selenium WebDriver Commands in NUnit, I’ll take deep dive into various Selenium WebDriver Commands for web browsers, browser elements &amp;amp; drop down. In case you’re not familiar with ‘what is Selenium?’, you can refer to our detailed page on the topic on &lt;a href="https://www.lambdatest.com/automation-testing?utm_source=devto&amp;amp;utm_medium=organic&amp;amp;utm_campaign=apr_02&amp;amp;utm_term=bh&amp;amp;utm_content=webpage" rel="noopener noreferrer"&gt;Selenium 4&lt;/a&gt; that covers all the aspects on what features has been improvised and what functions are deprecated.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Get faster loading times and better user experience with our efficient &lt;a href="https://www.lambdatest.com/free-online-tools/json-stringify?utm_source=devto&amp;amp;utm_medium=organic&amp;amp;utm_campaign=apr_02&amp;amp;utm_term=bh&amp;amp;utm_content=free_online_tools" rel="noopener noreferrer"&gt;JSON Stringify&lt;/a&gt; tool. Quickly compress your JSON data with ease and optimize your website now.&lt;/strong&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2&gt;
  
  
  What are Selenium Webdriver Commands?
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://www.lambdatest.com/learning-hub/webdriver?utm_source=devto&amp;amp;utm_medium=organic&amp;amp;utm_campaign=apr_02&amp;amp;utm_term=bh&amp;amp;utm_content=learning_hub" rel="noopener noreferrer"&gt;Selenium Webdriver&lt;/a&gt; commands are the methods used to run your Selenium test automation scripts. You can use these browser commands to automate different browser actions to perform testing.&lt;/p&gt;

&lt;p&gt;Selenium WebDriver commands in NUnit are divided in three different categories on the basis of different kinds of browser interactions. The commands are categorized based on the type of interaction and the element/application on which action is being performed. &lt;a href="https://www.lambdatest.com/automated-browser-testing?utm_source=devto&amp;amp;utm_medium=organic&amp;amp;utm_campaign=apr_02&amp;amp;utm_term=bh&amp;amp;utm_content=webpage" rel="noopener noreferrer"&gt;Automated browser testing&lt;/a&gt; involves the following Selenium commands:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;&lt;p&gt;*&lt;em&gt;Web Browser Commands *&lt;/em&gt;— Performs actions on the web browser (e.g. open, close, maximize, etc.)&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;*&lt;em&gt;Web Element Commands *&lt;/em&gt;— Performs actions on the WebElements in the current window (e.g. checking a button, entering text in a textbox, clicking a button, etc.)&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;**Drop Down Commands — **Performs actions on the drop-down elements in the current window (e.g. selecting or deselecting a value from drop-down window).&lt;/p&gt;&lt;/li&gt;
&lt;/ol&gt;

&lt;h2&gt;
  
  
  Web Browser Commands
&lt;/h2&gt;

&lt;p&gt;When C# is used along with the Selenium testing framework , the &lt;em&gt;IWebDriver&lt;/em&gt; interface is used to perform actions on the web browser. To access the methods of the &lt;em&gt;IWebDriver&lt;/em&gt; interface, an instance/driver object from &lt;em&gt;IWebDriver&lt;/em&gt; should be created. The complete list of methods for &lt;em&gt;IWebDriver&lt;/em&gt; comes up as soon as you press the dot key.&lt;/p&gt;

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

&lt;p&gt;Below are the Selenium WebDriver Commands in Nunit for handling browsers:&lt;/p&gt;

&lt;h2&gt;
  
  
  1. Url{}
&lt;/h2&gt;

&lt;p&gt;Using this Selenium WebDriver command you can Get or Set the URL/web page that needs to be displayed.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Syntax:&lt;/strong&gt;&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;string IWebDriver.Url { get; set;}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;Example&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;/* Selenium Webdriver command for URL*/
String URL;


driver = new ChromeDriver();
/* Set Operation */
driver.Url = "https://www.lambdatest.com";
/* Get Operation */
URL = driver.Url;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;
&lt;h2&gt;
  
  
  2. Title{}
&lt;/h2&gt;

&lt;p&gt;This Selenium WebDriver command gets the title of the current browser window.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Syntax:&lt;/strong&gt;&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;string IWebDriver.Title{get;}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;Example&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;/* Selenium Webdriver command for getting title */
String page_title;
driver = new ChromeDriver();
driver.Url = "https://www.lambdatest.com";
page_title = driver.Title;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;
&lt;h2&gt;
  
  
  3. PageSource{}
&lt;/h2&gt;

&lt;p&gt;You can get the source of the page that is loaded by the web browser with this Selenium WebDriver command.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Syntax:&lt;/strong&gt;&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;string IWebDriver.PageSource{get;}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;Example&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;/* Selenium Webdriver command for getting page source*/
String page_source;
driver = new ChromeDriver();
driver.Url = "https://www.lambdatest.com";
page_source = driver.PageSource;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;
&lt;h2&gt;
  
  
  4. Quit()
&lt;/h2&gt;

&lt;p&gt;You can quit the driver and close every browser window associated with this Selenium WebDriver command in NUnit.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Syntax:&lt;/strong&gt;&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;void IWebDriver.Quit()
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;Example&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;/* Selenium Webdriver command for URL*/
driver = new ChromeDriver();
driver.Url = "https://www.lambdatest.com";
/* Sleep for 4 seconds after page is displayed */
System.Threading.Thread.Sleep(4000);
/* Quit the window for this NUnit tutorial*/
driver.Quit();
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;
&lt;h2&gt;
  
  
  5. Close()
&lt;/h2&gt;

&lt;p&gt;With &lt;em&gt;close()&lt;/em&gt; Selenium WebDriver command, you can close the current window, quit the browser in case if it is the last window open. In case you want more detailed information on how to &lt;a href="https://www.lambdatest.com/blog/selenium-c-tutorial-handling-multiple-browser-windows/?utm_source=devto&amp;amp;utm_medium=organic&amp;amp;utm_campaign=apr_02&amp;amp;utm_term=bh&amp;amp;utm_content=blog" rel="noopener noreferrer"&gt;handle browser windows&lt;/a&gt;, you can refer to our blog.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Syntax:&lt;/strong&gt;&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;void IWebDriver.Close()
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;Example&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;/* Selenium Webdriver command for Closing window*/
driver = new ChromeDriver();
driver.Url = "https://www.lambdatest.com";
driver.Close();
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;
&lt;h2&gt;
  
  
  6. Navigate()
&lt;/h2&gt;

&lt;p&gt;With this Selenium WebDriver command in NUnit you can navigate the browser to another location i.e. Refresh the current window/Go Back to the previous window/Go to the next page as per browsing history/Go to a particular URL.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.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%2AIr7BvmbvxKNAccbO.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.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%2AIr7BvmbvxKNAccbO.png" width="726" height="389"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Syntax:&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;INavigation IWebDriver.Navigate()
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;Shown below are the different options available with the &lt;em&gt;Navigate()&lt;/em&gt; command:&lt;/p&gt;

&lt;h3&gt;
  
  
  Back()
&lt;/h3&gt;

&lt;p&gt;Move back a single entry as per the browser’s history.&lt;/p&gt;

&lt;p&gt;Syntax:&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;void INavigation.Back()
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;Example:&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;driver.Navigate().Back();
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;
&lt;h3&gt;
  
  
  Forward()
&lt;/h3&gt;

&lt;p&gt;Move a single item forward as per the browser’s history&lt;/p&gt;

&lt;p&gt;Syntax:&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;void INavigation.Forward()
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;Example:&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;driver.Navigate().Forward();
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;
&lt;h3&gt;
  
  
  GoToUrl()
&lt;/h3&gt;

&lt;p&gt;Navigate to a particular URL which is passed as a parameter to the &lt;em&gt;Navigate()&lt;/em&gt; Selenium test automation command&lt;/p&gt;

&lt;p&gt;Syntax:&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;void INavigation.GoToUrl(string url)
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;Example:&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;driver.Navigate().GoToUrl(“https://www.lambdatest.com")
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;
&lt;h3&gt;
  
  
  Refresh()
&lt;/h3&gt;

&lt;p&gt;Refresh the current browser window&lt;/p&gt;

&lt;p&gt;Syntax:&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;void INavigation.Refresh()
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;Example:&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;driver.Navigate().Refresh();
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Online CSS to STYLUS converter to convert &lt;a href="https://www.lambdatest.com/free-online-tools/css-to-stylus?utm_source=devto&amp;amp;utm_medium=organic&amp;amp;utm_campaign=apr_02&amp;amp;utm_term=bh&amp;amp;utm_content=free_online_tools" rel="noopener noreferrer"&gt;CSS to Stylus&lt;/a&gt;. Get clean, readable code instantly. Start converting today!&lt;/strong&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2&gt;
  
  
  Web Element Commands
&lt;/h2&gt;

&lt;p&gt;While performing Selenium test automation with NUnit you’ll often have to rely on Selenium WebDriver command to perform operations on WebElements (e.g. textboxes, radio buttons, hyperlinks, etc.) on a page, that are triggered via the IWebElement interface.&lt;/p&gt;

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

&lt;p&gt;To find the details of a WebElement e.g. &lt;a href="https://www.lambdatest.com/blog/xpath-in-selenium/?utm_source=devto&amp;amp;utm_medium=organic&amp;amp;utm_campaign=apr_02&amp;amp;utm_term=bh&amp;amp;utm_content=blog" rel="noopener noreferrer"&gt;*XPath&lt;/a&gt;*, outerHTML, etc.; you can make use of the Inspect Tool available in web browsers like Chrome, Firefox, etc. Once the WebElement is located, you can perform the corresponding operation on that element. To know more about how to access WebElements, you can refer to our detailed article on &lt;a href="https://www.lambdatest.com/learning-hub/selenium-locators?utm_source=devto&amp;amp;utm_medium=organic&amp;amp;utm_campaign=apr_02&amp;amp;utm_term=bh&amp;amp;utm_content=learning_hub" rel="noopener noreferrer"&gt;Selenium locators&lt;/a&gt;.&lt;/p&gt;

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

&lt;p&gt;Shown below in this Nunit tutorial are the Selenium WebDriver command for WebElements.&lt;/p&gt;

&lt;p&gt;To learn more about WebElements and how to interact with the elements, watch the complete video tutorials and get valuable.&lt;/p&gt;

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

&lt;p&gt;To get started with your web automation journey you can Subscribe to the &lt;a href="https://www.youtube.com/c/LambdaTest?sub_confirmation=1?utm_source=devto&amp;amp;utm_medium=organic&amp;amp;utm_campaign=apr_02&amp;amp;utm_term=bh&amp;amp;utm_content=youtube" rel="noopener noreferrer"&gt;LambdaTest YouTube channel&lt;/a&gt; for details guidance on various &lt;a href="https://www.lambdatest.com/blog/automation-testing-tools/?utm_source=devto&amp;amp;utm_medium=organic&amp;amp;utm_campaign=apr_02&amp;amp;utm_term=bh&amp;amp;utm_content=webpage" rel="noopener noreferrer"&gt;automation testing tools&lt;/a&gt;, &lt;a href="https://www.lambdatest.com/selenium-automation?utm_source=devto&amp;amp;utm_medium=organic&amp;amp;utm_campaign=apr_02&amp;amp;utm_term=bh&amp;amp;utm_content=webpage" rel="noopener noreferrer"&gt;Selenium testing&lt;/a&gt;, &lt;a href="https://www.lambdatest.com/blog/cypress-test-automation-framework/?utm_source=devto&amp;amp;utm_medium=organic&amp;amp;utm_campaign=apr_02&amp;amp;utm_term=bh&amp;amp;utm_content=blog" rel="noopener noreferrer"&gt;Cypress testing&lt;/a&gt;, &lt;a href="https://www.lambdatest.com/playwright?utm_source=devto&amp;amp;utm_medium=organic&amp;amp;utm_campaign=apr_02&amp;amp;utm_term=bh&amp;amp;utm_content=webpage" rel="noopener noreferrer"&gt;Playwright testing&lt;/a&gt;, and more.&lt;/p&gt;

&lt;p&gt;For demonstrating the usage of the WebElement commands, we have used the the demo sites given below as for Menu system and HTML form example&lt;/p&gt;

&lt;p&gt;Identify locators and perform seamless automation testing on the cloud platform &lt;a href="https://accounts.lambdatest.com/register?utm_source=devto&amp;amp;utm_medium=organic&amp;amp;utm_campaign=apr_02&amp;amp;utm_term=bh&amp;amp;utm_content=webpage" rel="noopener noreferrer"&gt;Try LambdaTest Now&lt;/a&gt;!&lt;/p&gt;

&lt;h2&gt;
  
  
  1. Click
&lt;/h2&gt;

&lt;p&gt;Using the &lt;em&gt;Click()&lt;/em&gt; Selenium WebDriver command, you can click on a particular WebElement present on the web page. The Click operation can be performed on hyperlinks, buttons, checkboxes, or any other element that is clickable on the page. The element is first located on the page, after which the Click operation is performed.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Syntax:&lt;/strong&gt;&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;void IWebElement.Click()
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;Example&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;/*Selenium Webdriver command for click*/
driver = new ChromeDriver();
driver.Url = "https://www.lambdatest.com";
/* The Xpath of the Login Button on LambdaTest homepage */
IWebElement web_element = driver.FindElement(By.XPath("//*[@id="navbarSupportedContent"]/ul/li[6]/a"));
web_element.Click();
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;
&lt;h2&gt;
  
  
  2. Enabled
&lt;/h2&gt;

&lt;p&gt;You can check whether a particular WebElement is enabled or not with the Enabled Selenium WebDriver command. It returns true if the WebElement on the page is enabled else it returns false.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Syntax:&lt;/strong&gt;&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Bool IWebElement.Enabled{get;}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;Example&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;/*Selenium Webdriver command to check whether a particular web element is enabled */
driver = new ChromeDriver();
driver.Url = "https://www.lambdatest.com";
/* The Xpath of the Login Button on LambdaTest homepage */
IWebElement web_element = driver.FindElement(By.XPath("//*[@id="navbarSupportedContent"]/ul/li[6]/a"));
Boolean element_enabled = web_element.Enabled;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;
&lt;h2&gt;
  
  
  3. Displayed
&lt;/h2&gt;

&lt;p&gt;This Selenium WebDriver command checks whether a particular WebElement is displayed or not. It returns true if the WebElement on the page is displayed else it returns false.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Syntax:&lt;/strong&gt;&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Bool IWebElement.Displayed{get;}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;Example&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;/*Selenium Webdriver command to check whether a particular web element is displayed */
driver = new ChromeDriver();
driver.Url = "https://www.lambdatest.com";
/* The Xpath of the Login Button on LambdaTest homepage */
IWebElement web_element = driver.FindElement(By.XPath("//*[@id="navbarSupportedContent"]/ul/li[6]/a"));
Boolean element_enabled = web_element.Displayed;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;
&lt;h2&gt;
  
  
  4. Clear
&lt;/h2&gt;

&lt;p&gt;The &lt;em&gt;Clear()&lt;/em&gt; Selenium WebDriver command clears the content in the WebElement present on the page. It is used for clearing the pre-loaded contents in a text box.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Syntax:&lt;/strong&gt;&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;void IWebElement.Clear()
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;Example&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;driver = new ChromeDriver();
driver.Url = "https://www.lambdatest.com";
/* The Xpath of the Email address text box on LambdaTest homepage */
IWebElement web_element = driver.FindElement(By.XPath("//*[@id='useremail']");
web_element.Clear();
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;
&lt;h2&gt;
  
  
  5. SendKeys
&lt;/h2&gt;

&lt;p&gt;This Selenium Webdriver command Inputs the values in an element like a textbox on the web page.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Syntax:&lt;/strong&gt;&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;void IWebElement.SendKeys(string text)
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;Example&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;driver = new ChromeDriver();
driver.Url = "https://www.lambdatest.com";
/* The Xpath of the Email address text box on LambdaTest homepage */
IWebElement web_element = driver.FindElement(By.XPath("//*[@id='useremail']");
web_element.SendKeys("test@email.com");
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;
&lt;h2&gt;
  
  
  6. GetAttribute
&lt;/h2&gt;

&lt;p&gt;There may be attributes and properties associated to WebElements present on a web page. These are added by the website developer. With &lt;em&gt;GetAttribute()&lt;/em&gt; you get the value of a certain attribute as a string of the WebElement.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Syntax:&lt;/strong&gt;&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;string IWebElement.GetAttribute(string attributename)
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;Example&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;driver = new ChromeDriver();
driver.Url = "https://www.lambdatest.com";
/* The Xpath of the Email address text box on LambdaTest homepage */
IWebElement web_element = driver.FindElement(By.XPath("//*[@id='navbarSupportedContent']/ul/li[6]/a"));
String titleValue = web_element.GetAttribute("class");
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;
&lt;h2&gt;
  
  
  7. GetCssValue
&lt;/h2&gt;

&lt;p&gt;Gets the value of a CSS property of a WebElement present on the web page. Color values are returned in the form of rgba string. For example, a “background-color” property set as “green” in the HTML source, will return “#008000” for its value.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Syntax:&lt;/strong&gt;&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;string IWebElement.GetCssValue(string propertyname)
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;Example:&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;driver = new ChromeDriver();
driver.Url = "https://www.lambdatest.com";
/* The Xpath of the Login Button on LambdaTest homepage */
IWebElement web_element = driver.FindElement(By.XPath("//*[@id='navbarSupportedContent']/ul/li[6]/a"));
/* returns #000000 */
String cssvalue = web_element.GetCssValue("background-color");
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;To get demonstrate the functionality of &lt;em&gt;GetCssValue()&lt;/em&gt; we will be performing inspection on platform called LambdaTest. In this example we will get the background color of the ‘Login’ button on LambdaTest homepage, we check the properties associated with the element using Inspect tool.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.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%2A0BJoqT1OKB5OnNlg.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.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%2A0BJoqT1OKB5OnNlg.png" width="512" height="267"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;As seen in the snapshot, background-color is the property associated with Login button. The same is used with &lt;em&gt;GetCssValue()&lt;/em&gt;.&lt;/p&gt;

&lt;p&gt;LambdaTest is an AI-powered test orchestration and execution platform that lets you run manual and automated tests at scale with over 3000+ real devices, browsers and OS combinations. This platform allows you to perform web and mobile automation without having to worry about maintain infrastructure, security of your test data and more.&lt;/p&gt;

&lt;h2&gt;
  
  
  8. Submit
&lt;/h2&gt;

&lt;p&gt;Performs a click on a button which is of the type submit.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Syntax:&lt;/strong&gt;&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;void IWebElement.Submit()
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;Example&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;driver = new ChromeDriver();
driver.Url = "https://accounts.lambdatest.com/login";
/* The Xpath of the Login Button on which is of the type submit */
IWebElement web_element = driver.FindElement(By.XPath("//*[@id='app']/section/form/div/div/button"));
web_element.Submit();
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;
&lt;h2&gt;
  
  
  9. Text
&lt;/h2&gt;

&lt;p&gt;Gets the innerText of a WebElement, without any leading or trailing whitespace &amp;amp; other whitespaces collapsed.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Syntax:&lt;/strong&gt;&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;string IWebElement.Text{get;}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;Example&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;driver = new ChromeDriver();
driver.Url = "https://www.lambdatest.com";
/* The Xpath of the Login Button on LambdaTest homepage */
IWebElement web_element = driver.FindElement(By.XPath("//*[@id='navbarSupportedContent']/ul/li[6]/a"));
String text_value = web_element.Text;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;
&lt;h2&gt;
  
  
  10. TagName
&lt;/h2&gt;

&lt;p&gt;Gets the tag name of a WebElement on the web page.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Syntax:&lt;/strong&gt;&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;string IWebElement.TagName{get;}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;Example&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;driver = new ChromeDriver();
driver.Url = "https://www.lambdatest.com";
/* The Xpath of the Login Button on LambdaTest homepage */
IWebElement web_element = driver.FindElement(By.XPath("//*[@id='navbarSupportedContent']/ul/li[6]/a"));
String tag_name = web_element.TagName;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;
&lt;h2&gt;
  
  
  11. Selected
&lt;/h2&gt;

&lt;p&gt;Checks whether a WebElement is selected or not. The WebElement can be radio button, checkbox, etc.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Syntax:&lt;/strong&gt;&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;bool IWebElement.Selected{get;}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;Example&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;driver = new ChromeDriver();
driver.Url = "https://lambdatest.github.io/sample-todo-app/";
IWebElement web_element = driver.FindElement(By.XPath("/html/body/div/div/div/ul/li[1]/input"));
bool selected_status = web_element.Selected;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;
&lt;h2&gt;
  
  
  12. Size
&lt;/h2&gt;

&lt;p&gt;Gets the height &amp;amp; width associated with a WebElement or the current browser window. The return type is of type struct size.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Syntax:&lt;/strong&gt;&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Size IWebElement.Size{get;}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;Example&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;driver = new ChromeDriver();
driver.Url = "https://www.lambdatest.com";
/* Get the window size */
Console.WriteLine(driver.Manage().Window.Size);
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;
&lt;h2&gt;
  
  
  Drop Down Commands
&lt;/h2&gt;

&lt;p&gt;Drop down elements are common in web pages, especially when a user is registering on a website and has to enter the country/city related information. In C#, drop down operations are performed using the &lt;em&gt;SelectElement&lt;/em&gt; class. &lt;em&gt;SelectElement&lt;/em&gt; is declared in using &lt;em&gt;OpenQA.Selenium.Support.UI&lt;/em&gt; package which is imported before performing any operation on the drop down box.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.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%2Ai-m7bMtPlvJ_dW-Y.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.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%2Ai-m7bMtPlvJ_dW-Y.png" width="761" height="398"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;em&gt;For demonstrating the usage of the WebElement commands, we have used the test URLs Menu/a&amp;gt; and formex.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;Below are drop down operations in C#:&lt;/p&gt;
&lt;h2&gt;
  
  
  1. SelectByIndex
&lt;/h2&gt;

&lt;p&gt;Select a value based on the index value. The starting index in drop down is 0.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Syntax:&lt;/strong&gt;&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;void SelectElement.SelectByIndex(int index)
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;Example&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;driver = new ChromeDriver();

driver.Url = "http://demos.dojotoolkit.org/dijit/tests/test_Menu.html";
IWebElement element = driver.FindElement(By.XPath("//select[@aria-label='select']"));
SelectElement select_elem = new SelectElement(element);

/* Sleep for 4 seconds after page is displayed */
System.Threading.Thread.Sleep(4000);

select_elem.SelectByIndex(1);
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;
&lt;h2&gt;
  
  
  2. DeselectByIndex
&lt;/h2&gt;

&lt;p&gt;Deselect a selected value based on the index.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Syntax:&lt;/strong&gt;&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;void SelectElement.DeselectByIndex(int index)
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;Example&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;driver = new ChromeDriver();

driver.Url = "http://demos.dojotoolkit.org/dijit/tests/test_Menu.html";
IWebElement element = driver.FindElement(By.XPath("//select[@aria-label='select']"));
SelectElement select_elem = new SelectElement(element);

/* Sleep for 4 seconds after page is displayed */
System.Threading.Thread.Sleep(4000);

select_elem.DeselectByIndex(1);
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;
&lt;h2&gt;
  
  
  3. SelectByText
&lt;/h2&gt;

&lt;p&gt;Select an option based on the text of the options available in the drop down. There is an option to do partial matching of the text and by default partial matching is False.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Syntax:&lt;/strong&gt;&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;void SelectElement.SelectByText(string text, [bool partialMatch = false])
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;Example&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;driver = new ChromeDriver();

driver.Url = "http://demos.dojotoolkit.org/dijit/tests/test_Menu.html";
IWebElement element = driver.FindElement(By.XPath("//select[@aria-label='select']"));
SelectElement select_elem = new SelectElement(element);

/* Sleep for 4 seconds after page is displayed */
System.Threading.Thread.Sleep(4000);

select_elem.SelectByText("on IE6");
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;
&lt;h2&gt;
  
  
  4. DeselectByText
&lt;/h2&gt;

&lt;p&gt;Deselect an already selected option based on the text of the options available in the drop down.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Syntax:&lt;/strong&gt;&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;void SelectElement.DeselectByText(string text)
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;Example&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;driver = new ChromeDriver();

driver.Url = "http://demos.dojotoolkit.org/dijit/tests/test_Menu.html";
IWebElement element = driver.FindElement(By.XPath("//select[@aria-label='select']"));
SelectElement select_elem = new SelectElement(element);

/* Sleep for 4 seconds after page is displayed */
System.Threading.Thread.Sleep(4000);

select_elem.DeselectByText("on IE6");
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;
&lt;h2&gt;
  
  
  5. SelectByValue
&lt;/h2&gt;

&lt;p&gt;Select an option based on the value supplied as input.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Syntax:&lt;/strong&gt;&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;void SelectElement.SelectByValue(string value)
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;Example&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;driver = new ChromeDriver();

driver.Url = "http://www.tizag.com/phpT/examples/formex.php";

IWebElement element = driver.FindElement(By.XPath("//*[@id='examp']/form/select[1]"));
SelectElement select_elem = new SelectElement(element);

/* Sleep for 4 seconds after page is displayed */
System.Threading.Thread.Sleep(4000);

select_elem.SelectByValue("HighSchool");
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;
&lt;h2&gt;
  
  
  6. DeselectByValue
&lt;/h2&gt;

&lt;p&gt;Deselect an already selected option based on the value supplied as input.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Syntax:&lt;/strong&gt;&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;void SelectElement.DeselectByValue(string value)
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;Example&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;driver = new ChromeDriver();

driver.Url = "http://www.tizag.com/phpT/examples/formex.php";

IWebElement element = driver.FindElement(By.XPath("//*[@id='examp']/form/select[1]"));
SelectElement select_elem = new SelectElement(element);

/* Sleep for 4 seconds after page is displayed */
System.Threading.Thread.Sleep(4000);

select_elem.DeselectByValue("HighSchool");
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;
&lt;h2&gt;
  
  
  7. DeselectAll
&lt;/h2&gt;

&lt;p&gt;Used to clear the selected options in multi select drop down menus.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Syntax:&lt;/strong&gt;&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;void SelectElement.DeselectAll()
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;Example&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;driver = new ChromeDriver();
driver.Url = "http://www.example-site.com";
IWebElement element = driver.FindElement(By.XPath("x-path"));
SelectElement select_elem = new SelectElement(element);
select_elem.DeselectAll();
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;
&lt;h2&gt;
  
  
  8. IsMultiple
&lt;/h2&gt;

&lt;p&gt;This Selenium Webdriver command gets a value showing if the parent element supports multiple selections. If the drop down is multi select capable it returns true else otherwise it returns false.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Syntax:&lt;/strong&gt;&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;bool SelectElement.IsMultiple{get;}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;Example&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;driver = new ChromeDriver();

driver.Url = "http://demos.dojotoolkit.org/dijit/tests/test_Menu.html";
IWebElement element = driver.FindElement(By.XPath("//select[@aria-label='select']"));

SelectElement select_elem = new SelectElement(element);
bool is_multi_select = select_elem.IsMultiple;
Console.WriteLine("Multi Select {0}", is_multi_select);
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;
&lt;h2&gt;
  
  
  9. Options
&lt;/h2&gt;

&lt;p&gt;You can get the list of options for the select element with this Selenium WebDriver command.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Syntax:&lt;/strong&gt;&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;IList&amp;lt;IWebElement&amp;gt; SelectElement.Options{ get; }
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;Example&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
using System.Drawing;
using NUnit.Framework;
using OpenQA.Selenium;
using OpenQA.Selenium.Chrome;
using OpenQA.Selenium.Support.UI;

namespace NUnitDemo
{
    class NUnit_Demo
    {
        IWebDriver driver;

        [Test]
        public void cssDemo()
        {
            driver = new ChromeDriver();
            driver.Url = "http://demos.dojotoolkit.org/dijit/tests/test_Menu.html";

            IWebElement element = driver.FindElement(By.XPath("//select[@aria-label='select']"));

            SelectElement select_elem = new SelectElement(element);
            /* Sleep for 4 seconds after page is displayed */
            System.Threading.Thread.Sleep(4000);

            select_elem.SelectByText("on IE6");

            /* List will contain details of all the options */
            IList&amp;lt;IWebElement&amp;gt; avail_options = select_elem.Options;

            int ListSize = avail_options.Count;

            for (int loop_var = 0; loop_var &amp;lt; ListSize; loop_var++)
            {
                String text_value = select_elem.Options.ElementAt(loop_var).Text;
                System.Diagnostics.Debug.WriteLine("Selected item is " + text_value);
            }

        }

        driver.Close();
    }
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;
&lt;h2&gt;
  
  
  10. AllSelectedOptions
&lt;/h2&gt;

&lt;p&gt;Gets all of the selected options within the select element. It is similar to *Options *except that it should be used on a multi select drop down menu.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Syntax:&lt;/strong&gt;&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;IList&amp;lt;IWebElement&amp;gt; AllSelectedOptions { get; }
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;Example&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;£
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
using System.Drawing;
using NUnit.Framework;
using OpenQA.Selenium;
using OpenQA.Selenium.Chrome;
using OpenQA.Selenium.Support.UI;

namespace NUnitDemo
{
    class NUnit_Demo
    {
        IWebDriver driver;

        [Test]
        public void cssDemo()
        {
            driver = new ChromeDriver();
            driver.Url = "http://demos.dojotoolkit.org/dijit/tests/test_Menu.html";

            IWebElement element = driver.FindElement(By.XPath("//select[@aria-label='select']"));

            SelectElement select_elem = new SelectElement(element);
            /* Sleep for 4 seconds after page is displayed */
            System.Threading.Thread.Sleep(4000);

            select_elem.SelectByText("on IE6");

            /* List will contain details of all the options */
            IList&amp;lt;IWebElement&amp;gt; avail_options = select_elem.AllSelectedOptions;

            int ListSize = avail_options.Count;
            System.Diagnostics.Debug.WriteLine("List size is " + ListSize);

            for (int loop_var = 0; loop_var &amp;lt; ListSize; loop_var++)
            {
                String text_value = avail_options.ElementAt(loop_var).Text;
                System.Diagnostics.Debug.WriteLine("Selected item is " + text_value);
            }

        }
        driver.Close();
    }
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Tired of manually creating JSON data? Generate random JSON data in seconds with our easy-to-use tool. Try &lt;a href="https://www.lambdatest.com/free-online-tools/random-json-generator?utm_source=devto&amp;amp;utm_medium=organic&amp;amp;utm_campaign=apr_02&amp;amp;utm_term=bh&amp;amp;utm_content=free_online_tools" rel="noopener noreferrer"&gt;random json generator&lt;/a&gt; now and make your development process a breeze!&lt;/strong&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2&gt;
  
  
  It’s A Wrap!
&lt;/h2&gt;

&lt;p&gt;The &lt;em&gt;IWebDriver&lt;/em&gt; interface is the base on which the browser interactions for Selenium test automation happens. In this NUnit tutorial, I had a look at the most widely used Selenium WebDriver commands in Nunit for automated browser testing.&lt;/p&gt;

&lt;p&gt;Using these Selenium WebDriver Tutorial commands in Nunit, you can interact with different types (and categories) of WebElements such as text boxes, drop-downs, radio buttons, and more. This forms the basis of automated browser testing with Selenium, C#, and NUnit framework.&lt;/p&gt;

&lt;p&gt;I hope you found this NUnit tutorial informative, In case of any questions feel free to t reach out to us in the comment section down below. That’s all for now. &lt;strong&gt;Happy Testing!!!&lt;/strong&gt; ?&lt;/p&gt;

</description>
      <category>webdev</category>
      <category>beginners</category>
      <category>tutorial</category>
      <category>productivity</category>
    </item>
    <item>
      <title>Pyppeteer Tutorial: The Ultimate Guide to Using Puppeteer with Python</title>
      <dc:creator>himanshuseth004</dc:creator>
      <pubDate>Mon, 05 Feb 2024 07:56:26 +0000</pubDate>
      <link>https://forem.com/testmuai/pyppeteer-tutorial-the-ultimate-guide-to-using-puppeteer-with-python-1621</link>
      <guid>https://forem.com/testmuai/pyppeteer-tutorial-the-ultimate-guide-to-using-puppeteer-with-python-1621</guid>
      <description>&lt;p&gt;Pyppeteer, a Python library used for web browser automation, is a valuable resource in the testing world. It serves as the unofficial counterpart to &lt;a href="https://www.lambdatest.com/puppeteer?utm_source=devto&amp;amp;utm_medium=organic&amp;amp;utm_campaign=feb_05&amp;amp;utm_term=bh&amp;amp;utm_content=webpage" rel="noopener noreferrer"&gt;Puppeteer&lt;/a&gt;, a renowned JavaScript library. By leveraging Pyppeteer, users can manipulate web browsers, automate tasks, and extract data from websites using Python.&lt;/p&gt;

&lt;p&gt;This Pyppeteer tutorial will walk you through the installation process and will cover all the major scenarios that can be automated with code examples.&lt;/p&gt;

&lt;p&gt;So, let’s get started!&lt;/p&gt;

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

&lt;p&gt;Pyppeteer, a Python library, is an unofficial Python port of Puppeteer (a Node.js Chromium browser automation library). Pyppeteer APIs can be leveraged for automating interaction with web elements, &lt;a href="https://www.lambdatest.com/blog/unit-testing-frameworks/?utm_source=devto&amp;amp;utm_medium=organic&amp;amp;utm_campaign=feb_05&amp;amp;utm_term=bh&amp;amp;utm_content=blog" rel="noopener noreferrer"&gt;scraping content from dynamic websites&lt;/a&gt;, handling iFrames, and more.&lt;/p&gt;

&lt;p&gt;Though the last released version of Pyppeteer was 1.0.2, on Jan 11, 2022, the QA community still uses the library for web automation on the Chromium browser on the local machine (or cloud grid). Pyppeteer can be used with pytest and PyUnit (Python’s default &lt;a href="https://www.lambdatest.com/blog/unit-testing-frameworks/?utm_source=devto&amp;amp;utm_medium=organic&amp;amp;utm_campaign=feb_05&amp;amp;utm_term=bh&amp;amp;utm_content=blog" rel="noopener noreferrer"&gt;unit testing framework)&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;**&lt;em&gt;Note: **Please replace pip3 with pip depending on the pip version installed on your machine. Also, function and method are used interchangeably throughout the blog.&lt;/em&gt;&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;a href="https://www.lambdatest.com/remote-test-lab?utm_source=devto&amp;amp;utm_medium=organic&amp;amp;utm_campaign=feb_05&amp;amp;utm_term=bh&amp;amp;utm_content=webpage" rel="noopener noreferrer"&gt;**Remote Test device&lt;/a&gt; Lab enables efficient software testing on various devices remotely. Click to explore and enhance your development process now!**&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2&gt;
  
  
  How to install Pyppeteer?
&lt;/h2&gt;

&lt;p&gt;As stated in the Pyppeteer installation guide, Pyppeteer requires Python 3.6 (or above) installed on the machine. As a good practice, we would install Pyppeteer in a virtual environment &lt;em&gt;(venv)&lt;/em&gt; to isolate it from the packages in the base environment.&lt;/p&gt;

&lt;p&gt;When writing this blog, the latest version of Pyppeteer is 1.0.2.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.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%2AqueIGso65KyTRysG.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.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%2AqueIGso65KyTRysG.png" width="636" height="106"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Run the commands &lt;em&gt;virtualenv venv&lt;/em&gt; and &lt;em&gt;source venv/bin/activate&lt;/em&gt; on the terminal to create the virtual environment.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.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%2A5T0rZqBglRd2TCSg.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.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%2A5T0rZqBglRd2TCSg.png" width="800" height="135"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Now that the virtual environment is ready, let’s install Pyppeteer using either of the two ways mentioned below:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Run the command &lt;em&gt;pip3 install pyppeteer&lt;/em&gt; (or &lt;em&gt;pip install pyppeteer&lt;/em&gt;) on the terminal&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;&lt;a href="https://media2.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%2AVm0OwEJ92RrYqCKh.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.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%2AVm0OwEJ92RrYqCKh.png" width="800" height="400"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;As seen above, Pyppeteer (v 1.0.2) was successfully installed in the virtual environment (venv).&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;The latest version of Pyppeteer, i.e., 1.0.2, can also be installed by executing &lt;em&gt;pip3 install -U git+&lt;a href="https://github.com/pyppeteer/pyppeteer@dev" rel="noopener noreferrer"&gt;https://github.com/pyppeteer/pyppeteer@dev&lt;/a&gt;&lt;/em&gt; on the terminal.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;&lt;a href="https://media2.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%2Ab0xeEzABAoniwEuF.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.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%2Ab0xeEzABAoniwEuF.png" width="800" height="216"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;If Pyppeteer is run for the first time and no Chromium is in the machine, it downloads and installs the latest version.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.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%2AEdIuWJNo6dMmMZUn.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.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%2AEdIuWJNo6dMmMZUn.png" width="800" height="54"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Pyppeteer also lets you use a specified version of Chromium. The Default value can be checked using &lt;em&gt;pyppeteer.&lt;strong&gt;chromium_revision&lt;/strong&gt;&lt;/em&gt;, which is 588429 in our case.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.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%2ADmIDhT1STcmxM64i.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.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%2ADmIDhT1STcmxM64i.png" width="800" height="394"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.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%2AF22VLTuFjuqrvB6b.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.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%2AF22VLTuFjuqrvB6b.png" width="800" height="400"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;As seen below, it downloaded &amp;amp; installed Chromium (v 71.0) on the machine.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.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%2AvonT3XHgBFAX9aGc.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.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%2AvonT3XHgBFAX9aGc.png" width="800" height="415"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;In further sections of the blog, we will demonstrate using the &lt;em&gt;$PYPPETEER_CHROMIUM_REVISION&lt;/em&gt; environment variable to use a certain Chromium version.&lt;/p&gt;

&lt;p&gt;Now that the Pyppeteer setup is complete, let’s look at some prominent test scenarios (or use cases) that can be automated using Pyppeteer.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;This article explains the &lt;a href="https://www.lambdatest.com/blog/emulator-vs-simulator-vs-real-device/?utm_source=devto&amp;amp;utm_medium=organic&amp;amp;utm_campaign=feb_05&amp;amp;utm_term=bh&amp;amp;utm_content=blog" rel="noopener noreferrer"&gt;emulator vs simulator&lt;/a&gt; vs real device differences, the learning of which can help you select the right mobile testing solution for your business.&lt;/strong&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2&gt;
  
  
  Demonstration: Web automation with Pyppeteer
&lt;/h2&gt;

&lt;p&gt;For demonstration of major test scenarios, we have used Pyppeteer in conjunction with the pytest framework. This choice is primarily due to support for &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=feb_05&amp;amp;utm_term=bh&amp;amp;utm_content=blog" rel="noopener noreferrer"&gt;pytest fixtures&lt;/a&gt;, test parameterization, and more.&lt;/p&gt;

&lt;h2&gt;
  
  
  Directory Structure
&lt;/h2&gt;

&lt;p&gt;As seen in the project structure, the configuration and execution are driven via a &lt;em&gt;Makefile&lt;/em&gt;. All the relevant test scenarios (or use cases) that demonstrate the usage of Pyppeteer APIs are separated into different folders.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.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%2Avt_pCufJhMEzxSne.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.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%2Avt_pCufJhMEzxSne.png" width="558" height="986"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Here is a closer look at the directory structure:&lt;/p&gt;

&lt;h2&gt;
  
  
  Configuration (or setup) files
&lt;/h2&gt;

&lt;p&gt;The conftest.py file in pytest contains the implementation related to fixtures, hooks, and other configuration options used in the test code. Based on the value of the environment variable &lt;em&gt;EXEC_PLATFORM&lt;/em&gt;, Pyppeteer tests will be executed on the local Chromium browser or on Chrome (which is built on Chromium) on a cloud grid like LambdaTest.&lt;/p&gt;

&lt;p&gt;LambdaTest is an AI-powered test orchestration and execution platform that allows you to perform &lt;a href="https://www.lambdatest.com/puppeteer?utm_source=devto&amp;amp;utm_medium=organic&amp;amp;utm_campaign=feb_05&amp;amp;utm_term=bh&amp;amp;utm_content=webpage" rel="noopener noreferrer"&gt;Puppeteer testing&lt;/a&gt; using Python on an &lt;a href="https://www.lambdatest.com/online-browser-farm?utm_source=devto&amp;amp;utm_medium=organic&amp;amp;utm_campaign=feb_05&amp;amp;utm_term=bh&amp;amp;utm_content=webpage" rel="noopener noreferrer"&gt;online browser farm&lt;/a&gt; spanning 50+ browser versions on the cloud-based testing infrastructure, ensuring efficient &lt;a href="https://www.lambdatest.com/automation-testing?utm_source=devto&amp;amp;utm_medium=organic&amp;amp;utm_campaign=feb_05&amp;amp;utm_term=bh&amp;amp;utm_content=webpage" rel="noopener noreferrer"&gt;automation testing&lt;/a&gt; using the Pyppeteer library.&lt;/p&gt;

&lt;p&gt;With LambdaTest, you can also perform parallel testing at scale and accelerate software release cycles by multiple folds.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.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%2A6KA2rzidvU9epvTM.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.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%2A6KA2rzidvU9epvTM.png" width="800" height="400"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  Fixture: Creation of a new browser instance
&lt;/h3&gt;

&lt;p&gt;The scope of the pytest fixture (i.e., &lt;em&gt;@pytest.fixture&lt;/em&gt;) is set to function. Hence, the fixture is set up and torn down before &amp;amp; after executing a test function (or method). If &lt;strong&gt;&lt;em&gt;EXEC_PLATFORM&lt;/em&gt;&lt;/strong&gt; is set to &lt;strong&gt;&lt;em&gt;local&lt;/em&gt;&lt;/strong&gt;, the Chromium browser on the local machine is instantiated using the &lt;em&gt;launch()&lt;/em&gt; method in Pyppeteer.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.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%2AQRHtLuxbYVr0nDdN.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.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%2AQRHtLuxbYVr0nDdN.png" width="800" height="425"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The &lt;em&gt;launch()&lt;/em&gt; method is a part of the &lt;em&gt;pyppeteer.launcher&lt;/em&gt; module that is used to launch an instance of Chrome browser in headless mode (default). Since we intend to run the tests in non-headless mode, we set the &lt;em&gt;headless&lt;/em&gt; parameter to &lt;em&gt;False&lt;/em&gt;. The &lt;em&gt;[‘–start-maximized’]&lt;/em&gt; argument starts the browser in a maximized window.&lt;/p&gt;

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

&lt;p&gt;If &lt;strong&gt;&lt;em&gt;EXEC_PLATFORM&lt;/em&gt;&lt;/strong&gt; is set to &lt;strong&gt;&lt;em&gt;cloud&lt;/em&gt;&lt;/strong&gt;, the Chrome browser on the LambdaTest grid is instantiated using the &lt;em&gt;connect()&lt;/em&gt; method in Pyppeteer. In the demonstration, we used the WebSocket endpoint (&lt;em&gt;browserWSEndpoint&lt;/em&gt;) that points to &lt;em&gt;wss://cdp.lambdatest.com/puppeteer&lt;/em&gt;. We have used the Endpoint from the &lt;a href="https://github.com/LambdaTest/puppeteer-sample/blob/main/puppeteer-parallel.js#L7" rel="noopener noreferrer"&gt;Puppeteer sample&lt;/a&gt; on LambdaTest.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.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%2AIFXz1_Gohsg43KIM.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.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%2AIFXz1_Gohsg43KIM.png" width="800" height="268"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The capabilities (in dictionary format) generated using &lt;a href="https://www.lambdatest.com/capabilities-generator/?utm_source=devto&amp;amp;utm_medium=organic&amp;amp;utm_campaign=feb_05&amp;amp;utm_term=bh&amp;amp;utm_content=webpage" rel="noopener noreferrer"&gt;LambdaTest Capabilities Generator&lt;/a&gt; are passed as a query parameter to &lt;em&gt;browserWSEndpoint&lt;/em&gt;. The &lt;em&gt;dumps() *function of the *json&lt;/em&gt; module converts the capabilities into a JSON formatted string.&lt;/p&gt;

&lt;p&gt;As seen below, the values of &lt;em&gt;username&lt;/em&gt; and &lt;em&gt;access_key&lt;/em&gt; are obtained from the environment variables &lt;em&gt;LT_USERNAME&lt;/em&gt; and &lt;em&gt;LT_ACCESS_KEY&lt;/em&gt; specified in the Makefile. Since we want to check out the execution, the headless capability is set to &lt;em&gt;False&lt;/em&gt;.&lt;/p&gt;

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

&lt;p&gt;Finally, yield is used for defining the &lt;em&gt;teardown&lt;/em&gt; code (i.e., releasing browser resources) after the test execution is complete. In our case, we close the browser instance in the fixture responsible for opening a new Browser page.&lt;/p&gt;

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

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Looking for an effective way to test on &lt;a href="https://www.lambdatest.com/test-on-safari-browsers?utm_source=devto&amp;amp;utm_medium=organic&amp;amp;utm_campaign=feb_05&amp;amp;utm_term=bh&amp;amp;utm_content=webpage" rel="noopener noreferrer"&gt;Safari browsers online&lt;/a&gt;? Skip the hassle of emulators and simulators. Experience authentic testing with LambdaTest’s real online Safari browsers. Start now!&lt;/strong&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h3&gt;
  
  
  Fixture: Creation of a new page
&lt;/h3&gt;

&lt;p&gt;Next up, we have a Fixture of &lt;em&gt;function&lt;/em&gt; scope primarily responsible for creating (or opening) a new page. The fixture takes the newly created &lt;em&gt;browser&lt;/em&gt; instance as the input parameter.&lt;/p&gt;

&lt;p&gt;The &lt;em&gt;newPage()&lt;/em&gt; method of the &lt;em&gt;BrowserContext&lt;/em&gt; class is invoked to open up a new web page. Since the viewport sizes differ when the tests are run on the local machine (i.e., MacBook Pro 13 inch) &amp;amp; cloud grid, the same is set using the &lt;em&gt;setViewport()&lt;/em&gt; method of the page class. The required viewport width &amp;amp; height are passed in a dictionary format to the &lt;em&gt;setViewport()&lt;/em&gt; method.&lt;/p&gt;

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

&lt;p&gt;Finally, the resources used by the &lt;em&gt;browser&lt;/em&gt; (created in the &lt;em&gt;browser()&lt;/em&gt; fixture) and &lt;em&gt;page(s)&lt;/em&gt; that are created in the &lt;em&gt;page()&lt;/em&gt; fixture are released as a part of the teardown code.&lt;/p&gt;

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

&lt;h3&gt;
  
  
  pyproject.toml
&lt;/h3&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;[tool.poetry]
name = "Web automation with Pyppeteer"
version = "0.1.0"
description = ""
authors = ["Himanshu Jagdish Sheth &amp;lt;himanshu.sheth@gmail.com&amp;gt;"]


[tool.poetry.dependencies]
python = "^3.7.9"
flake8 = "^4.0.1"
autopep8 = "^1.6.0"
pytest-asyncio = "^0.21.0"


[tool.pytest.ini_options]
asyncio_mode = "auto"
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;All the project dependencies (i.e., &lt;em&gt;flake8&lt;/em&gt;, autopep8, and &lt;em&gt;pytest-asyncio&lt;/em&gt;) are under the &lt;em&gt;[tool.poetry.dependencies]&lt;/em&gt; section. &lt;em&gt;pytest-asyncio&lt;/em&gt; is a pytest plugin that provides support for coroutines as test functions. Awaits inside the test code are made possible with this plugin. The &lt;a href="https://www.lambdatest.com/blog/pytest-asyncio-to-reduce-test-execution-time/?utm_source=devto&amp;amp;utm_medium=organic&amp;amp;utm_campaign=feb_05&amp;amp;utm_term=bh&amp;amp;utm_content=blog" rel="noopener noreferrer"&gt;pytest asyncio&lt;/a&gt; is also instrumental in greatly reducing the test execution time!&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.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%2AwYzsfP-cRJvHcCLv.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.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%2AwYzsfP-cRJvHcCLv.png" width="800" height="400"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;In the &lt;em&gt;[tool.pytest.ini_options]&lt;/em&gt; section, the &lt;em&gt;asyncio&lt;/em&gt; mode is set to &lt;em&gt;auto&lt;/em&gt;. This means that the &lt;em&gt;asyncio&lt;/em&gt; mode will be automatically detected and enabled by pytest, depending on the presence of async fixtures (or tests).&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.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%2A2iVY0W2bchV0Ie7q.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.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%2A2iVY0W2bchV0Ie7q.png" width="666" height="376"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  requirements.txt
&lt;/h3&gt;

&lt;p&gt;The requirements.txt contains all the packages and libraries required for execution.&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;pytest
pyppeteer
pytest-asyncio
nest_asyncio
pytest-xdist
pytest-order
py
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;The &lt;em&gt;pytest-order&lt;/em&gt; plugin is used for ordering the execution of the tests. &lt;a href="https://www.lambdatest.com/blog/pytest-tutorial-parallel-testing-with-selenium-grid/?utm_source=devto&amp;amp;utm_medium=organic&amp;amp;utm_campaign=feb_05&amp;amp;utm_term=bh&amp;amp;utm_content=blog" rel="noopener noreferrer"&gt;pytest parallel execution&lt;/a&gt; is performed for a couple of test scenarios. Hence, the &lt;em&gt;pytest-xdist&lt;/em&gt; plugin is installed to realize the same.&lt;/p&gt;

&lt;p&gt;All the dependencies and packages are installed by executing &lt;em&gt;poetry install&lt;/em&gt; and &lt;em&gt;pip3 install -r requirements.txt&lt;/em&gt; on the terminal.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.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%2A-7xjOxu_XrcMD-a5.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.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%2A-7xjOxu_XrcMD-a5.png" width="800" height="351"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.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%2A_6kPQonrwDuNAriK.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.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%2A_6kPQonrwDuNAriK.png" width="800" height="400"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Test native, hybrid, and web apps on any mobile OS with our free &lt;a href="https://www.lambdatest.com/android-emulator-online?utm_source=devto&amp;amp;utm_medium=organic&amp;amp;utm_campaign=feb_05&amp;amp;utm_term=bh&amp;amp;utm_content=webpage" rel="noopener noreferrer"&gt;online Android emulator&lt;/a&gt;. Sign up to optimize app performance.&lt;/strong&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h3&gt;
  
  
  Makefile
&lt;/h3&gt;

&lt;p&gt;As stated earlier, the execution is all driven using the Makefile. Depending on the Python and pip versions installed on your machine, you may change &lt;em&gt;python3&lt;/em&gt; &amp;amp; &lt;em&gt;pip3&lt;/em&gt; to &lt;em&gt;python&lt;/em&gt; &amp;amp; &lt;em&gt;pip&lt;/em&gt;, respectively.&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;# Define variables
PYTHON := python3
POETRY := poetry
PYTEST := pytest
PIP := pip3
PROJECT_NAME := web automation with Pyppeteer


.PHONY: install
install:
   $(POETRY) install
   @echo "Dependency installation complete"


   $(PIP) install -r requirements.txt
   @echo "Set env vars LT_USERNAME &amp;amp; LT_ACCESS_KEY"
   # Procure Username and AccessKey from https://accounts.lambdatest.com/security
   export LT_USERNAME=himansh
   export LT_ACCESS_KEY=Ia1MiqNfci


.PHONY: install
poetry-install:
   poetry install


.PHONY: test
test:
   export NODE_ENV = test


.PHONY: test
pyunit-pyppeteer:
   - echo $(EXEC_PLATFORM)
   - $(PYTHON) tests/pyunit-pyppeteer/test_pyunit_pyppeteer.py


.PHONY: test
pytest-pyppeteer:
   - echo $(EXEC_PLATFORM)
   - $(PYTEST) --verbose --capture=no -s -n 2 tests/pytest-pyppeteer/test_pytest_pyppeteer_1.py \
   tests/pytest-pyppeteer/test_pytest_pyppeteer_2.py


.PHONY: test
pyunit-pyppeteer-browser-session:
   - echo $(EXEC_PLATFORM)
   - $(PYTHON) tests/starting-browser-session/pyunit/test_pyppeteer_browser_session.py


.PHONY: test
pytest-pyppeteer-browser-session:
   - echo $(EXEC_PLATFORM)
   - $(PYTEST) --verbose --capture=no -s \
   tests/starting-browser-session/pytest/test_pyppeteer_browser_session.py


.PHONY: test
asyncio-run-pyppeteer-browser-session:
   - echo $(EXEC_PLATFORM)
   - $(PYTHON) tests/starting-browser-session/asyncio_run/test_pyppeteer_browser_session.py


.PHONY: test
asyncio-run-complete-pyppeteer-browser-session:
   - echo $(EXEC_PLATFORM)
   - $(PYTHON) tests/starting-browser-session/\
   asyncio_run_until_complete/test_pyppeteer_browser_session.py


.PHONY: test
pyppeteer-button-click:
   - echo $(EXEC_PLATFORM)
   - $(PYTEST) --verbose --capture=no -s tests/button-click/test_page_class_click.py


.PHONY: test
pyppeteer-activate-tab:
   - echo $(EXEC_PLATFORM)
   - $(PYTEST) --verbose --capture=no -s tests/active-tab/test_page_class_bringtofront.py


###### Testing Custom Environment - https://miyakogi.github.io/pyppeteer/reference.html#environment-variables
# Available versions: 113, 121, and default
.PHONY: test
pyppeteer-custom-chromium-version:
   - echo $(EXEC_PLATFORM)
   - echo 'Browser Version:' $(CHROMIUM_VERSION)
   - $(PYTEST) --verbose --capture=no -s tests/custom-configuration/test_launcher_exe_path.py


###### Testing Headless - https://miyakogi.github.io/pyppeteer/reference.html#launcher
# Available values: headless and non-headless
.PHONY: test
pyppeteer-custom-browser-mode:
   - echo $(EXEC_PLATFORM)
   - echo $(BROWSER_MODE)
   - $(PYTEST) --verbose --capture=no -s tests/custom-configuration/test_launcher_headless.py


.PHONY: test
pyppeteer-generate-pdf:
   - echo $(EXEC_PLATFORM)
   - $(PYTEST) --verbose --capture=no -s tests/generate-pdf/test_page_class_pdf.py


.PHONY: test
pyppeteer-generate-screenshot:
   - echo $(EXEC_PLATFORM)
   - $(PYTEST) --verbose --capture=no -s tests/generate-screenshots/test_page_class_screenshot.py


.PHONY: test
pyppeteer-cookies:
   - echo $(EXEC_PLATFORM)
   - $(PYTEST) --verbose --capture=no -s tests/handling-cookies/test_page_class_cookies.py


.PHONY: test
pyppeteer-dialog-box:
   - echo $(EXEC_PLATFORM)
   - $(PYTEST) --verbose --capture=no -s tests/handling-dialog-box/test_handling_dialog_box.py


.PHONY: test
pyppeteer-iframe:
   - echo $(EXEC_PLATFORM)
   - $(PYTEST) --verbose --capture=no -s tests/handling-iframe/test_page_class_iframe.py


# Like Puppeteer, Navigation operations mentioned below only work in Headless mode
# goBack: https://miyakogi.github.io/pyppeteer/reference.html#pyppeteer.page.Page.goBack
# goForward: https://miyakogi.github.io/pyppeteer/reference.html#pyppeteer.page.Page.goForward


# Bug Link
# https://github.com/puppeteer/puppeteer/issues/7739
# https://stackoverflow.com/questions/65540674/how-to-error-check-pyppeteer-page-goback


.PHONY: test
pyppeteer-navigate-ops:
   - echo $(EXEC_PLATFORM)
   - $(PYTEST) --verbose --capture=no -s tests/navigate-operations/test_page_class_navigation_ops.py


.PHONY: test
pyppeteer-request-response:
   - echo $(EXEC_PLATFORM)
   - $(PYTEST) --verbose --capture=no -s tests/request-response/test_page_class_req_resp.py


.PHONY: test
pyppeteer-viewport:
   - echo $(EXEC_PLATFORM)
   - echo $(BROWSER_MODE)
   - $(PYTEST) --verbose --capture=no -s tests/setting-useragent-viewports/\
   test_page_class_useragent_viewport.py::test_mod_viewport


.PHONY: test
pyppeteer-non-headless-useragent:
   - echo $(EXEC_PLATFORM)
   - echo $(BROWSER_MODE)
   - $(PYTEST) --verbose --capture=no -s tests/setting-useragent-viewports/\
   test_page_class_useragent_viewport.py::test_get_nonheadless_user_agent


.PHONY: test
pyppeteer-headless-useragent:
   - echo $(EXEC_PLATFORM)
   - echo $(BROWSER_MODE)
   - $(PYTEST) --verbose --capture=no -s tests/setting-useragent-viewports/\
   test_page_class_useragent_viewport.py::test_get_headless_user_agent


.PHONY: test
pyppeteer-dynamic-content:
   - echo $(EXEC_PLATFORM)
   - echo $(BROWSER_MODE)
   - $(PYTEST) --verbose --capture=no -s -n 4 tests/handling-dynamic-content/\
   test_page_class_lazy_loaded_content.py


.PHONY: test
pyppeteer-web-scraping:
   - echo $(EXEC_PLATFORM)
   - $(PYTEST) --verbose --capture=no -s tests/web-scraping-content/\
   test_scraping_with_pyppeteer.py


.PHONY: clean
clean:
   # This helped: https://gist.github.com/hbsdev/a17deea814bc10197285
   find . | grep -E "(__pycache__|\.pyc$$)" | xargs rm -rf
   rm -rf .pytest_cache/
   @echo "Clean Succeeded"


.PHONY: distclean
distclean: clean
   rm -rf venv


.PHONY: help
help:
   @echo ""
   @echo "install : Install project dependencies"
   @echo "clean : Clean up temp files"
   @echo "pyunit-pyppeteer : Running Pyppeteer tests with Pyunit framework"
   @echo "pytest-pyppeteer : Running Pyppeteer tests with Pytest framework"
   @echo "pyunit-pyppeteer-browser-session : Browser session using Pyppeteer and Pyunit"
   @echo "pytest-pyppeteer-browser-session : Browser session using Pyppeteer and Pytest"
   @echo "asyncio-run-pyppeteer-browser-session : Browser session using Pyppeteer (Approach 1)"
   @echo "asyncio-run-complete-pyppeteer-browser-session : Browser session using Pyppeteer (Approach 2)"
   @echo "pyppeteer-button-click : Button click demo using Pyppeteer"
   @echo "pyppeteer-activate-tab : Switching browser tabs using Pyppeteer"
   @echo "pyppeteer-custom-chromium-version : Custom Chromium version with Pyppeteer"
   @echo "pyppeteer-custom-browser-mode : Headless and non-headless test execution with Pyppeteer"
   @echo "pyppeteer-generate-pdf : Generating pdf using Pyppeteer"
   @echo "pyppeteer-generate-screenshot : Generating page &amp;amp; element screenshots with Pyppeteer"
   @echo "pyppeteer-cookies : Customizing cookies with Pyppeteer"
   @echo "pyppeteer-dialog-box : Handling Dialog boxes with Pyppeteer"
   @echo "pyppeteer-iframe : Handling iFrames with Pyppeteer"
   @echo "pyppeteer-navigate-ops : Back &amp;amp; Forward browser operations with Pyppeteer"
   @echo "pyppeteer-request-response : Request and Response demonstration using Pyppeteer"
   @echo "pyppeteer-viewport : Customizing viewports using Pyppeteer"
   @echo "pyppeteer-non-headless-useragent : Customizing user-agent (with browser in headed mode) using Pyppeteer"
   @echo "pyppeteer-headless-useragent : Customizing user-agent (with browser in headless mode) using Pyppeteer"
   @echo "pyppeteer-dynamic-content : Handling dynamic web content using Pyppeteer"
   @echo "pyppeteer-web-scraping : Dynamic web scraping using Pyppeteer"
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;Since environment variables &lt;em&gt;LT_USERNAME&lt;/em&gt; and &lt;em&gt;LT_ACCESS_KEY&lt;/em&gt; are used for accessing the LambdaTest cloud grid, replace them with credentials from &lt;a href="https://accounts.lambdatest.com/security?utm_source=devto&amp;amp;utm_medium=organic&amp;amp;utm_campaign=feb_05&amp;amp;utm_term=bh&amp;amp;utm_content=webpage" rel="noopener noreferrer"&gt;LambdaTest Accounts Page&lt;/a&gt;. Also, &lt;em&gt;make install&lt;/em&gt; sets up configurations &amp;amp; installs dependencies from the .toml &amp;amp; requirements.txt, respectively.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.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%2AmfaZq96X0Bh9xz84.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.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%2AmfaZq96X0Bh9xz84.png" width="800" height="400"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Invoking make clean on the terminal removes all the generated files or artifacts (e.g., *.pyc) created during the build process.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.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%2AIOCprGd7F4JxmEKJ.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.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%2AIOCprGd7F4JxmEKJ.png" width="800" height="400"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Typing &lt;em&gt;make help&lt;/em&gt; provide all the options available for execution.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.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%2AEKua42KhPsRcwlnF.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.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%2AEKua42KhPsRcwlnF.png" width="800" height="400"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Like earlier, we are not getting into the nuances of Makefile since it is pretty much self-explanatory! With the stage all set, let’s get our hands dirty by implementing some prominent scenarios (including web scraping) with Pyppeteer.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;*Note: **The examples below work on local Chromium (&lt;/strong&gt;EXEC_PLATFORM = local*&lt;em&gt;) and Chrome on LambdaTest cloud grid (&lt;/em&gt;&lt;em&gt;EXEC_PLATFORM = cloud&lt;/em&gt;&lt;em&gt;).&lt;/em&gt;&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Test native, hybrid, and web apps on any mobile OS with our free &lt;a href="https://www.lambdatest.com/android-emulator-online?utm_source=devto&amp;amp;utm_medium=organic&amp;amp;utm_campaign=feb_05&amp;amp;utm_term=bh&amp;amp;utm_content=webpage" rel="noopener noreferrer"&gt;online emulator Android&lt;/a&gt;. Sign up to optimize app performance.&lt;/strong&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2&gt;
  
  
  Setting up a browser session with Pyppeteer
&lt;/h2&gt;

&lt;p&gt;Like any other library (or framework), the first step is to instantiate the browser and navigate to the respective URL. The &lt;em&gt;launch()&lt;/em&gt; function of the &lt;em&gt;pyppeteer.launcher&lt;/em&gt; module is invoked to start up a new instance of the Chrome (or Chromium) browser.&lt;/p&gt;

&lt;p&gt;As stated in the Pyppeteer official documentation, the &lt;em&gt;headless&lt;/em&gt; option is set to &lt;em&gt;False&lt;/em&gt; so that the browser is invoked in non-headless (or headed) mode. Additional arg (or argument), i.e., &lt;em&gt;–start-maximized&lt;/em&gt;, is also passed to the &lt;em&gt;launch()&lt;/em&gt; function so that the browser is maximized.&lt;/p&gt;

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

&lt;p&gt;Now that the browser is instantiated, the &lt;em&gt;newPage()&lt;/em&gt; function is invoked to create a new page within the browser. The method returns a &lt;em&gt;Page&lt;/em&gt; object that represents the newly opened page.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.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%2AeUI_cpXK6yhxCQ3j.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.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%2AeUI_cpXK6yhxCQ3j.png" width="800" height="400"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Finally, the &lt;em&gt;goto()&lt;/em&gt; method of the &lt;em&gt;Page&lt;/em&gt; class is used to navigate to the URL under test.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.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%2AEdFh_D2XKEb0BkXN.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.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%2AEdFh_D2XKEb0BkXN.png" width="800" height="270"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;We compare the page title with the expected title in the current test. An assert is raised in case the titles do not match.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.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%2AOuTzVhI4i7eAWPEF.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.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%2AOuTzVhI4i7eAWPEF.png" width="684" height="376"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;There are a couple of ways to invoke the test scenarios. The choice depends on the automation framework being used in conjunction with Pyppeteer.&lt;/p&gt;

&lt;h3&gt;
  
  
  Approach 1: Using the run function of the asyncio module
&lt;/h3&gt;

&lt;p&gt;The &lt;strong&gt;first approach&lt;/strong&gt; is using the &lt;em&gt;run_until_complete(future)&lt;/em&gt; method (of the asyncio module) until the &lt;em&gt;future&lt;/em&gt; has been completed. It blocks the execution of the code following it.&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;import asyncio
import pytest
import os
import sys
import json
from os import environ
from urllib.parse import quote
from pyppeteer import connect, launch
from pyppeteer.errors import PageError


test_url = 'https://search.brave.com/'
exec_platform = os.getenv('EXEC_PLATFORM')


# Get username and access key of the LambdaTest Platform
username = environ.get('LT_USERNAME', None)
access_key = environ.get('LT_ACCESS_KEY', None)


# Capabilities array with the respective configuration for parallel tests
cloud_capabilities = {
       'browserName': 'Chrome',
       'browserVersion': 'latest',
       'LT:Options': {
           'platform': 'Windows 11',
           'build': '[Build] Launching browser session with Pyppeteer (with Pytest)',
           'name': 'Launching browser session with Pyppeteer (with Pytest)',
           'user': username,
           'accessKey': access_key,
           'resolution': '1920x1080',
           'network': True,
           'video': True,
           'console': True,
           'headless': False
       }
}


local_capabilities = {
       'browserName': 'Chrome'
}


async def test_browser_session():
   # Launch a new browser instance
   # browser = await launch()
   if exec_platform == 'cloud':
       capability = quote(json.dumps(cloud_capabilities))
       print('Initializing test:: ', cloud_capabilities['LT:Options']['name'])


       browser = await connect(
           browserWSEndpoint=f'wss://cdp.lambdatest.com/puppeteer?capabilities={capability}'
       )
   elif exec_platform == 'local':
       print('Initializing test:: ', local_capabilities['browserName'])
       browser = await launch(headless = False, args=['--start-maximized'])


   # Create a new page
   page = await browser.newPage()


   # Navigate to a website
   await page.goto('https://search.brave.com/')
   title = await page.title()
   print(title)


   try:
       assert title == 'Private Search Engine - Brave Search', 'Expected page title is incorrect!'
       await page.evaluate('_ =&amp;gt; {}', f'lambdatest_action: {json.dumps({ "action": "setTestStatus", "arguments": { "status": "passed", "remark": "Title matched" } })}')
   except PageError as e:
       await page.evaluate('_ =&amp;gt; {}', f'lambdatest_action: {json.dumps({ "action": "setTestStatus", "arguments": { "status": "failed", "remark": str(e) } })}')


   # Release the resources
   await page.close()
   await asyncio.sleep(1)
   await browser.close()


# Run the event loop
asyncio.get_event_loop().run_until_complete(test_browser_session())
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;The &lt;strong&gt;second approach&lt;/strong&gt; is to execute the coroutine directly by passing the coroutine to the &lt;em&gt;run()&lt;/em&gt; function of the &lt;em&gt;asyncio&lt;/em&gt; module. As stated in the official documentation, the &lt;em&gt;run()&lt;/em&gt; function cannot be called when another asyncio event loop is running in the same thread.&lt;/p&gt;

&lt;p&gt;As shown below, the test method [i.e., &lt;em&gt;test_lambdatest_search()&lt;/em&gt; ] is passed to the &lt;em&gt;run()&lt;/em&gt; function. These test execution steps are repeated for all the browser capabilities passed to the test method (or coroutine).&lt;/p&gt;

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

&lt;p&gt;Here is the complete implementation of setting up a browser session with Pyppeteer when coroutines (or test methods) are run using the &lt;em&gt;run()&lt;/em&gt; function.&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;import asyncio
import pytest
import os
import sys
import json
from os import environ
from urllib.parse import quote
from pyppeteer import connect, launch
from pyppeteer.errors import PageError


test_url = 'https://search.brave.com/'
exec_platform = os.getenv('EXEC_PLATFORM')


# Get username and access key of the LambdaTest Platform
username = environ.get('LT_USERNAME', None)
access_key = environ.get('LT_ACCESS_KEY', None)


# Capabilities array with the respective configuration for parallel tests
cloud_capabilities = {
       'browserName': 'Chrome',
       'browserVersion': 'latest',
       'LT:Options': {
           'platform': 'Windows 11',
           'build': '[Build] Launching browser session with Pyppeteer (with Pytest)',
           'name': 'Launching browser session with Pyppeteer (with Pytest)',
           'user': username,
           'accessKey': access_key,
           'resolution': '1920x1080',
           'network': True,
           'video': True,
           'console': True,
           'headless': False
       }
}


local_capabilities = {
       'browserName': 'Chrome'
}


# Pytest fixture for browser setup
@pytest.fixture(scope='function')
async def browser():
   if exec_platform == 'cloud':
       capability = quote(json.dumps(cloud_capabilities))
       print('Initializing test:: ', cloud_capabilities['LT:Options']['name'])


       browser = await connect(
           browserWSEndpoint=f'wss://cdp.lambdatest.com/puppeteer?capabilities={capability}'
       )
   elif exec_platform == 'local':
       print('Initializing test:: ', local_capabilities['browserName'])
       browser = await launch(headless = False, args=['--start-maximized'])

   yield browser


   await asyncio.sleep(1)


# Pytest fixture for page setup
@pytest.fixture(scope='function')
async def page(browser):
   page = await browser.newPage()


   yield page
   await page.close()
   await asyncio.sleep(1)
   await browser.close()


@pytest.mark.asyncio
async def test_browser_session(page):
   await page.goto('https://search.brave.com/')
   title = await page.title()
   print(title)


   try:
       assert title == 'Private Search Engine - Brave Search', 'Expected page title is incorrect!'
       await page.evaluate('_ =&amp;gt; {}', f'lambdatest_action: {json.dumps({ "action": "setTestStatus", "arguments": { "status": "passed", "remark": "Title matched" } })}')
   except PageError as e:
       await page.evaluate('_ =&amp;gt; {}', f'lambdatest_action: {json.dumps({ "action": "setTestStatus", "arguments": { "status": "failed", "remark": str(e) } })}')
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;
&lt;h3&gt;
  
  
  Approach 2: Using Pyppeteer with pytest
&lt;/h3&gt;

&lt;p&gt;As mentioned earlier, Pyppeteer can also be used with the pytest framework. The &lt;em&gt;yield&lt;/em&gt; in conjunction with pytest fixtures is used for implementing the &lt;em&gt;setup&lt;/em&gt; and &lt;em&gt;teardown&lt;/em&gt; logic for the following:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;Instantiating the browser [using either &lt;em&gt;connect()&lt;/em&gt; or &lt;em&gt;launch()&lt;/em&gt; methods]&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Creating a new page in the browser context [using &lt;em&gt;newPage()&lt;/em&gt; method]&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Closing &amp;amp; releasing the resources held by the instantiated browser and page&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;a href="https://media2.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%2A0_XL5ahkiBG0OZNM.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.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%2A0_XL5ahkiBG0OZNM.png" width="800" height="400"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Here is the complete implementation of setting up a browser session with Pyppeteer in conjunction with the pytest framework:&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;import asyncio
import pytest
import os
import sys
import json
from os import environ
from urllib.parse import quote
from pyppeteer import connect, launch
from pyppeteer.errors import PageError


test_url = 'https://search.brave.com/'
exec_platform = os.getenv('EXEC_PLATFORM')


# Get username and access key of the LambdaTest Platform
username = environ.get('LT_USERNAME', None)
access_key = environ.get('LT_ACCESS_KEY', None)


# Capabilities array with the respective configuration for parallel tests
cloud_capabilities = {
       'browserName': 'Chrome',
       'browserVersion': 'latest',
       'LT:Options': {
           'platform': 'Windows 11',
           'build': '[Build] Launching browser session with Pyppeteer (with Pytest)',
           'name': 'Launching browser session with Pyppeteer (with Pytest)',
           'user': username,
           'accessKey': access_key,
           'resolution': '1920x1080',
           'network': True,
           'video': True,
           'console': True,
           'headless': False
       }
}


local_capabilities = {
       'browserName': 'Chrome'
}


# Pytest fixture for browser setup
@pytest.fixture(scope='function')
async def browser():
   if exec_platform == 'cloud':
       capability = quote(json.dumps(cloud_capabilities))
       print('Initializing test:: ', cloud_capabilities['LT:Options']['name'])


       browser = await connect(
           browserWSEndpoint=f'wss://cdp.lambdatest.com/puppeteer?capabilities={capability}'
       )
   elif exec_platform == 'local':
       print('Initializing test:: ', local_capabilities['browserName'])
       browser = await launch(headless = False, args=['--start-maximized'])

   yield browser


   await asyncio.sleep(1)


# Pytest fixture for page setup
@pytest.fixture(scope='function')
async def page(browser):
   page = await browser.newPage()


   yield page
   await page.close()
   await asyncio.sleep(1)
   await browser.close()


@pytest.mark.asyncio
async def test_browser_session(page):
   await page.goto('https://search.brave.com/')
   title = await page.title()
   print(title)


   try:
       assert title == 'Private Search Engine - Brave Search', 'Expected page title is incorrect!'
       await page.evaluate('_ =&amp;gt; {}', f'lambdatest_action: {json.dumps({ "action": "setTestStatus", "arguments": { "status": "passed", "remark": "Title matched" } })}')
   except PageError as e:
       await page.evaluate('_ =&amp;gt; {}', f'lambdatest_action: {json.dumps({ "action": "setTestStatus", "arguments": { "status": "failed", "remark": str(e) } })}')
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;In this article, we take a look at some aspects of simulation and discuss some ways through which we can use &lt;a href="https://www.lambdatest.com/blog/iphone-simulators-for-windows/?utm_source=devto&amp;amp;utm_medium=organic&amp;amp;utm_campaign=feb_05&amp;amp;utm_term=bh&amp;amp;utm_content=webpage" rel="noopener noreferrer"&gt;ios emulator for pc&lt;/a&gt; Windows.&lt;/strong&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h3&gt;
  
  
  Approach 3: Using Pyppeteer with PyUnit
&lt;/h3&gt;

&lt;p&gt;As stated earlier, Pyppeteer can also be used with the PyUnit (or unittest) framework. However, I would always prefer pytest (with Pyppeteer) over PyUnit (with Pyppeteer) since PyUnit (or unittest) methods are synchronous — a deal breaker when used with the Pyppeteer library!&lt;/p&gt;

&lt;p&gt;Since the intent is to run synchronous and asynchronous code, we have used the &lt;em&gt;IsolatedAsyncioTestCase&lt;/em&gt; test class in the &lt;em&gt;unittest&lt;/em&gt; module. The &lt;em&gt;IsolatedAsyncioTestCase&lt;/em&gt; class provides an API similar to &lt;em&gt;TestCase&lt;/em&gt; and accepts coroutines as test functions. It also isolates the test case’s &lt;em&gt;asyncio&lt;/em&gt; event loop, providing each test case with its fresh event loop.&lt;/p&gt;

&lt;p&gt;The &lt;em&gt;IsolatedAsyncioTestCase&lt;/em&gt; class provides the &lt;em&gt;asyncSetUp()&lt;/em&gt; &amp;amp; &lt;em&gt;asyncTearDown()&lt;/em&gt; methods that accept coroutines. These methods are called after &lt;em&gt;setUp()&lt;/em&gt; and before &lt;em&gt;tearDown()&lt;/em&gt;, respectively.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.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%2AkY2InztOz9efh_7X.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.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%2AkY2InztOz9efh_7X.png" width="800" height="400"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Here is the complete implementation of setting up a browser session with Pyppeteer in conjunction with the PyUnit (or unittest) framework:&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;import asyncio
import unittest
from pyppeteer import connect, launch
from pyppeteer.errors import PageError
from urllib.parse import quote
import json
import os
from os import environ


exec_platform = os.getenv('EXEC_PLATFORM')


# Get username and access key of the LambdaTest Platform
username = environ.get('LT_USERNAME', None)
access_key = environ.get('LT_ACCESS_KEY', None)


# Capabilities array with the respective configuration for parallel tests
cloud_capabilities = {
       'browserName': 'Chrome',
       'browserVersion': 'latest',
       'LT:Options': {
           'platform': 'Windows 11',
           'build': '[Build] Launching browser session with Pyppeteer (with unittest)',
           'name': 'Launching browser session with Pyppeteer (with unittest)',
           'user': username,
           'accessKey': access_key,
           'resolution': '1920x1080',
           'network': True,
           'video': True,
           'console': True,
           'headless': False
       }
}


local_capabilities = {
       'browserName': 'Chrome'
}


class LambdaTestAsyncTest(unittest.IsolatedAsyncioTestCase):
   async def asyncSetUp(self):
       if exec_platform == 'cloud':
           capability = quote(json.dumps(cloud_capabilities))
           print('Initializing test:: ', cloud_capabilities['LT:Options']['name'])


           self.browser = await connect(
               browserWSEndpoint=f'wss://cdp.lambdatest.com/puppeteer?capabilities={capability}'
           )
       elif exec_platform == 'local':
           print('Initializing test:: ', local_capabilities['browserName'])
           self.browser = await launch(headless = False, args=['--start-maximized'])


       await asyncio.sleep(1)
       self.page = await self.browser.newPage()


   async def asyncTearDown(self):
       await self.page.close()
       await asyncio.sleep(1)
       await self.browser.close()


   async def test_page_title(self):
       await self.page.goto('https://search.brave.com/')
       title = await self.page.title()
       print('Scenario 1: Page Title ' + title)


       try:
           assert title == 'Private Search Engine - Brave Search', 'Expected page title is incorrect!'
           await self.page.evaluate('_ =&amp;gt; {}', f'lambdatest_action: {json.dumps({ "action": "setTestStatus", "arguments": { "status": "passed", "remark": "Title matched" } })}')
       except PageError as e:
           await self.page.evaluate('_ =&amp;gt; {}', f'lambdatest_action: {json.dumps({ "action": "setTestStatus", "arguments": { "status": "failed", "remark": str(e) } })}')


   async def test_page_content(self):
       # Navigate to a website to see the effect
       await self.page.goto('https://www.duckduckgo.com')
       element = await self.page.querySelector('[name="q"]')


       await element.click()
       await element.type('LambdaTest')
       await asyncio.gather(
           self.page.keyboard.press('Enter'),
           self.page.waitForNavigation()
       )


       page_title = await self.page.title()
       print('Scenario 2: Page Title ' + page_title)
       return page_title


if __name__ == '__main__':
   unittest.main()
&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;To run the set up a browser session with Pyppeteer, simply trigger the relevant &lt;em&gt;make&lt;/em&gt; command from the terminal:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.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%2Azgl6dgKjhercueM4Br7frw.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.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%2Azgl6dgKjhercueM4Br7frw.png" width="800" height="343"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;As seen below, we have set the &lt;em&gt;EXEC_PLATFORM&lt;/em&gt; to local and successfully started a browser session using PyUnit (or unittest) with Pyppeteer.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.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%2A5mL9YOaoo2LN3vDq.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.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%2A5mL9YOaoo2LN3vDq.png" width="800" height="361"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Perform browser automation testing on the most powerful cloud infrastructure. Leverage LambdaTest &lt;a href="https://www.lambdatest.com/automation-testing?utm_source=devto&amp;amp;utm_medium=organic&amp;amp;utm_campaign=feb_05&amp;amp;utm_term=bh&amp;amp;utm_content=webpage" rel="noopener noreferrer"&gt;test automation cloud&lt;/a&gt; for faster, reliable and scalable experience on cloud.&lt;/strong&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2&gt;
  
  
  Clicking buttons with Pyppeteer
&lt;/h2&gt;

&lt;p&gt;&lt;em&gt;Page&lt;/em&gt; class in Pyppeteer provides the &lt;em&gt;click()&lt;/em&gt; method for clicking on an element that matches the selector. For example, when c&lt;a href="https://www.lambdatest.com/blog/selenium-click-button-with-examples/?utm_source=devto&amp;amp;utm_medium=organic&amp;amp;utm_campaign=feb_05&amp;amp;utm_term=bh&amp;amp;utm_content=blog" rel="noopener noreferrer"&gt;licking buttons with Selenium&lt;/a&gt;, the first step is to locate the element using XPath, CSS Selector, or any other suitable locator.&lt;/p&gt;

&lt;p&gt;Once the element is located, the &lt;em&gt;click()&lt;/em&gt; method also scrolls it into view (if needed). Once inside the view, it uses the mouse to click in the element’s center. Like the &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=feb_05&amp;amp;utm_term=bh&amp;amp;utm_content=blog" rel="noopener noreferrer"&gt;Selenium exception&lt;/a&gt;, the &lt;em&gt;click() *method raises *PageError&lt;/em&gt; if it is impossible to interact with the element (or if there is any other error on the page).&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Syntax&lt;/strong&gt;&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;coroutine click(selector: str, options: dict = None, **kwargs)
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;The available options are below:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;&lt;em&gt;button&lt;/em&gt; (str) — left, right, or middle (default: left)&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;em&gt;clickCount&lt;/em&gt; (int) — 1 (default)&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;em&gt;delay&lt;/em&gt; (int|float) — Time to wait between mouse-down and mouse-up in milliseconds (default — 0)&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Demonstration&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;For demonstrating button click with Pyppeteer, we use the following test scenario:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;&lt;p&gt;Navigate 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 the menu item ‘Shop by category’.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Add Macbook Pro to the cart.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Assert if the checkout is not successful.&lt;/p&gt;&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;The complete implementation of realizing button clicks with Pyppeteer:&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;import asyncio
import pytest
from pyppeteer.errors import PageError
from urllib.parse import quote
import json
import os
import sys
from os import environ
from pyppeteer import connect, launch


exec_platform = os.getenv('EXEC_PLATFORM')


test_url = 'https://ecommerce-playground.lambdatest.io/'
product_url = 'https://ecommerce-playground.lambdatest.io/index.php?route=product/category&amp;amp;path=57'


# XPath's of the elements that need to be located on the page


shopcategory = "//a[contains(.,'Shop by Category')]"
megamenu = "//a[contains(.,'Mega Menu')]"
# We could have also used XPath for the same
# phonecategory = "//span[contains(.,'Phone, Tablets &amp;amp; Ipod')]"


phonecategorySelector = '#widget-navbar-217841 &amp;gt; ul &amp;gt; li:nth-child(3) &amp;gt; a &amp;gt; div.info &amp;gt; span'
# Macbook Air product
macbook_locator = '#mz-product-grid-image-44-212408'


# Buy Now button
button_buynow = '.btn-buynow'
target_checkout_url = 'https://ecommerce-playground.lambdatest.io/index.php?route=checkout/checkout'
target_page_str = 'Billing Address'


menu_hover_xpath = "//*[@id='__docusaurus']/nav/div[1]/div[2]/div[1]"


@pytest.mark.asyncio
@pytest.mark.order(1)
async def test_click_element(page):
   # await page.goto(test_url)
   await page.goto(test_url,
       {'waitUntil': 'load'})


   # Wait for the 'Shop by Category' menu to be available
   menu_element = await page.waitForXPath(shopcategory)


   # Click on the 'Shop by Category' menu
   await menu_element.click()


   # Can be changed with non-blocking sleep
   await asyncio.sleep(2)


   shop_element = await page.waitForSelector(phonecategorySelector, {'visible': True})


   # Click on the 'Shop by Category' menu
   if exec_platform == 'local':
       await shop_element.click()
   elif exec_platform == 'cloud':
       await asyncio.gather(
           shop_element.click(),
           page.waitForNavigation({'waitUntil': 'networkidle2', 'timeout': 60000}),
       )


   # Can be changed with non-blocking sleep
   await asyncio.sleep(2)


   page_title = await page.title()


   try:
       assert page_title == "Tablets"
       print("Test Success: Reached the target URL")
       await page.evaluate('_ =&amp;gt; {}', f'lambdatest_action: {json.dumps({ "action": "setTestStatus", "arguments": { "status": "passed", "remark": "Title matched" } })}')
   except PageError as e:
       print("Test Failure: Recheck the URL")
       await page.evaluate('_ =&amp;gt; {}', f'lambdatest_action: {json.dumps({ "action": "setTestStatus", "arguments": { "status": "failed", "remark": str(e) } })}')


   await asyncio.sleep(2)


   elem_macbook = await page.waitForSelector(macbook_locator, {'visible': True})


   # Click on the 'Shop by Category' menu
   await elem_macbook.click()


   await asyncio.sleep(2)


   # Click on the Buy Now Button
   elem_buynow = await page.waitForSelector(button_buynow, {'visible': True})
   if exec_platform == 'local':
       await elem_buynow.click()
   elif exec_platform == 'cloud':
       await asyncio.gather(
           elem_buynow.click(),
           page.waitForNavigation({'waitUntil': 'networkidle2'}),
       )


   await asyncio.sleep(2)


   current_url = page.url
   print('Current URL is: ' + current_url)


   try:
       assert current_url == target_checkout_url
       print("Test Success: Product checkout successful")
       await page.evaluate('_ =&amp;gt; {}', f'lambdatest_action: {json.dumps({ "action": "setTestStatus", "arguments": { "status": "passed", "remark": "Title matched" } })}')
   except PageError as e:
       print("Test Failure: Could not checkout Product")
       await page.evaluate('_ =&amp;gt; {}', f'lambdatest_action: {json.dumps({ "action": "setTestStatus", "arguments": { "status": "failed", "remark": str(e) } })}')


   await asyncio.sleep(2)


@pytest.mark.asyncio
@pytest.mark.order(2)
async def test_get_content(page):
   await page.goto('https://ecommerce-playground.lambdatest.io/')


   await asyncio.sleep(2)


   html_content = await page.content()


   await asyncio.sleep(2)


   assert 'Upto 50% Off on Fully Automatic' in html_content, "Expected string not found in page content"
&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;Since we are using the pytest framework with Pyppeteer library, we first import &lt;em&gt;pytest&lt;/em&gt;, &lt;em&gt;pyppeteer&lt;/em&gt;, and &lt;em&gt;json&lt;/em&gt; modules.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.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%2Ai7LPH79VVJzOdryE.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.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%2Ai7LPH79VVJzOdryE.png" width="800" height="604"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The tests are marked with the &lt;em&gt;@pytest.mark.asyncio&lt;/em&gt; decorator, making them coroutines instead of tests (or test methods). This lets us use await code inside the tests.&lt;/p&gt;

&lt;p&gt;Next, the &lt;em&gt;@pytest.mark.order&lt;/em&gt; marker of the &lt;em&gt;pytest-order&lt;/em&gt; plugin is used to order the execution of the tests.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.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%2AWA8ih6mup_ZUXjr3.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.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%2AWA8ih6mup_ZUXjr3.png" width="800" height="400"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The &lt;em&gt;goto()&lt;/em&gt; method of the &lt;em&gt;Page&lt;/em&gt; class is invoked to navigate to the URL under test. The &lt;em&gt;waitUntil&lt;/em&gt; option is set to &lt;em&gt;load&lt;/em&gt; (i.e., default — navigation succeeds when the load event is fired).&lt;/p&gt;

&lt;p&gt;The &lt;em&gt;waitUntil&lt;/em&gt; option can also be set to &lt;em&gt;domcontentloaded&lt;/em&gt; (i.e., &lt;em&gt;DOMContentLoaded&lt;/em&gt; is fired), &lt;em&gt;networkidle0&lt;/em&gt; (i.e., no more than 0 network connections for at least 500 ms), or &lt;em&gt;networkidle2&lt;/em&gt; (i.e., no more than 2 network connections for at least 500 ms).&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.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%2AD4R7Y-2y_4tnIp4q.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.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%2AD4R7Y-2y_4tnIp4q.png" width="684" height="376"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Now that the page is loaded, we use the &lt;em&gt;waitForXPath()&lt;/em&gt; method until the element matching the XPath appears on the page. The default wait is 3000 ms.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.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%2AA9fUgam03G-fwBMC.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.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%2AA9fUgam03G-fwBMC.png" width="800" height="400"&gt;&lt;/a&gt;&lt;/p&gt;

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

&lt;p&gt;We locate the menu item using the &lt;a href="https://www.lambdatest.com/blog/css-selectors/?utm_source=devto&amp;amp;utm_medium=organic&amp;amp;utm_campaign=feb_05&amp;amp;utm_term=bh&amp;amp;utm_content=blog" rel="noopener noreferrer"&gt;CSS Selector&lt;/a&gt;, passed to the &lt;em&gt;waitForSelector() *method of the *Page *class. The method returns immediately in case the selector already exists on the page. On the other hand, it waits until the selector appears on the page for the duration specified by the *timeout&lt;/em&gt; value (default — 3000 ms).&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.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%2AhQiC7dzBvdp35Pb6.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.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%2AhQiC7dzBvdp35Pb6.png" width="800" height="400"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;As seen below, the &lt;em&gt;visible&lt;/em&gt; option is set to &lt;em&gt;True&lt;/em&gt;; hence, it waits for the element to be visible when it is present in the DOM.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.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%2Ay53jNv8McFkb3vVL.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.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%2Ay53jNv8McFkb3vVL.png" width="800" height="400"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Once the element is located, the &lt;em&gt;click()&lt;/em&gt; method is invoked for clicking on the element. In order to ensure that there is zero &lt;a href="https://www.lambdatest.com/learning-hub/flaky-test?utm_source=devto&amp;amp;utm_medium=organic&amp;amp;utm_campaign=feb_05&amp;amp;utm_term=bh&amp;amp;utm_content=learning_hub" rel="noopener noreferrer"&gt;test flakiness&lt;/a&gt; (when using the cloud grid), we have also used the &lt;em&gt;waitForNavigation()&lt;/em&gt; method with the timeout set to 60000 ms.&lt;/p&gt;

&lt;p&gt;The &lt;em&gt;waitUntil&lt;/em&gt; option in the method is set to &lt;em&gt;networkidle2&lt;/em&gt;. Hence, the execution moves to the next line only if there is a &lt;em&gt;timeout&lt;/em&gt; or when there are no more than 2 network connections for at least 500 ms.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;a href="https://www.lambdatest.com/learning-hub/what-is-xcode?utm_source=devto&amp;amp;utm_medium=organic&amp;amp;utm_campaign=feb_05&amp;amp;utm_term=bh&amp;amp;utm_content=learning_hub" rel="noopener noreferrer"&gt;**What is Xcode&lt;/a&gt;: It is an IDE developed by Apple for developing your applications for macOS, iOS, iPadOS, watchOS, tvOS, and visionOS with powerful, integrated tools.**&lt;/p&gt;
&lt;/blockquote&gt;

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

&lt;p&gt;Then, we check the page title using the &lt;em&gt;title()&lt;/em&gt; method of the &lt;em&gt;Page&lt;/em&gt; class.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.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%2AoQaPguUnp6mfU_Yd.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.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%2AoQaPguUnp6mfU_Yd.png" width="768" height="334"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Once we have the page title, the &lt;em&gt;page.evaluate()&lt;/em&gt; method in Pyppeteer is used to execute JavaScript code within the context of a page. An assert is raised in case the title (of the current page) does not match with the expected page title.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.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%2A4QYQL4fHbmmYgE9r.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.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%2A4QYQL4fHbmmYgE9r.png" width="800" height="215"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Now that we are on the Product Page, we locate the respective WebElement using the CSS Selector. The &lt;em&gt;visible&lt;/em&gt; option in the &lt;em&gt;waitForSelector()&lt;/em&gt; method is set to &lt;em&gt;True;&lt;/em&gt; hence, it waits for the located element to be visible in the DOM. By default, the &lt;em&gt;timeout&lt;/em&gt; is 30000 ms (or 30 seconds).&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.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%2A6IEPrKylmrjfJ3bP.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.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%2A6IEPrKylmrjfJ3bP.png" width="800" height="482"&gt;&lt;/a&gt;&lt;/p&gt;

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

&lt;p&gt;Once the button is clicked, we pause the execution (for 2 seconds) by invoking the &lt;em&gt;asyncio.sleep()&lt;/em&gt;. It is used for introducing asynchronous delays in an event loop.&lt;/p&gt;

&lt;p&gt;After an asynchronous sleep of 2 seconds, the &lt;strong&gt;Buy Now&lt;/strong&gt; button is located using the CSS Selector (i.e., &lt;em&gt;.btn-buynow&lt;/em&gt;). Like earlier, the visibility of the located element is checked; otherwise, an exception is raised if the element is not visible within a default timeout of 30000 ms.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.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%2AgdxHhfQicsrZdVs0.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.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%2AgdxHhfQicsrZdVs0.png" width="800" height="400"&gt;&lt;/a&gt;&lt;/p&gt;

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

&lt;p&gt;Here, we have used &lt;em&gt;asyncio.gather()&lt;/em&gt;, a function provided by the &lt;em&gt;asyncio&lt;/em&gt; library that allows multiple coroutines to be executed concurrently and waits for them to complete before moving further with the execution.&lt;/p&gt;

&lt;p&gt;In our case, the click to the located element (or button) and &lt;a href="https://www.lambdatest.com/blog/selenium-wait-for-page-to-load/?utm_source=devto&amp;amp;utm_medium=organic&amp;amp;utm_campaign=feb_05&amp;amp;utm_term=bh&amp;amp;utm_content=blog" rel="noopener noreferrer"&gt;waiting for the page to load&lt;/a&gt; with the &lt;em&gt;waitUntil *option set to *networkidle2 *are executed as a part of the *gather()&lt;/em&gt; function.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.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%2AjqUy9g9j3yJ0MVl7.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.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%2AjqUy9g9j3yJ0MVl7.png" width="800" height="363"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Finally, the current page’s URL is fetched by invoking the &lt;em&gt;url()&lt;/em&gt; method of the Page class. An assert is raised if the current URL does not match the expected destination URL.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.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%2AFBIMq8-s6jAQ-RYx.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.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%2AFBIMq8-s6jAQ-RYx.png" width="800" height="188"&gt;&lt;/a&gt;&lt;/p&gt;

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

&lt;p&gt;To automate interactions with a button element with Pyppeteer, invoke the command *make pyppeteer-button-click *on the terminal. In our case, we have set the execution to the LambdaTest cloud grid.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.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%2AeVSiEg_iEw_wOKBx.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.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%2AeVSiEg_iEw_wOKBx.png" width="800" height="439"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Below is the snapshot from the &lt;a href="https://automation.lambdatest.com/?utm_source=devto&amp;amp;utm_medium=organic&amp;amp;utm_campaign=feb_05&amp;amp;utm_term=bh&amp;amp;utm_content=webpage" rel="noopener noreferrer"&gt;LambdaTest Automation Dashboard&lt;/a&gt; indicating that the test execution was successful.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.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%2AQO-x4IyyB3FDOhbn.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.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%2AQO-x4IyyB3FDOhbn.png" width="800" height="444"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Explore ‘What is TDD’ in Agile with our comprehensive tutorial. Learn &lt;a href="https://www.lambdatest.com/learning-hub/test-driven-development?utm_source=devto&amp;amp;utm_medium=organic&amp;amp;utm_campaign=feb_05&amp;amp;utm_term=bh&amp;amp;utm_content=learning_hub" rel="noopener noreferrer"&gt;Test-Driven Development&lt;/a&gt; meaning, examples, and best practices for effective software development.&lt;/strong&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2&gt;
  
  
  Printing PDF files with Pyppeteer
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://www.lambdatest.com/blog/selenium-testing-pdf-files/?utm_source=devto&amp;amp;utm_medium=organic&amp;amp;utm_campaign=feb_05&amp;amp;utm_term=bh&amp;amp;utm_content=blog" rel="noopener noreferrer"&gt;Testing PDF files&lt;/a&gt; is a common test scenario with any automation framework, and the same is the case with the Pyppeteer library. The *pdf() *method (or coroutine) of the *Page *class lets you generate a pdf of the entire web page.&lt;/p&gt;

&lt;p&gt;As stated in official Pyppeteer documentation, generating PDFs is only supported in headless mode. The &lt;em&gt;pdf()&lt;/em&gt; method provides many options, some mentioned below:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.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%2Ata97APp9nQqKpYWO.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.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%2Ata97APp9nQqKpYWO.png" width="800" height="914"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://miyakogi.github.io/pyppeteer/_modules/pyppeteer/page.html#Page.pdf" rel="noopener noreferrer"&gt;*Image Source&lt;/a&gt;*&lt;/p&gt;

&lt;p&gt;By default, the &lt;em&gt;pdf()&lt;/em&gt; method generates a pdf of the page with &lt;em&gt;print&lt;/em&gt; CSS media. For generating a PDF with &lt;em&gt;screen&lt;/em&gt; media, &lt;em&gt;page.emulateMedia(‘screen’)&lt;/em&gt; needs to be invoked before invoking the &lt;em&gt;pdf()&lt;/em&gt; method.&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;import asyncio
import pytest
from pyppeteer.errors import PageError
from urllib.parse import quote
import os
import sys
from os import environ
from pyppeteer import connect, launch


exec_platform = os.getenv('EXEC_PLATFORM')


test_url = 'https://lambdatest.com/'


# Selectors of the page
# Pytest fixture for browser setup
@pytest.fixture(scope='function')
async def browser():
   if exec_platform == 'local':
       browser = await launch()
   yield browser
   await asyncio.sleep(1)   
   await browser.close()


# Pytest fixture for page setup
@pytest.fixture(scope='function')
async def page(browser):
   page = await browser.newPage()
   yield page
   await page.close()


@pytest.mark.asyncio
@pytest.mark.order(1)
async def test_print_pdf(page):
   await page.goto(test_url, {'waitUntil' : 'networkidle2'})


   asyncio.sleep(1)


   page.emulateMedia('screen')


   # Further details
   # https://miyakogi.github.io/pyppeteer/reference.html#pyppeteer.page.Page.pdf
   await page.pdf({'path': 'lambdatest.pdf', 'format': 'A4'})
&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 &lt;em&gt;launch()&lt;/em&gt; method is invoked for instantiating &lt;a href="https://www.lambdatest.com/blog/headless-chrome/?utm_source=devto&amp;amp;utm_medium=organic&amp;amp;utm_campaign=feb_05&amp;amp;utm_term=bh&amp;amp;utm_content=blog" rel="noopener noreferrer"&gt;headless Chrome&lt;/a&gt;. Since we are using pytest with Pyppeteer, *browser *instance and *new pages *on the browser are created under the pytest fixture with *function *scope.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.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%2AV900oTLyyKX6QILk.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.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%2AV900oTLyyKX6QILk.png" width="800" height="860"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Before printing the page, the &lt;em&gt;emulateMedia()&lt;/em&gt; method is called to simulate the appearance of the page when rendered for *screen *media.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.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%2AyNU242gI55Hb6UJ4.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.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%2AyNU242gI55Hb6UJ4.png" width="700" height="334"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Finally, the &lt;em&gt;pdf()&lt;/em&gt; method of the &lt;em&gt;Page *class is invoked with the path set to *current-working-directory/lambdatest.pdf&lt;/em&gt; and format set to A4.&lt;/p&gt;

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

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

&lt;p&gt;In order to generate a PDF of the current web page with Pyppeteer, invoke the command *make pyppeteer-generate-pdf *on the terminal. We have instantiated the Chromium browser on the local machine for this scenario.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.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%2Aj3qDgJ71k2ofuGTx.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.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%2Aj3qDgJ71k2ofuGTx.png" width="800" height="385"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Below is a screenshot of the generated PDF document:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.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%2A18ic8jSU2wKUvoeE.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.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%2A18ic8jSU2wKUvoeE.png" width="800" height="400"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;a href="https://www.lambdatest.com/learning-hub/dynamic-testing?utm_source=devto&amp;amp;utm_medium=organic&amp;amp;utm_campaign=feb_05&amp;amp;utm_term=bh&amp;amp;utm_content=learning_hub" rel="noopener noreferrer"&gt;**Dynamic testing&lt;/a&gt; evaluates software by executing code to identify defects, ensuring functionality, and verifying system performance. Learn what is dynamic testing, its types, phases, and more.**&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2&gt;
  
  
  Opening specified version of Chromium browser with Pyppeteer
&lt;/h2&gt;

&lt;p&gt;As stated in the earlier sections, Pyppeteer also provides the flexibility to use a specified version of Chromium. The version can be specified in the &lt;em&gt;PYPPETEER_CHROMIUM_REVISION&lt;/em&gt; environment variable.&lt;/p&gt;

&lt;p&gt;The other option is downloading the required version of the Chromium browser on the machine. After that, we have to pass the path to &lt;em&gt;launch()&lt;/em&gt; method of the &lt;em&gt;Launcher *class. The *executablePath&lt;/em&gt; option in the &lt;em&gt;launch()&lt;/em&gt; method lets you run the specified Chromium version instead of the default bundled Chromium.&lt;/p&gt;

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

&lt;p&gt;To run a particular version of Chromium on the machine (e.g., macOS in my case), we downloaded a couple of Chromium versions from the following locations:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.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%2AfrP9UtxxYI4ZOsKlC42vBQ.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.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%2AfrP9UtxxYI4ZOsKlC42vBQ.png" width="800" height="247"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;I have uploaded the downloaded different Chromium browser versions in the &lt;a href="https://drive.google.com/drive/folders/1CDBGc2PmbiOKZtH66BSqwmyt3JzMnfF3?usp=sharing" rel="noopener noreferrer"&gt;mac-chrome&lt;/a&gt; folder.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.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%2A1CSk_j7wEEDQlGqK.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.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%2A1CSk_j7wEEDQlGqK.png" width="800" height="293"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Once downloaded, you need to copy the different versions of Chromium in the &lt;em&gt;mac-chrome&lt;/em&gt; folder in the root of the working directory.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.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%2Avv6Y6O2huY6AyRZA.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.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%2Avv6Y6O2huY6AyRZA.png" width="800" height="400"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;With this, we are all set to run Pyppeteer with the different versions of the Chromium browser!&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Demonstration&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;In the sample shown below, choosing Chromium versions (&lt;em&gt;default&lt;/em&gt;, &lt;em&gt;121&lt;/em&gt;, and &lt;em&gt;113&lt;/em&gt;) is flexible depending on the value of the &lt;em&gt;CHROMIUM_VERSION&lt;/em&gt; environment variable. For this example, you must ensure that EXEC_PLATFORM is set to &lt;em&gt;local&lt;/em&gt;.&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;import asyncio
import pytest
from pyppeteer.errors import PageError
from urllib.parse import quote
import json
import os
import sys
from os import environ
from pyppeteer import connect, launch


exec_platform = os.getenv('EXEC_PLATFORM')


# Can take values - headless and non-headless
chromium_version = os.getenv('CHROMIUM_VERSION')


# Pytest fixture for browser setup
@pytest.fixture(scope='function')
async def browser():
   if exec_platform == 'local':
       if chromium_version == '121':
           custom_chrome_path = "mac-chrome/Chromium_121.app/Contents/MacOS/Chromium"
       elif chromium_version == '113':
           custom_chrome_path = "mac-chrome/Chromium_113.app/Contents/MacOS/Chromium"
       else:
           custom_chrome_path = "mac-chrome/Chromium.app/Contents/MacOS/Chromium"

   browser = await launch(headless = False,
               executablePath = custom_chrome_path, args=['--start-maximized'])
   yield browser
   await asyncio.sleep(1)   
   await browser.close()


# Pytest fixture for page setup
@pytest.fixture(scope='function')
async def page(browser):
   page = await browser.newPage()
   yield page
   await page.close()


# Ported code from https://github.com/LambdaTest/puppeteer-sample/blob/main/puppeteer-parallel.js


@pytest.mark.asyncio
async def test_exe_path(page):
   await page.goto('https://www.duckduckgo.com')
   await page.setViewport({'width': 1920, 'height': 1080})


   element = await page.querySelector('[name="q"]')
   await element.click()
   await element.type('LambdaTest')
   await asyncio.gather(
       page.keyboard.press('Enter'),
       page.waitForNavigation()
   )


   page_title = await page.title()


   try:
       assert page_title == 'LambdaTest at DuckDuckGo', 'Expected page title is incorrect!'
       await page.evaluate('_ =&amp;gt; {}', f'lambdatest_action: {json.dumps({ "action": "setTestStatus", "arguments": { "status": "passed", "remark": "Title matched" } })}')
   except PageError as e:
       await page.evaluate('_ =&amp;gt; {}', f'lambdatest_action: {json.dumps({ "action": "setTestStatus", "arguments": { "status": "failed", "remark": str(e) } })}')
&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;To get started, we read the value of the environment variable &lt;em&gt;CHROMIUM_VERSION&lt;/em&gt;.&lt;/p&gt;

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

&lt;p&gt;Once the value of the environment variable is read, the custom path is assigned to *executablePath *[of the *launch() *method].&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.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%2AdKjfyo2ccP3xcGbp.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.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%2AdKjfyo2ccP3xcGbp.png" width="800" height="400"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;As seen in the modified pytest fixture implementation, the custom Chromium browser is launched in non-headless mode and maximized state.&lt;/p&gt;

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

&lt;p&gt;As stated in the official documentation, Pyppeteer works best with the version of Chromium it is bundled with. However, we did not encounter any issues when testing with the custom versions of the Chromium!&lt;/p&gt;

&lt;p&gt;In the test method [i.e. &lt;em&gt;test_exe_path()&lt;/em&gt;], we first set the viewport size to (1920* 1080) using the &lt;em&gt;setViewport()&lt;/em&gt; method.&lt;/p&gt;

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

&lt;p&gt;After that, we locate the text-box element in the &lt;a href="https://www.duckduckgo.com/" rel="noopener noreferrer"&gt;DuckDuckGo&lt;/a&gt; search page with the CSS Selector.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.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%2A6FkdmMtuJlR4eltV.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.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%2A6FkdmMtuJlR4eltV.png" width="800" height="400"&gt;&lt;/a&gt;&lt;/p&gt;

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

&lt;p&gt;Next, we enter the search term LambdaTest in the search box. As a part of &lt;em&gt;asyncio.gather()&lt;/em&gt;, we first trigger the &lt;em&gt;Enter&lt;/em&gt; key via the &lt;em&gt;press()&lt;/em&gt; method of the &lt;em&gt;keyboard *class. The *waitForNavigation()&lt;/em&gt; method waits (for a maximum of 30000 ms) for the navigation event to occur before it proceeds to the next step in execution.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.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%2A1ZdPSOHSttQ7AiUu.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.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%2A1ZdPSOHSttQ7AiUu.png" width="800" height="400"&gt;&lt;/a&gt;&lt;/p&gt;

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

&lt;p&gt;We first set the &lt;em&gt;EXEC_PLATFORM&lt;/em&gt; variable to &lt;em&gt;local&lt;/em&gt;. For execution on Chromium version 121, set the &lt;em&gt;CHROMIUM_VERSION&lt;/em&gt; to 121 by triggering the &lt;em&gt;export CHROMIUM_VERSION=121&lt;/em&gt; command on the terminal. Run the &lt;em&gt;make pyppeteer-custom-chromium-version&lt;/em&gt; command to run the automated test on the specified Chromium version.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.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%2AP4LC2K0DUOJrZZOx.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.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%2AP4LC2K0DUOJrZZOx.png" width="800" height="411"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;To verify the Chromium version, we temporarily disabled closing (or releasing resources) of the Page &amp;amp; Browser.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.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%2ALCZtU5r2kntap-bx.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.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%2ALCZtU5r2kntap-bx.png" width="800" height="400"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;As seen below, the tests are rightly triggered on Chromium v121!&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.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%2ALBqJ7XbY0Lud3XqE.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.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%2ALBqJ7XbY0Lud3XqE.png" width="800" height="400"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;For execution on Chromium version 113, set the &lt;em&gt;CHROMIUM_VERSION&lt;/em&gt; to 113 by triggering the &lt;em&gt;export CHROMIUM_VERSION=113&lt;/em&gt; command on the terminal. Run the &lt;em&gt;make pyppeteer-custom-chromium-version&lt;/em&gt; command to run the automated test on the specified Chromium version.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.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%2AMGnovhSjx084f8mV.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.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%2AMGnovhSjx084f8mV.png" width="800" height="400"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.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%2ADgQzdBnSu5cLaA9h.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.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%2ADgQzdBnSu5cLaA9h.png" width="800" height="400"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Check out &lt;a href="https://www.lambdatest.com/blog/top-java-frameworks/?utm_source=devto&amp;amp;utm_medium=organic&amp;amp;utm_campaign=feb_05&amp;amp;utm_term=bh&amp;amp;utm_content=blog" rel="noopener noreferrer"&gt;top Java Frameworks&lt;/a&gt; to use in 2023. Elevate your web development with top Java frameworks such as JUnit, Spring, Hibernate, Play, Apache Struts, Blade, Grails, and more.&lt;/strong&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2&gt;
  
  
  Switching tabs with Pyppeteer
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://www.lambdatest.com/blog/switch-tabs-in-browser-using-selenium-python/?utm_source=devto&amp;amp;utm_medium=organic&amp;amp;utm_campaign=feb_05&amp;amp;utm_term=bh&amp;amp;utm_content=blog" rel="noopener noreferrer"&gt;Switching tabs in a browser&lt;/a&gt; is very commonly used when automating browser-based test scenarios. The same principle also applies to Pyppeteer as well. Every invocation of the *newPage() *method in Pyppeteer returns a Page Object. This becomes the identifier of the page, similar to window handles in Selenium. To learn more about it you can go through this blog on &lt;a href="https://www.lambdatest.com/blog/selenium-c-tutorial-handling-multiple-browser-windows/?utm_source=devto&amp;amp;utm_medium=organic&amp;amp;utm_campaign=feb_05&amp;amp;utm_term=bh&amp;amp;utm_content=blog" rel="noopener noreferrer"&gt;handling windows in Selenium&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;In case more than one browser page is open, you use the &lt;em&gt;targets()&lt;/em&gt; method that returns a list consisting of all active targets inside the browser. Switching to a certain tab (or activating a tab) in Pyppeteer is done via the &lt;em&gt;bringToFront()&lt;/em&gt; method of the &lt;em&gt;Page&lt;/em&gt; class.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Demonstration&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;For demonstration, let’s open up two browser tabs and switch to the first tab using the &lt;em&gt;bringToFront()&lt;/em&gt; coroutine.&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;import asyncio
import pytest
from pyppeteer.errors import PageError
from urllib.parse import quote
import json
import os
import sys
from os import environ
from pyppeteer import connect, launch


exec_platform = os.getenv('EXEC_PLATFORM')


@pytest.mark.asyncio
async def test_bring_to_front(browser, page):
   await page.goto('https://www.duckduckgo.com', {'waitUntil' : 'networkidle2'})
   # Maximize the page
   await page.setViewport({'width': 1920, 'height': 1080})


   element = await page.querySelector('[name="q"]')
   await element.click()
   await element.type('LambdaTest')
   await asyncio.gather(
       page.keyboard.press('Enter'),
       page.waitForNavigation()
   )


   # Use asyncio.sleep within the async test function
   # await asyncio.sleep(1)


   page_title = await page.title()


   try:
       assert page_title == 'LambdaTest at DuckDuckGo', 'Expected page title is incorrect!'
       await page.evaluate('_ =&amp;gt; {}', f'lambdatest_action: {json.dumps({ "action": "setTestStatus", "arguments": { "status": "passed", "remark": "Title matched" } })}')
   except PageError as e:
       await page.evaluate('_ =&amp;gt; {}', f'lambdatest_action: {json.dumps({ "action": "setTestStatus", "arguments": { "status": "failed", "remark": str(e) } })}')



   page_1 = await browser.newPage()
   await page_1.goto('https://www.lambdatest.com', {'waitUntil' : 'networkidle2'})


   await page.bringToFront()
   await asyncio.sleep(2)
&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;Since the test scenario remains unchanged, we would only focus on how tabs are switched in the example. First, we open a new page with the URL under test set to DuckDuckGo.&lt;/p&gt;

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

&lt;p&gt;Next, we open a new page with the URL under test set to &lt;a href="https://automation.lambdatest.com/?utm_source=devto&amp;amp;utm_medium=organic&amp;amp;utm_campaign=feb_05&amp;amp;utm_term=bh&amp;amp;utm_content=webpage" rel="noopener noreferrer"&gt;LambdaTest&lt;/a&gt;. As seen below, the page identifier is set to &lt;em&gt;page_1&lt;/em&gt;.&lt;/p&gt;

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

&lt;p&gt;For switching to the earlier tab, we invoke the &lt;em&gt;bringToFront()&lt;/em&gt; method on the page object — &lt;em&gt;page&lt;/em&gt;.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.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-rGeE39RZpW8hwvv.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.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-rGeE39RZpW8hwvv.png" width="666" height="376"&gt;&lt;/a&gt;&lt;/p&gt;

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

&lt;p&gt;Invoke the command &lt;em&gt;make pyppeteer-activate-tab&lt;/em&gt; on the terminal with the Chromium browser instantiated on the local machine.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.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%2A0WGPUT4Ia1d09g4u.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.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%2A0WGPUT4Ia1d09g4u.png" width="800" height="490"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;As seen in the execution screenshot, the older tab (where the DuckDuckGo scenario is run) is brought back to focus.&lt;/p&gt;

&lt;h2&gt;
  
  
  Taking screenshots with Pyppeteer
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://www.lambdatest.com/blog/how-to-capture-screenshots-in-selenium-guide-with-examples/?utm_source=devto&amp;amp;utm_medium=organic&amp;amp;utm_campaign=feb_05&amp;amp;utm_term=bh&amp;amp;utm_content=blog" rel="noopener noreferrer"&gt;Capturing screenshots with Selenium&lt;/a&gt;, Pyppeteer, Cypress, etc., is a common practice for isolating and debugging visual issues in the website/app. Screenshots are also essential for &lt;a href="https://www.lambdatest.com/learning-hub/visual-regression-testing?utm_source=devto&amp;amp;utm_medium=organic&amp;amp;utm_campaign=feb_05&amp;amp;utm_term=bh&amp;amp;utm_content=learning_hub" rel="noopener noreferrer"&gt;visual regression testing&lt;/a&gt;, as it helps in comparing captured screenshots with the baseline screenshots.&lt;/p&gt;

&lt;p&gt;Apart from identifying visual inconsistencies, screenshots (full-page or element-level) provide a visual confirmation of the page at a particular point in time. The &lt;em&gt;screenshot()&lt;/em&gt; method of the *Page *class helps capture a screenshot of the entire page or a particular element that is currently under focus.&lt;/p&gt;

&lt;p&gt;The &lt;em&gt;screenshot()&lt;/em&gt; method throws an &lt;em&gt;ElementHandleError&lt;/em&gt; in case the target element is detached from the &lt;a href="https://www.lambdatest.com/blog/document-object-model/?utm_source=devto&amp;amp;utm_medium=organic&amp;amp;utm_campaign=feb_05&amp;amp;utm_term=bh&amp;amp;utm_content=blog" rel="noopener noreferrer"&gt;DOM&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;For capturing element screenshot, we first capture the element using an appropriate locator. Once located, the &lt;em&gt;boundingBox()&lt;/em&gt; method is used to calculate the bounding box of the element.&lt;/p&gt;

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

&lt;p&gt;&lt;a href="https://developer.mozilla.org/en-US/docs/Web/API/Element/getBoundingClientRect/element-box-diagram.png" rel="noopener noreferrer"&gt;*BoundingBox&lt;/a&gt;*&lt;/p&gt;

&lt;p&gt;The &lt;em&gt;boundingBox()&lt;/em&gt; method returns a dictionary of items mentioned below:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;&lt;em&gt;x&lt;/em&gt; (integer): X coordinate value in pixels&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;em&gt;y&lt;/em&gt; (integer): Y coordinate value in pixels&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;*width *(integer): Element width in pixels&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;*height *(integer): Element height in pixels&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Demonstration&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;For demonstrating capturing screenshots with Pyppeteer, we automate the following test scenario on Chromium installed on the local machine (&lt;em&gt;EXEC_PLATFORM&lt;/em&gt; = &lt;em&gt;local&lt;/em&gt;):&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;&lt;p&gt;Navigate to the &lt;a href="https://ecommerce-playground.lambdatest.io/index.php?route=product/category&amp;amp;path=30" rel="noopener noreferrer"&gt;Product Page&lt;/a&gt;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Locate the required element using the most-suited selector.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Click on the located element.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Capture full-page screenshots and element screenshots.&lt;/p&gt;
&lt;h1&gt;
  
  
  Further details of screenshot API
&lt;/h1&gt;
&lt;h1&gt;
  
  
  &lt;a href="https://miyakogi.github.io/pyppeteer/reference.html#pyppeteer.page.Page.screenshot" rel="noopener noreferrer"&gt;https://miyakogi.github.io/pyppeteer/reference.html#pyppeteer.page.Page.screenshot&lt;/a&gt;
&lt;/h1&gt;

&lt;p&gt;import asyncio&lt;br&gt;
import pytest&lt;br&gt;
from pyppeteer.errors import PageError&lt;br&gt;
from urllib.parse import quote&lt;br&gt;
import os&lt;br&gt;
import sys&lt;br&gt;
from os import environ&lt;br&gt;
from pyppeteer import connect, launch&lt;/p&gt;

&lt;p&gt;exec_platform = os.getenv('EXEC_PLATFORM')&lt;/p&gt;

&lt;p&gt;timeOut = 60000&lt;/p&gt;

&lt;p&gt;test_url = '&lt;a href="https://ecommerce-playground.lambdatest.io/index.php?route=product/category&amp;amp;path=30" rel="noopener noreferrer"&gt;https://ecommerce-playground.lambdatest.io/index.php?route=product/category&amp;amp;path=30&lt;/a&gt;'&lt;br&gt;
loc_product_1 = "#mz-product-grid-image-43-212408 &amp;gt; div &amp;gt; div.carousel-item.active &amp;gt; img"&lt;br&gt;
loc_final_product = "#image-gallery-216811 &amp;gt; div.image-thumb.d-flex &amp;gt; a &amp;gt; img"&lt;br&gt;
target_url = "&lt;a href="https://ecommerce-playground.lambdatest.io/index.php?route=product/product&amp;amp;path=25_30&amp;amp;product_id=43" rel="noopener noreferrer"&gt;https://ecommerce-playground.lambdatest.io/index.php?route=product/product&amp;amp;path=25_30&amp;amp;product_id=43&lt;/a&gt;"&lt;/p&gt;

&lt;p&gt;@pytest.mark.asyncio&lt;br&gt;
@pytest.mark.order(1)&lt;br&gt;
async def test_screenshot(page):&lt;br&gt;
   # The time out can be set using the setDefaultNavigationTimeout&lt;br&gt;
   # It is primarily used for overriding the default page timeout of 30 seconds&lt;br&gt;
   page.setDefaultNavigationTimeout(timeOut)&lt;br&gt;
   await page.goto(test_url,&lt;br&gt;
       {'waitUntil': 'networkidle2', 'timeout': timeOut})&lt;/p&gt;

&lt;p&gt;await asyncio.sleep(1)&lt;/p&gt;

&lt;p&gt;# Wait for the element to be present in the DOM&lt;br&gt;
   elem_prod_link = await page.waitForSelector(loc_product_1, {'visible': True})&lt;br&gt;
   # elem_prod_link = await page.querySelector(loc_product_1)&lt;/p&gt;

&lt;p&gt;await asyncio.sleep(2)&lt;/p&gt;

&lt;p&gt;await asyncio.gather(&lt;br&gt;
       elem_prod_link.click(),&lt;br&gt;
       page.waitForNavigation()&lt;br&gt;
   )&lt;/p&gt;

&lt;p&gt;# Assert if required, since the test is a simple one; we leave as is :D&lt;br&gt;
   current_url = page.url&lt;br&gt;
   print('Current URL is: ' + current_url)&lt;/p&gt;

&lt;p&gt;try:&lt;br&gt;
       assert current_url == target_url&lt;br&gt;
       print("Test Success: Product checkout successful")&lt;br&gt;
   except PageError as e:&lt;br&gt;
       print("Test Failure: Could not checkout Product")&lt;br&gt;
       print("Error Code" + str(e))&lt;/p&gt;

&lt;p&gt;await asyncio.sleep(2)&lt;/p&gt;

&lt;p&gt;# Wait for the element to be present in the DOM&lt;br&gt;
   await page.waitForSelector(loc_final_product)&lt;br&gt;
   elem_prod_img = await page.querySelector(loc_final_product)&lt;/p&gt;

&lt;p&gt;# Further information on boundingBox&lt;br&gt;
   # &lt;a href="https://miyakogi.github.io/pyppeteer/reference.html#pyppeteer.element_handle.ElementHandle.boundingBox" rel="noopener noreferrer"&gt;https://miyakogi.github.io/pyppeteer/reference.html#pyppeteer.element_handle.ElementHandle.boundingBox&lt;/a&gt;&lt;br&gt;
   bounding_box = await elem_prod_img.boundingBox()&lt;/p&gt;

&lt;p&gt;# Take a screenshot of the element&lt;br&gt;
   if bounding_box:&lt;br&gt;
       await elem_prod_img.screenshot({'path': 'product-screenshot.png', 'clip': bounding_box})&lt;/p&gt;

&lt;p&gt;await asyncio.sleep(1)&lt;/p&gt;

&lt;p&gt;# Take a screenshot of the entire page&lt;br&gt;
   await page.screenshot({'path': 'page-screenshot.png'})&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;

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

&lt;p&gt;We first navigate to the &lt;a href="https://ecommerce-playground.lambdatest.io/index.php?route=product/category&amp;amp;path=30" rel="noopener noreferrer"&gt;LambdaTest eCommerce Playground&lt;/a&gt; using the *goto() *method of the *Page *class.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.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%2AvYZ6upflExjSsoYS.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.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%2AvYZ6upflExjSsoYS.png" width="800" height="179"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Next, we locate the required element using the CSS Selector property. Instead of CSS Selector, you can also use &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=feb_05&amp;amp;utm_term=bh&amp;amp;utm_content=blog" rel="noopener noreferrer"&gt;XPath&lt;/a&gt;, in which case you would need to use &lt;em&gt;waitForXPath() *instead of the *waitForSelector()&lt;/em&gt; method.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.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%2AZrd7N7n5QbyNPFQo.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.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%2AZrd7N7n5QbyNPFQo.png" width="800" height="500"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.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%2AEGBkreG4ZGhMQSXg.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.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%2AEGBkreG4ZGhMQSXg.png" width="800" height="400"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Click operation is performed on the located element using the &lt;em&gt;click()&lt;/em&gt; method. Like before, the &lt;em&gt;waitForNavigation()&lt;/em&gt; method after the click helps wait for the completion of the navigation operation before the browser proceeds with further actions.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.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%2AVG1aEFALHSVPRyvk.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.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%2AVG1aEFALHSVPRyvk.png" width="716" height="460"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The product image is located via the CSS Selector. The &lt;em&gt;querySelector()&lt;/em&gt; method of the &lt;em&gt;Page *class returns the *ElementHandle&lt;/em&gt; of the element that matches the selector.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.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%2A1j7uM0ISlYVBb-_z.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.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%2A1j7uM0ISlYVBb-_z.png" width="800" height="500"&gt;&lt;/a&gt;&lt;/p&gt;

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

&lt;p&gt;Next, the &lt;em&gt;boundingBox()&lt;/em&gt; method on the located element returns a dictionary of the bounding box.&lt;/p&gt;

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

&lt;p&gt;To capture a screenshot of the element, the &lt;em&gt;screenshot()&lt;/em&gt; method is used with the &lt;em&gt;clip&lt;/em&gt; option that specifies an object (of type dict), which specifies the clipping region of the page. The screenshot is saved as &lt;em&gt;product-screenshot.png&lt;/em&gt;.&lt;/p&gt;

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

&lt;p&gt;&lt;a href="https://media2.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%2AVah8AT2Fm7skxrbM.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.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%2AVah8AT2Fm7skxrbM.png" width="800" height="186"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The page screenshot (saved as &lt;em&gt;page-screenshot.png&lt;/em&gt;) is captured by invoking the &lt;em&gt;screenshot()&lt;/em&gt; method. The &lt;em&gt;fullPage&lt;/em&gt; option is set to &lt;em&gt;False&lt;/em&gt; (default) and takes a screenshot of the page currently under display.&lt;/p&gt;

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

&lt;p&gt;Full-page (or scrollable) screenshot is captured by setting the &lt;em&gt;fullPage&lt;/em&gt; option to &lt;em&gt;True&lt;/em&gt;.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.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%2AJmYYLaBIQxCB8Ofm.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.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%2AJmYYLaBIQxCB8Ofm.png" width="800" height="183"&gt;&lt;/a&gt;&lt;/p&gt;

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

&lt;p&gt;Invoke the command &lt;em&gt;make pyppeteer-generate-screenshot&lt;/em&gt; on the terminal with the Chromium browser instantiated on the local machine. As seen below, the test execution is successful.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.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%2AtegLOTPsqy1oDKpi.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.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%2AtegLOTPsqy1oDKpi.png" width="800" height="423"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The full-page screenshot is below:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.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%2Ae4DngmW398JMmm3z.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.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%2Ae4DngmW398JMmm3z.png" width="800" height="502"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The scrollable-page screenshot is below:&lt;/p&gt;

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

&lt;p&gt;The element screenshot is below:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.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%2AXQHStqvBU3EPIijF.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.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%2AXQHStqvBU3EPIijF.png" width="750" height="998"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;In summary, the &lt;em&gt;screenshot()&lt;/em&gt; method of Pyppeteer has all the options to realize &lt;a href="https://www.lambdatest.com/learning-hub/python-visual-regression-testing?utm_source=devto&amp;amp;utm_medium=organic&amp;amp;utm_campaign=feb_05&amp;amp;utm_term=bh&amp;amp;utm_content=learning_hub" rel="noopener noreferrer"&gt;visual regression testing with Python&lt;/a&gt;!&lt;/p&gt;

&lt;h2&gt;
  
  
  Managing cookies with Pyppeteer
&lt;/h2&gt;

&lt;p&gt;Website cookies plays an integral role in automated testing. Modifying cookies can be useful for test scenarios related to session management, &lt;a href="https://www.lambdatest.com/blog/how-to-perform-localization-testing/?utm_source=devto&amp;amp;utm_medium=organic&amp;amp;utm_campaign=feb_05&amp;amp;utm_term=bh&amp;amp;utm_content=blog" rel="noopener noreferrer"&gt;localization testing&lt;/a&gt;, handling cookie consent, and &lt;a href="https://www.lambdatest.com/online-browser-testing?utm_source=devto&amp;amp;utm_medium=organic&amp;amp;utm_campaign=feb_05&amp;amp;utm_term=bh&amp;amp;utm_content=webpage" rel="noopener noreferrer"&gt;cross browser testing&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;I recommend reading this detailed blog on &lt;a href="https://www.lambdatest.com/blog/handling-cookies-in-selenium-webdriver/?utm_source=devto&amp;amp;utm_medium=organic&amp;amp;utm_campaign=feb_05&amp;amp;utm_term=bh&amp;amp;utm_content=blog" rel="noopener noreferrer"&gt;handling cookies with Selenium&lt;/a&gt;, as overall cookie handling principles remain unchanged irrespective of the framework (or library) used for &lt;a href="https://www.lambdatest.com/learning-hub/automation-testing?utm_source=devto&amp;amp;utm_medium=organic&amp;amp;utm_campaign=feb_05&amp;amp;utm_term=bh&amp;amp;utm_content=learning_hub" rel="noopener noreferrer"&gt;automated testing&lt;/a&gt;. The Pyppeteer library provides the following methods (or coroutines) for cookie management:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.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%2Avh_W_p4m-nUBiR6eLEUpLQ.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.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%2Avh_W_p4m-nUBiR6eLEUpLQ.png" width="800" height="381"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Demonstration&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;For demonstrating cookie management with Pyppeteer, we automate the following test scenario on Chromium installed on the local machine (&lt;em&gt;EXEC_PLATFORM&lt;/em&gt; = &lt;em&gt;local&lt;/em&gt;):&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;&lt;p&gt;Navigate to &lt;a href="https://ecommerce-playground.lambdatest.io/" rel="noopener noreferrer"&gt;LambdaTest eCommerce Playground&lt;/a&gt;.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Print cookies.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Set a new cookie.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Delete a cookie and print the cookies.&lt;/p&gt;

&lt;p&gt;import asyncio&lt;br&gt;
import pytest&lt;br&gt;
from pyppeteer.errors import PageError&lt;br&gt;
from urllib.parse import quote&lt;br&gt;
import json&lt;br&gt;
import os&lt;br&gt;
import sys&lt;br&gt;
from os import environ&lt;br&gt;
from pyppeteer import connect, launch&lt;/p&gt;

&lt;p&gt;exec_platform = os.getenv('EXEC_PLATFORM')&lt;br&gt;
test_url = '&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;cookie_dict = {}&lt;/p&gt;

&lt;p&gt;@pytest.mark.asyncio&lt;br&gt;
@pytest.mark.order(1)&lt;br&gt;
async def test_get_cookie_info(page):&lt;br&gt;
   await page.goto(test_url)&lt;/p&gt;

&lt;p&gt;await asyncio.sleep(1)&lt;/p&gt;

&lt;p&gt;# Output is a list of dictionaries&lt;br&gt;
   # &lt;a href="https://miyakogi.github.io/pyppeteer/reference.html#pyppeteer.page.Page.cookies" rel="noopener noreferrer"&gt;https://miyakogi.github.io/pyppeteer/reference.html#pyppeteer.page.Page.cookies&lt;/a&gt;&lt;br&gt;
   cookie_dict = await page.cookies()&lt;/p&gt;

&lt;p&gt;await asyncio.sleep(1)&lt;/p&gt;

&lt;p&gt;# Let's print values of all the cookies&lt;br&gt;
   for cookie in cookie_dict:&lt;br&gt;
       print("Cookie Name:")&lt;br&gt;
       for key, val in cookie.items():&lt;br&gt;
           print(f'  {key}: {val}')&lt;br&gt;
       print()&lt;/p&gt;

&lt;p&gt;@pytest.mark.asyncio&lt;br&gt;
@pytest.mark.order(2)&lt;br&gt;
async def test_delete_cookies(page):&lt;br&gt;
   before_del_cookies = {}&lt;br&gt;
   after_del_cookies = {}&lt;/p&gt;

&lt;p&gt;await page.goto(test_url)&lt;/p&gt;

&lt;p&gt;await asyncio.sleep(12)&lt;/p&gt;

&lt;p&gt;# &lt;a href="https://miyakogi.github.io/pyppeteer/reference.html#pyppeteer.page.Page.cookies" rel="noopener noreferrer"&gt;https://miyakogi.github.io/pyppeteer/reference.html#pyppeteer.page.Page.cookies&lt;/a&gt;&lt;br&gt;
   # Create/Add a new cookie&lt;br&gt;
   await page.setCookie({'name': 'pyppeteer', 'value': 'v1.0.2'})&lt;/p&gt;

&lt;p&gt;before_del_cookies = await page.cookies()&lt;br&gt;
   print("\nBefore deletion\n")&lt;/p&gt;

&lt;p&gt;print_cookie(before_del_cookies)&lt;/p&gt;

&lt;p&gt;# Delete cookies&lt;br&gt;
   await page.deleteCookie({'name': 'pyppeteer'})&lt;/p&gt;

&lt;p&gt;after_del_cookies = await page.cookies()&lt;br&gt;
   print("After deletion\n")&lt;/p&gt;

&lt;p&gt;print_cookie(after_del_cookies)&lt;/p&gt;

&lt;p&gt;def print_cookie(cookie_info):&lt;br&gt;
   # Let's print values of all the cookies&lt;br&gt;
   for cookie in cookie_info:&lt;br&gt;
       print("Cookie Name:")&lt;br&gt;
       for key, val in cookie.items():&lt;br&gt;
           print(f'  {key}: {val}')&lt;br&gt;
       print()&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;

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

&lt;p&gt;After navigating to the URL under test, get the cookies by invoking the &lt;em&gt;cookies()&lt;/em&gt; method of the *Page *class.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.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%2AOjeLwuWxAgEqL95x.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.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%2AOjeLwuWxAgEqL95x.png" width="800" height="400"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The &lt;em&gt;cookies()&lt;/em&gt; method returns a dictionary of &lt;em&gt;key: value&lt;/em&gt; pairs. An iterative &lt;em&gt;For&lt;/em&gt; loop is used for printing the value of each cookie.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.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%2AhsbFxSMD9aumkcr4.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.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%2AhsbFxSMD9aumkcr4.png" width="800" height="400"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Next up, we set a new cookie &lt;em&gt;pyppeteer *to *v1.0.2&lt;/em&gt; value with the &lt;em&gt;setCookie()&lt;/em&gt; method of the *Page *class.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.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%2AqYiKzC2SPcZDkBao.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.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%2AqYiKzC2SPcZDkBao.png" width="800" height="400"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;With the new cookie set, we delete the cookie &lt;em&gt;pyppeteer&lt;/em&gt; by invoking the &lt;em&gt;deleteCookie()&lt;/em&gt; method, where the cookie name is passed as a parameter to the method.&lt;/p&gt;

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

&lt;p&gt;At each step, cookies are printed before &amp;amp; after the deletion of cookies.&lt;/p&gt;

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

&lt;p&gt;Invoke the command &lt;em&gt;make pyppeteer-cookies&lt;/em&gt; on the terminal with the Chromium browser instantiated on the local machine. As seen below, the newly-added cookie &lt;em&gt;pyppeteer&lt;/em&gt; is successfully set to &lt;em&gt;v1.0.2&lt;/em&gt;, and the cookie added by the &lt;em&gt;setCookie()&lt;/em&gt; method was successfully deleted by the &lt;em&gt;deleteCookie()&lt;/em&gt; method.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.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%2Afolqu3zBTphg7jwT.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.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%2Afolqu3zBTphg7jwT.png" width="800" height="512"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Handling iFrames with Pyppeteer
&lt;/h2&gt;

&lt;p&gt;iFrames (or inline frames) are commonly used in websites to insert a document from an entirely different domain. &lt;a href="https://www.lambdatest.com/blog/handling-frames-and-iframes-selenium-c-sharp/?utm_source=devto&amp;amp;utm_medium=organic&amp;amp;utm_campaign=feb_05&amp;amp;utm_term=bh&amp;amp;utm_content=blog" rel="noopener noreferrer"&gt;Handling iFrames with Selenium&lt;/a&gt;, Cypress, Pyppeteer, and other automation frameworks is a common test scenario for automating interactions with modern websites.&lt;/p&gt;

&lt;p&gt;The &lt;em&gt;&amp;lt; iframe &amp;gt;&lt;/em&gt; tag in HTML specifies an inline frame. Every HTML page (or document) has a top-level frame called a parent frame. The parent (or main) frame is the container of the child frames or iFrames. Shown below is an example of an iFrame from &lt;a href="https://www.lambdatest.com/selenium-playground/iframe-demo/?utm_source=devto&amp;amp;utm_medium=organic&amp;amp;utm_campaign=feb_05&amp;amp;utm_term=bh&amp;amp;utm_content=webpage" rel="noopener noreferrer"&gt;LambdaTest Selenium Playground&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.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%2A_iEqz4IhhwuFlj-U.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.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%2A_iEqz4IhhwuFlj-U.png" width="800" height="499"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;em&gt;iFrame Example&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;The methods (or coroutines) for accessing iFrames with Pyppeteer are available in the &lt;a href="https://miyakogi.github.io/pyppeteer/reference.html#frame-class" rel="noopener noreferrer"&gt;*Frame&lt;/a&gt;* class. Here are some prominent methods for handling iFrames with Pyppeteer:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.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%2ApELAkkoBd6S5XrLagnDq7A.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.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%2ApELAkkoBd6S5XrLagnDq7A.png" width="800" height="400"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Demonstration&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;For demonstrating iFrame handling with Pyppeteer, we automate the following test scenario on Chromium installed on the local machine (&lt;em&gt;EXEC_PLATFORM&lt;/em&gt; = &lt;em&gt;local&lt;/em&gt;):&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;&lt;p&gt;Navigate to &lt;a href="https://www.lambdatest.com/selenium-playground/iframe-demo/?utm_source=devto&amp;amp;utm_medium=organic&amp;amp;utm_campaign=feb_05&amp;amp;utm_term=bh&amp;amp;utm_content=webpage" rel="noopener noreferrer"&gt;Selenium Playground iFrame Demo&lt;/a&gt;.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Locate the &lt;em&gt;first iFrame&lt;/em&gt; using the best-suited selector.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Enter the message — &lt;em&gt;LambdaTest is an awesome platform!&lt;/em&gt; in the textbox inside the iFrame.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Capture a screenshot of the Page for verification.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Locate the second &lt;em&gt;iFrame&lt;/em&gt; using the best-suited selector.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Scroll to the element of the search string &lt;em&gt;Playwright Testing&lt;/em&gt;.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Capture a screenshot of the Page for verification.&lt;/p&gt;

&lt;p&gt;import asyncio&lt;br&gt;
import pytest&lt;br&gt;
from pyppeteer.errors import PageError&lt;br&gt;
from urllib.parse import quote&lt;br&gt;
import os&lt;br&gt;
import sys&lt;br&gt;
from os import environ&lt;br&gt;
from pyppeteer import connect, launch&lt;/p&gt;

&lt;p&gt;exec_platform = os.getenv('EXEC_PLATFORM')&lt;/p&gt;

&lt;p&gt;test_url = '&lt;a href="https://www.lambdatest.com/selenium-playground/iframe-demo/" rel="noopener noreferrer"&gt;https://www.lambdatest.com/selenium-playground/iframe-demo/&lt;/a&gt;'&lt;/p&gt;
&lt;h1&gt;
  
  
  Selectors of the page
&lt;/h1&gt;
&lt;h1&gt;
  
  
  Simple iFrame containing Editor
&lt;/h1&gt;

&lt;p&gt;loc_iframe_1 = "#iFrame1"&lt;/p&gt;
&lt;h1&gt;
  
  
  Use class name to select an element
&lt;/h1&gt;

&lt;p&gt;loc_iframe_inside_con = ".rsw-ce"&lt;br&gt;
test_message = "LambdaTest is an awesome platform!"&lt;br&gt;
loc_bold_button = "#__next &amp;gt; div &amp;gt; div.rsw-toolbar &amp;gt; button:nth-child(1)"&lt;br&gt;
loc_underline_button = "//span[.='𝐔']"&lt;/p&gt;
&lt;h1&gt;
  
  
  Simple iFrame containing webpage
&lt;/h1&gt;

&lt;p&gt;loc_iframe_2 = "//*[&lt;a class="mentioned-user" href="https://dev.to/id"&gt;@id&lt;/a&gt;='iFrame2']"&lt;br&gt;
loc_playwright_testing = "//a[.='Playwright Testing']"&lt;/p&gt;

&lt;p&gt;async def scroll_to_element(page, element):&lt;br&gt;
   # Scroll until the element is detected&lt;br&gt;
   await page.evaluateHandle(&lt;br&gt;
       '''async (element) =&amp;gt; {&lt;br&gt;
           if (element) {&lt;br&gt;
               element.scrollIntoView();&lt;br&gt;
           }&lt;br&gt;
       }'''&lt;br&gt;
   )&lt;/p&gt;

&lt;p&gt;@pytest.mark.asyncio&lt;br&gt;
@pytest.mark.order(1)&lt;br&gt;
async def test_handling_iframe_1(page):&lt;br&gt;
   await page.goto(test_url)&lt;/p&gt;

&lt;p&gt;# Set the viewport - Apple MacBook Air 13-inch&lt;br&gt;
   # Reference - &lt;a href="https://codekbyte.com/devices-viewport-sizes/" rel="noopener noreferrer"&gt;https://codekbyte.com/devices-viewport-sizes/&lt;/a&gt;&lt;br&gt;
   await page.setViewport({'width': 1440, 'height': 770})&lt;/p&gt;

&lt;p&gt;asyncio.sleep(2)&lt;/p&gt;

&lt;p&gt;# Get the iframe element handle&lt;br&gt;
   iframe_handle = await page.querySelector(loc_iframe_1)&lt;/p&gt;

&lt;p&gt;# Switch to the iframe&lt;br&gt;
   iframe = await iframe_handle.contentFrame()&lt;/p&gt;

&lt;p&gt;# Locate the Search button in the iFrame&lt;br&gt;
   # Perform actions inside the iframe&lt;br&gt;
   elem_text_box = await iframe.querySelector(loc_iframe_inside_con)&lt;/p&gt;

&lt;p&gt;# Get the element inside the view&lt;br&gt;
   await elem_text_box.click()&lt;/p&gt;

&lt;p&gt;await asyncio.sleep(1)&lt;/p&gt;

&lt;p&gt;await elem_text_box.click(clickCount=3)&lt;br&gt;
   await page.keyboard.press('Backspace')&lt;/p&gt;

&lt;p&gt;# Wait for 2000 ms&lt;br&gt;
   await iframe.waitFor(2000)&lt;/p&gt;

&lt;p&gt;await elem_text_box.type(test_message)&lt;br&gt;
   await asyncio.sleep(2)&lt;br&gt;
   await elem_text_box.click(clickCount=3)&lt;/p&gt;

&lt;p&gt;elem_underline_button = await iframe.waitForXPath(loc_underline_button)&lt;br&gt;
   elem_bold_button = await iframe.querySelector(loc_bold_button)&lt;br&gt;
   await asyncio.sleep(1)&lt;br&gt;
   await elem_underline_button.click()&lt;br&gt;
   await asyncio.sleep(1)&lt;br&gt;
   await elem_bold_button.click()&lt;br&gt;
   await asyncio.sleep(1)&lt;/p&gt;

&lt;p&gt;await elem_text_box.click()&lt;/p&gt;

&lt;p&gt;# Switch back to the main frame if needed&lt;br&gt;
   await page.bringToFront()&lt;/p&gt;

&lt;p&gt;await asyncio.sleep(2)&lt;/p&gt;

&lt;p&gt;# Take a screenshot&lt;br&gt;
   await page.screenshot({'path': 'iFrame1-screenshot.png'})&lt;/p&gt;

&lt;p&gt;@pytest.mark.asyncio&lt;br&gt;
@pytest.mark.order(2)&lt;br&gt;
async def test_handling_iframe_2(page):&lt;br&gt;
   await page.goto(test_url)&lt;/p&gt;

&lt;p&gt;# Set the viewport - Apple MacBook Air 13-inch&lt;br&gt;
   # Reference - &lt;a href="https://codekbyte.com/devices-viewport-sizes/" rel="noopener noreferrer"&gt;https://codekbyte.com/devices-viewport-sizes/&lt;/a&gt;&lt;br&gt;
   await page.setViewport({'width': 1440, 'height': 770})&lt;/p&gt;

&lt;p&gt;asyncio.sleep(2)&lt;/p&gt;

&lt;p&gt;# Get the iframe element handle&lt;br&gt;
   iframe_handle = await page.waitForXPath(loc_iframe_2)&lt;/p&gt;

&lt;p&gt;# Switch to the iframe&lt;br&gt;
   iframe = await iframe_handle.contentFrame()&lt;/p&gt;

&lt;p&gt;# Locate the Search button in the iFrame&lt;br&gt;
   # Perform actions inside the iframe&lt;br&gt;
   elem_search = await iframe.waitForXPath(loc_playwright_testing)&lt;/p&gt;

&lt;p&gt;# Get the element inside the view&lt;br&gt;
   await scroll_to_element(iframe, elem_search)&lt;br&gt;
   await elem_search.click()&lt;/p&gt;

&lt;p&gt;await asyncio.sleep(2)&lt;/p&gt;

&lt;p&gt;# Take a screenshot&lt;br&gt;
   await page.screenshot({'path': 'iFrame2-screenshot.png'})&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;

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

&lt;p&gt;After navigating to the i&lt;a href="https://www.lambdatest.com/selenium-playground/iframe-demo/?utm_source=devto&amp;amp;utm_medium=organic&amp;amp;utm_campaign=feb_05&amp;amp;utm_term=bh&amp;amp;utm_content=webpage" rel="noopener noreferrer"&gt;Frame Demo&lt;/a&gt; page, we first locate the iFrame using the ID locator (i.e., &lt;em&gt;iFrame1&lt;/em&gt;). The *querySelector() *method is used to check the presence of the *iFrame *selector.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.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%2AklKvlvQxbbt9OoZn.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.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%2AklKvlvQxbbt9OoZn.png" width="800" height="499"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.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%2ASdB9Rbxr_QgbgIJr.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.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%2ASdB9Rbxr_QgbgIJr.png" width="800" height="400"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Next, we use the &lt;em&gt;contentFrame()&lt;/em&gt; method on the located element to get the content frame for the element handle.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.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%2ANacmAahD3EtvOKZy.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.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%2ANacmAahD3EtvOKZy.png" width="800" height="400"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Now that we have switched to the iFrame, we locate the text-area element using the CSS Selector (i.e., &lt;em&gt;.rsw-ce&lt;/em&gt;). To simplify the process of locating elements, I have used the &lt;a href="https://pombuilder.com/" rel="noopener noreferrer"&gt;POM Builder plugin&lt;/a&gt; on Arc browser.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.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%2AO_InkMvIteG_l5Yr.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.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%2AO_InkMvIteG_l5Yr.png" width="800" height="500"&gt;&lt;/a&gt;&lt;/p&gt;

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

&lt;p&gt;Since the text-area contains the default text, we first select the text by setting the &lt;em&gt;clickCount&lt;/em&gt; to 3 in &lt;em&gt;click()&lt;/em&gt; — a method used for stimulating mouse click on a DOM element.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.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%2AV6r81XxasPRZMP7e.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.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%2AV6r81XxasPRZMP7e.png" width="800" height="538"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.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%2AaRLF_k2UbZUYmkwA.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.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%2AaRLF_k2UbZUYmkwA.png" width="800" height="482"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The content in the text-area is deleted by pressing the Backspace key using the &lt;a href="https://miyakogi.github.io/pyppeteer/reference.html#keyboard-class" rel="noopener noreferrer"&gt;*press()&lt;/a&gt;* method of the &lt;em&gt;Keyboard&lt;/em&gt; class.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.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%2A9LkvtMsAMqOHKSEX.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.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%2A9LkvtMsAMqOHKSEX.png" width="800" height="301"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Enter the required text &lt;em&gt;LambdaTest is an awesome platform!&lt;/em&gt; in the text area by invoking the &lt;em&gt;type()&lt;/em&gt; method on the text-area element. Since we also want to Bold &amp;amp; Italicise the text, we again select the entire text by setting the &lt;em&gt;clickcount&lt;/em&gt; to 3.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.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%2AJD6GuG3mFohgv6t8.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.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%2AJD6GuG3mFohgv6t8.png" width="800" height="390"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.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%2A7KQkyi5z-QnwoYq4.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.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%2A7KQkyi5z-QnwoYq4.png" width="800" height="482"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Since we want to underline the text, we locate the element using the XPath locator. Once the element (i.e., &lt;em&gt;loc_underline_button&lt;/em&gt;) is located, a click operation is performed to underline the text in the text area.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.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%2AzzEmGc_L52uRnnNl.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.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%2AzzEmGc_L52uRnnNl.png" width="800" height="500"&gt;&lt;/a&gt;&lt;/p&gt;

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

&lt;p&gt;Similarly, we locate the element (to bold the text) using the CSS Selector. Once the element (i.e., &lt;em&gt;loc_bold_button&lt;/em&gt;) is located, a click operation is performed to bold the text in the text area.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.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%2ABUlZbu1OJQOG-VCL.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.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%2ABUlZbu1OJQOG-VCL.png" width="800" height="400"&gt;&lt;/a&gt;&lt;/p&gt;

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

&lt;p&gt;We switch back to the main window (or tab) by invoking the &lt;em&gt;bringToFront()&lt;/em&gt; method. Post this, we take a full-page screenshot (&lt;em&gt;iFrame1-screenshot.png&lt;/em&gt;) using the &lt;em&gt;screenshot()&lt;/em&gt; method that was discussed in the earlier section.&lt;/p&gt;

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

&lt;p&gt;For the &lt;strong&gt;second test scenario&lt;/strong&gt;, we first locate the iFrame element using the XPath locator (i.e., &lt;em&gt;iFrame2&lt;/em&gt;).&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.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%2AIR0r-hpIlQKpH5M6.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.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%2AIR0r-hpIlQKpH5M6.png" width="800" height="400"&gt;&lt;/a&gt;&lt;/p&gt;

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

&lt;p&gt;Switch to the iFrame by invoking the &lt;em&gt;contentFrame()&lt;/em&gt; method on the element located in the previous step.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.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%2Ar-jbAc8BjZkWfJMH.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.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%2Ar-jbAc8BjZkWfJMH.png" width="800" height="400"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;As seen below, two occurrences of the XPath &lt;em&gt;//a[.=’Playwright Testing’]&lt;/em&gt; exists.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.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%2AI5Xil8NU5oxPEKqT.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.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%2AI5Xil8NU5oxPEKqT.png" width="800" height="400"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;However, the element from the iFrame is selected since the &lt;em&gt;waitForXPath&lt;/em&gt; is invoked on the iFrame element.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.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%2A_Ajq-TuTRKpEKOyF.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.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%2A_Ajq-TuTRKpEKOyF.png" width="800" height="489"&gt;&lt;/a&gt;&lt;/p&gt;

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

&lt;p&gt;Now that the element within the iFrame is located, we scroll to the element by invoking the &lt;em&gt;scroll_to_element()&lt;/em&gt; user-defined method.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.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%2A5xMlMYrH3dffpbAW.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.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%2A5xMlMYrH3dffpbAW.png" width="800" height="552"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;A &lt;em&gt;click()&lt;/em&gt; operation is performed so that it navigates to the &lt;a href="https://www.lambdatest.com/playwright-testing" rel="noopener noreferrer"&gt;Playwright testing&lt;/a&gt; documentation page.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.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%2ALEyj6hZBrBJzFjEU.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.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%2ALEyj6hZBrBJzFjEU.png" width="800" height="373"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Finally, we capture a screenshot of the entire page via the &lt;em&gt;screenshot()&lt;/em&gt; method.&lt;/p&gt;

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

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

&lt;p&gt;Invoke the command make &lt;em&gt;pyppeteer-iframe&lt;/em&gt; on the terminal with the Chromium browser instantiated on the local machine. As seen below, the test execution is successful.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.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%2AC72UURcR-GCx-6bK.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.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%2AC72UURcR-GCx-6bK.png" width="800" height="291"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The iFrame screenshots are also successfully generated at the end of the test execution.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.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_XCsozPBWxfqb9PG.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.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_XCsozPBWxfqb9PG.png" width="664" height="232"&gt;&lt;/a&gt;&lt;/p&gt;

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

&lt;p&gt;&lt;em&gt;iFrame grab of test scenario — 1&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.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%2An1HKSrEKFec28ny_.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.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%2An1HKSrEKFec28ny_.png" width="800" height="428"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;em&gt;iFrame grab of test scenario — 2&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;With this, you are also set to tackle iFrame-related challenges with the Pyppeteer library!&lt;/p&gt;

&lt;h2&gt;
  
  
  Handling alerts and pop-ups with Pyppeteer
&lt;/h2&gt;

&lt;p&gt;Alerts and Pop-ups (or pop-ups) are used extensively in websites for gathering user information, seeking confirmation from end-users, and more. Simple alerts, information alerts, and prompt alerts are some of the most common types of alerts used across websites. You can learn more about handling alerts through this blog on &lt;a href="https://www.lambdatest.com/blog/selenium-c-tutorial-handling-alert-windows/#Alerts?utm_source=devto&amp;amp;utm_medium=organic&amp;amp;utm_campaign=feb_05&amp;amp;utm_term=bh&amp;amp;utm_content=blog" rel="noopener noreferrer"&gt;handling alerts in windows using Selenium&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.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%2ArSBQk8UjqA9i7a0x.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.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%2ArSBQk8UjqA9i7a0x.png" width="800" height="400"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Examples of Alerts&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;All websites show a ‘Cookie Consent’ popup when you visit the same for the first time or from an incognito window. Below is an Exit popup on the LambdaTest website that prompts the user to try out the platform.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.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%2AquZL96ju8ehXmatR.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.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%2AquZL96ju8ehXmatR.png" width="800" height="400"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Since the guiding principles of alerts and popups remain unchanged, we recommend you look at the video below, which deep dives into its essentials.&lt;br&gt;
&lt;iframe width="710" height="399" src="https://www.youtube.com/embed/C4YT0BT_wbQ"&gt;
&lt;/iframe&gt;
&lt;br&gt;
As far as the Pyppeteer library is concerned, the &lt;em&gt;dialog&lt;/em&gt; class provides methods that help handle popups and alerts. Dialog objects are dispatched by page via the dialog event.&lt;/p&gt;

&lt;p&gt;The &lt;em&gt;dismiss()&lt;/em&gt; method is below the dialog box. On similar lines, the &lt;em&gt;accept(promptText: str = ‘’)&lt;/em&gt; method lets you accept the dialog. You can also pass a &lt;em&gt;promptText&lt;/em&gt; (format: string), which is the text to enter in the prompt.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Demonstration&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;For demonstrating the usage of dialog boxes (or alerts &amp;amp; popups) with Pyppeteer, we would be automating interactions with the different alerts in &lt;a href="https://www.lambdatest.com/selenium-playground/javascript-alert-box-demo?utm_source=devto&amp;amp;utm_medium=organic&amp;amp;utm_campaign=feb_05&amp;amp;utm_term=bh&amp;amp;utm_content=webpage" rel="noopener noreferrer"&gt;LambdaTest Playground — Alert Demo&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.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%2As9scvIXAbTsJ1tzT.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.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%2As9scvIXAbTsJ1tzT.png" width="800" height="400"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The scenarios are automated with Chromium installed on a local machine (&lt;em&gt;EXEC_PLATFORM = local&lt;/em&gt;). However, the same tests would also work seamlessly with Pyppeteer on LambdaTest cloud (&lt;em&gt;EXEC_PLATFORM = cloud&lt;/em&gt;)&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;import asyncio
import pytest
from pyppeteer.errors import PageError
from urllib.parse import quote
import json
import os
import sys
from os import environ
from pyppeteer import connect, launch


# Documentation Link
# https://miyakogi.github.io/pyppeteer/reference.html#dialog-class


# Interesting reference question
# https://stackoverflow.com/questions/75622322/
# why-dialog-popup-alert-doesnt-dismiss-as-they-claim-by-using-pyppeteer-class


# Scenario Handling Alerts
dialog_test_url = 'https://www.lambdatest.com/selenium-playground/javascript-alert-box-demo'


# Locators for different elements
# 0 - JS Alert
# 1 - Confirm Box
# 2 - Prompt Box


loc_alert_arr =  ['.my-30',
               '.py-20.ml-10 .btn',
               'section:nth-of-type(3) div:nth-of-type(3) .btn']


test_message = 'LambdaTest is a great platform!'


# Event event listener for handling JS alert dialog box
def handle_js_dialog_box(dialog):
   asyncio.ensure_future(dialog.accept())
   print(f"Dialog message: {dialog.message}")


# Event event listener for handling confirm dialog box
def handle_confirm_accept_dialog_box(dialog):
   asyncio.ensure_future(dialog.accept())
   print(f"Dialog message: {dialog.message}")


# Event event listener for handling confirm dialog box
def handle_confirm_dismiss_dialog_box(dialog):
   asyncio.ensure_future(dialog.dismiss())
   print(f"Dialog message: {dialog.message}")


# Event event listener for handling prompt dialog box
def handle_confirm_prompt_dialog_box(dialog):
   asyncio.ensure_future(dialog.accept(test_message))
   print(f"Dialog message: {dialog.message}")


@pytest.mark.asyncio
@pytest.mark.order(1)
async def test_handling_js_alerts(page):
   await page.goto(dialog_test_url)


   # Can be changed with non-blocking sleep
   await asyncio.sleep(1)


   page.on('dialog', handle_js_dialog_box)


   elem_alert = await page.querySelector(loc_alert_arr[0])


   # Click on the located element
   await elem_alert.click()


   # Wait for the event loop to process events
   await asyncio.sleep(2)


@pytest.mark.asyncio
@pytest.mark.order(2)
async def test_handling_confirm_accept_alerts(page):
   await page.goto(dialog_test_url)


   # Can be changed with non-blocking sleep
   await asyncio.sleep(1)


   page.on('dialog', handle_confirm_accept_dialog_box)


   # Confirm Alert
   elem_alert = await page.querySelector(loc_alert_arr[1])


   # Click on the located element
   await elem_alert.click()


   # Wait for the event loop to process events
   await asyncio.sleep(2)


@pytest.mark.asyncio
@pytest.mark.order(3)
async def test_handling_confirm_dismiss_alerts(page):
   await page.goto(dialog_test_url)


   # Can be changed with non-blocking sleep
   await asyncio.sleep(1)


   page.on('dialog', handle_confirm_dismiss_dialog_box)


   await asyncio.sleep(2)


   # Dismiss Alert
   elem_alert = await page.querySelector(loc_alert_arr[1])


   # Click on the located element
   await elem_alert.click()


   # Wait for the event loop to process events
   await asyncio.sleep(2)


@pytest.mark.asyncio
@pytest.mark.order(4)
async def test_handling_prompt_alerts(page):
   await page.goto(dialog_test_url)


   # Can be changed with non-blocking sleep
   await asyncio.sleep(1)


   page.on('dialog', handle_confirm_prompt_dialog_box)


   await asyncio.sleep(1)


   # Prompt Alert
   elem_alert = await page.querySelector(loc_alert_arr[2])


   # Click on the located element
   await elem_alert.click()


   page.on('dialog', handle_confirm_dismiss_dialog_box)


   await asyncio.sleep(2)
&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;Four separate event listeners are created for automating interactions with the dialog boxes:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.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%2AVzUa6pVYPoP8Iu0LTGBn5w.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.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%2AVzUa6pVYPoP8Iu0LTGBn5w.png" width="800" height="400"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Let’s deep dive into different test scenarios bifurcated based on the interactions they have with the respective alert box.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Test Method: test_handling_js_alerts()&lt;/strong&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;After navigating to the &lt;a href="https://www.lambdatest.com/selenium-playground/javascript-alert-box-demo?utm_source=devto&amp;amp;utm_medium=organic&amp;amp;utm_campaign=feb_05&amp;amp;utm_term=bh&amp;amp;utm_content=webpage" rel="noopener noreferrer"&gt;LambdaTest Alert Demo&lt;/a&gt; website, the &lt;em&gt;page.on() *method in Pyppeteer is used for attaching event listeners to the current page. The syntax of the *page.on()&lt;/em&gt; method is below:&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;await page.on(event, callback)
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;– &lt;em&gt;event:&lt;/em&gt; Event name (string) to listen for.&lt;br&gt;
– &lt;em&gt;callback:&lt;/em&gt; Callback function to be executed when the specified event fires.&lt;/p&gt;

&lt;p&gt;Since dialog objects are dispatched by the page via the dialog event, the method listens to that particular event. The callback function in the current scenario is &lt;em&gt;handle_js_dialog_box(dialog)&lt;/em&gt;.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.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%2AhIrz-LjzSUImVAIy.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.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%2AhIrz-LjzSUImVAIy.png" width="800" height="296"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Here is the implementation of the callback function:&lt;/p&gt;

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

&lt;p&gt;The asyncio.ensure_future() method of the asyncio module is used for background execution of the accept() method. In short, when the callback function is fired, it accepts the dialog box. The message() method of the dialog class prints the message attached to the current dialog box.&lt;/p&gt;

&lt;p&gt;Next, locate the Click Me button associated with JavaScript Alerts on the page.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.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%2AVGvW_v8FVphw_SQL.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.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%2AVGvW_v8FVphw_SQL.png" width="800" height="505"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.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%2ApmYQM0LroPeBUsTi.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.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%2ApmYQM0LroPeBUsTi.png" width="800" height="400"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Since the element is located using the CSS Selector, we use the querySelector() method to get the element (i.e. elem_alert) matching that locator.&lt;/p&gt;

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

&lt;p&gt;Finally, the &lt;em&gt;click()&lt;/em&gt; method of the button element is invoked so that it opens up the dialog box.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.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%2AqLi21bsakCmT4RU7.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.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%2AqLi21bsakCmT4RU7.png" width="750" height="376"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;With this, the dialog event is also fired, after which the respective callback function [i.e., handle_js_dialog_box(dialog)] is executed non-blocking.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Test Method: test_handling_confirm_accept_alerts()&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Like before, the page.on() method in Pyppeteer is used for attaching event listeners to the current page.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.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%2AosQMpHrSpdM7CLoI.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.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%2AosQMpHrSpdM7CLoI.png" width="800" height="400"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Here is the implementation of &lt;em&gt;handle_confirm_accept_dialog_box()&lt;/em&gt;:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.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%2AJ3rr9SW7wg6uBDKf.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.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%2AJ3rr9SW7wg6uBDKf.png" width="800" height="400"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Next, locate the &lt;strong&gt;Click Me&lt;/strong&gt; button associated with the &lt;em&gt;Confirm box&lt;/em&gt; on the page.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.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%2AiMeMP2J75uy2Dckf.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.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%2AiMeMP2J75uy2Dckf.png" width="800" height="400"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Like earlier, the &lt;em&gt;click()&lt;/em&gt; method of the button element is invoked; after which the respective callback function [i.e., &lt;em&gt;test_handling_confirm_accept_alerts(dialog)&lt;/em&gt;] is executed in a non-blocking manner.&lt;/p&gt;

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

&lt;p&gt;&lt;strong&gt;Test Method: test_handling_confirm_dismiss_alerts()&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Like before, the &lt;em&gt;page.on()&lt;/em&gt; method in Pyppeteer is used for attaching event listeners to the current page.&lt;/p&gt;

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

&lt;p&gt;Since the dialog box will be dismissed, the &lt;em&gt;dismiss()&lt;/em&gt; method of the &lt;em&gt;Dialog&lt;/em&gt; class is invoked in the callback function.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.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%2A5XGrKQ9USYffspov.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.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%2A5XGrKQ9USYffspov.png" width="800" height="318"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Next, locate the &lt;strong&gt;Click Me&lt;/strong&gt; button associated with the &lt;em&gt;Confirm box&lt;/em&gt; on the page.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.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%2A_KaNNDfe7M4blmep.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.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%2A_KaNNDfe7M4blmep.png" width="800" height="505"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Like earlier, the &lt;em&gt;click()&lt;/em&gt; method of the button element is invoked, after which the respective callback function [i.e., &lt;em&gt;test_handling_confirm_dimiss_alerts(dialog)&lt;/em&gt;] is executed in a non-blocking manner.&lt;/p&gt;

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

&lt;p&gt;&lt;strong&gt;Test Method: test_handling_prompt_alerts()&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Like before, the &lt;em&gt;page.on()&lt;/em&gt; method in Pyppeteer is used for attaching event listeners to the current page.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.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%2AbsTBeL-0GztMIUG0.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.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%2AbsTBeL-0GztMIUG0.png" width="800" height="400"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Since there is a text message that needs to be entered in this prompt box, the respective message (i.e., &lt;em&gt;test_message&lt;/em&gt;) is passed to the &lt;em&gt;accept()&lt;/em&gt; method.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.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%2Afzj2fm9a3-bkt4I9.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.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%2Afzj2fm9a3-bkt4I9.png" width="800" height="458"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.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%2Ag1rFgj1QNTJqFmZQ.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.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%2Ag1rFgj1QNTJqFmZQ.png" width="800" height="318"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The &lt;strong&gt;Click Me&lt;/strong&gt; button associated with the &lt;em&gt;Prompt box&lt;/em&gt; uses the CSS Selector property. The respective callback function [i.e., &lt;em&gt;handle_confirm_dismiss_dialog_box(dialog)&lt;/em&gt;] is executed once the dialog box appears after the click operation on the button element.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.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%2AUQ-02HdgG-cTD4jq.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.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%2AUQ-02HdgG-cTD4jq.png" width="800" height="400"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.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%2A0EBDLo-MFqEvfDWi.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.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%2A0EBDLo-MFqEvfDWi.png" width="800" height="234"&gt;&lt;/a&gt;&lt;/p&gt;

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

&lt;p&gt;Invoke the command make &lt;em&gt;pyppeteer-dialog-box&lt;/em&gt; on the terminal with the Chromium browser instantiated on the local machine. As seen below, the test execution is successful.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.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%2AeNUcvhDW88d4BfJd.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.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%2AeNUcvhDW88d4BfJd.png" width="800" height="400"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Shown below are the execution snapshots that indicate that the interactions with the dialog (or alert) boxes were successful.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.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%2AWuVuALVFt4vJjHR0.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.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%2AWuVuALVFt4vJjHR0.png" width="800" height="400"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Execution: Handling of Alert box&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.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%2Ah3F1-FHqpqdDVWQv.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.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%2Ah3F1-FHqpqdDVWQv.png" width="800" height="400"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;&lt;em&gt;Execution: Handling of Confirm box (Accept)&lt;/em&gt;&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.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%2A-weWZSRE5x0cbYml.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.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%2A-weWZSRE5x0cbYml.png" width="800" height="442"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;&lt;em&gt;Execution: Handling of Confirm box (Dismiss)&lt;/em&gt;&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.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%2A5MlrNNMAPWBokhZp.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.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%2A5MlrNNMAPWBokhZp.png" width="800" height="444"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;&lt;em&gt;Execution: Handling of Prompt box (promptText)&lt;/em&gt;&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Though alerts that open up a &lt;em&gt;dialog&lt;/em&gt; box, popups (or pop-ups) are mere window(s) that appear on top of the existing content of the page. Automating interactions with popups involves locating the window using appropriate locators (i.e., &lt;em&gt;CSS Selector, XPath&lt;/em&gt;.&lt;/p&gt;

&lt;p&gt;After that, we use appropriate coroutines (i.e., &lt;em&gt;waitForXPath&lt;/em&gt;, &lt;em&gt;waitForSelector&lt;/em&gt;) to detect the presence of the popup element. Once detected, you can interact with it by clicking buttons or performing other actions.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Learn to implement Explicit Wait command in &lt;a href="https://www.lambdatest.com/blog/selenium-webdriverwait/?utm_source=devto&amp;amp;utm_medium=organic&amp;amp;utm_campaign=feb_05&amp;amp;utm_term=bh&amp;amp;utm_content=blog" rel="noopener noreferrer"&gt;Selenium WebDriverWait&lt;/a&gt; class in Java. Improve web testing by precisely waiting for elements!&lt;/strong&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2&gt;
  
  
  Handling dynamic content with Pyppeteer
&lt;/h2&gt;

&lt;p&gt;Most modern-age websites (including SPAs) leverage JavaScript for loading and updating content on the page. AJAX (Asynchronous JavaScript and XML) retrieves data from the server and updates relevant sections (or parts) on the page. This means that the content on the page is loaded with a reading of the entire page.&lt;/p&gt;

&lt;p&gt;Not only content, the WebElements can also be dynamic. These WebElements are invoked at run-time and dynamically added to the page. It is important to note that there is a difference between dynamic elements and hidden elements (&lt;em&gt;display: none, visibility: hidden&lt;/em&gt;).&lt;/p&gt;

&lt;p&gt;A WebElement can never be hidden as well as dynamic. For instance, a web page could have a &lt;em&gt;collapsed&lt;/em&gt; dropdown element (or menu) that becomes visible dynamically based on the interactions with the element. &lt;a href="https://www.lambdatest.com/blog/handling-dropdowns-in-selenium-webdriver-java/" rel="noopener noreferrer"&gt;Handling dynamic dropdown elements with Selenium&lt;/a&gt;, Pyppeteer, and other frameworks (or libraries) needs to be in an automation engineer’s arsenal!&lt;/p&gt;

&lt;p&gt;For instance, YouTube is a prominent website that leverages dynamic loading of content to showcase video content to its end-users. As seen below, content (including videos, and meta-data) on the &lt;a href="https://www.youtube.com/@LambdaTest/videos?utm_source=devto&amp;amp;utm_medium=organic&amp;amp;utm_campaign=feb_05&amp;amp;utm_term=bh&amp;amp;utm_content=youtube" rel="noopener noreferrer"&gt;LambdaTest YouTube channel &lt;/a&gt;is loaded dynamically when the user performs a scroll operation.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.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%2AVsM_f_IkUFuwyqdE.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.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%2AVsM_f_IkUFuwyqdE.png" width="800" height="400"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;em&gt;YouTube: Dynamic Content Example&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.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%2AmFa3hp_j98mLoumS.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.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%2AmFa3hp_j98mLoumS.png" width="800" height="499"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;em&gt;YouTube: Dynamic Content Example&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;For a demonstration of dynamic content handling with Pyppeteer, we would be automating interactions with WebElements on the following websites:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;LambdaTest eCommerce Playground&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Scraping Club Infinite Scroll&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Along with dynamically loaded content, the LambdaTest eCommerce Playground also &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_05&amp;amp;utm_term=bh&amp;amp;utm_content=blog" rel="noopener noreferrer"&gt;lazy loaded images for better website performance&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.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%2AyI8vm2AL7CC0elmR.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.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%2AyI8vm2AL7CC0elmR.png" width="800" height="400"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Lazy Loading of images&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Demonstration&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;For demonstrating dynamic content handling with Pyppeteer, we automate the following test scenario on Chrome installed on a local machine (&lt;em&gt;EXEC_PLATFORM = local&lt;/em&gt;) or LambdaTest cloud grid (&lt;em&gt;EXEC_PLATFORM = cloud&lt;/em&gt;)&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Test Scenario 1&lt;/strong&gt;&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;&lt;p&gt;Navigate to &lt;a href="https://ecommerce-playground.lambdatest.io/" rel="noopener noreferrer"&gt;LambdaTest eCommerce Playground&lt;/a&gt;.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Scroll into the &lt;em&gt;Nikon D300&lt;/em&gt; Product to be added in the Cart.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Assert if the target URL does not match the &lt;a href="https://ecommerce-playground.lambdatest.io/index.php?route=product/product&amp;amp;product_id=63" rel="noopener noreferrer"&gt;expected URL&lt;/a&gt;.&lt;/p&gt;&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;&lt;strong&gt;Test Scenario 2&lt;/strong&gt;&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;&lt;p&gt;Navigate to &lt;a href="https://ecommerce-playground.lambdatest.io/" rel="noopener noreferrer"&gt;LambdaTest eCommerce Playground&lt;/a&gt;.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Click on the &lt;em&gt;third tab&lt;/em&gt; in the carousel.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Assert if the target URL does not match the &lt;a href="https://ecommerce-playground.lambdatest.io/index.php?route=product/product&amp;amp;product_id=30" rel="noopener noreferrer"&gt;expected URL&lt;/a&gt;.&lt;/p&gt;&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;&lt;strong&gt;Test Scenario 3&lt;/strong&gt;&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;&lt;p&gt;Navigate to &lt;a href="https://scrapingclub.com/exercise/list_infinite_scroll/" rel="noopener noreferrer"&gt;Scraping Club Infinite Scroll&lt;/a&gt;.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Click on the &lt;em&gt;Short Chiffon Dress&lt;/em&gt; product.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Assert if the target URL does not match the &lt;a href="https://scrapingclub.com/exercise/list_basic_detail/93926-C/" rel="noopener noreferrer"&gt;expected URL&lt;/a&gt;.&lt;/p&gt;&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;&lt;strong&gt;Test Scenario 4&lt;/strong&gt;&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;&lt;p&gt;Navigate to &lt;a href="https://scrapingclub.com/exercise/list_infinite_scroll/" rel="noopener noreferrer"&gt;Scraping Club Infinite Scroll&lt;/a&gt;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Scroll to the &lt;em&gt;Top with Tie&lt;/em&gt; product.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Assert if the target URL does not match the &lt;a href="https://scrapingclub.com/exercise/list_basic_detail/94967-A/" rel="noopener noreferrer"&gt;expected URL&lt;/a&gt;.&lt;/p&gt;&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Though all four test scenarios appear similar, the difference lies in how the content (including images) is presented on the website. In one of the scenarios, we would also be demonstrating scroll operations that come in handy when automating interactions with dynamically loaded content.&lt;/p&gt;

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

&lt;p&gt;Here is the implementation of all the test scenarios. We will be dissecting the code in further sections of the blog!&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;import asyncio
import pytest
from pyppeteer.errors import PageError
from urllib.parse import quote
import json
import os
import sys
from os import environ
from pyppeteer import connect, launch


exec_platform = os.getenv('EXEC_PLATFORM')


# Get username and access key of the LambdaTest Platform
username = environ.get('LT_USERNAME', None)
access_key = environ.get('LT_ACCESS_KEY', None)


test1_url = 'https://ecommerce-playground.lambdatest.io/'
test2_url = 'https://scrapingclub.com/exercise/list_infinite_scroll/'


# Usecase - 1
# loc_ecomm_1 = ".order-1.col-lg-6 div:nth-of-type(1) &amp;gt; div:nth-of-type(1) &amp;gt; div:nth-of-type(1) &amp;gt; div:nth-of-type(1) &amp;gt; div:nth-of-type(1) div:nth-of-type(1) &amp;gt; img:nth-of-type(1)"
loc_ecomm_1 = "[aria-label='1 / 2'] div:nth-of-type(1) &amp;gt; [alt='Nikon D300']"
target_url_1 = "https://ecommerce-playground.lambdatest.io/index.php?route=product/product&amp;amp;product_id=63"


# Usecase - 2 (Click on e-commerce sliding banner)
loc_ecomm_2 = "[alt='Canon DSLR camera']"
target_url_2 = "https://ecommerce-playground.lambdatest.io/index.php?route=product/product&amp;amp;product_id=30"


# Usecase - 3 Automating interactions on https://scrapingclub.com/exercise/list_infinite_scroll/
loc_infinite_src_prod1 = ".grid .p-4 [href='/exercise/list_basic_detail/93926-C/']"
target_url_3 = "https://scrapingclub.com/exercise/list_basic_detail/93926-C/"


# Usecase - 4 Automating interactions on https://scrapingclub.com/exercise/list_infinite_scroll/
# when the images are lazy loaded
loc_infinite_src_prod2 = "div:nth-of-type(31) &amp;gt; .p-4 [href='/exercise/list_basic_detail/94967-A/']"
target_url_4 = "https://scrapingclub.com/exercise/list_basic_detail/94967-A/"


# Set timeout in ms
timeOut = 60000


async def scroll_to_element(page, selector):
   # Scroll until the element is detected
   await page.evaluateHandle(
       '''async (selector) =&amp;gt; {
           const element = document.querySelector(selector);
           if (element) {
               element.scrollIntoView();
           }
       }''',
       selector
   )


   return selector


async def scroll_carousel(page, scr_count):
   for scr in range(1, scr_count):
       elem_next_button = "#mz-carousel-213240 &amp;gt; ul li:nth-child(" + str(scr) + ")"
       await asyncio.sleep(1)
       elem_next_button = await page.querySelector(elem_next_button)
       await elem_next_button.click()


# Replica of https://github.com/hjsblogger/web-scraping-with-python/blob/
# main/tests/beautiful-soup/test_infinite_scraping.py#L67C5-L80C18


async def scroll_end_of_page(page):
   start_height = await page.evaluate('document.documentElement.scrollHeight')


   while True:
       # Scroll to the bottom of the page
       await page.evaluate(f'window.scrollTo(0, {start_height})')


       # Wait for the content to load
       await asyncio.sleep(1)


       # Get the new scroll height
       scroll_height = await page.evaluate('document.documentElement.scrollHeight')


       if scroll_height == start_height:
           # If heights are the same, we reached the end of the page
           break


       # Add an additional wait
       await asyncio.sleep(2)


       start_height = scroll_height


   # Additional wait after scrolling
   await asyncio.sleep(2)


@pytest.mark.asyncio
@pytest.mark.order(1)
async def test_lazy_load_ecomm_1(page):


   # The time out can be set using the setDefaultNavigationTimeout
   # It is primarily used for overriding the default page timeout of 30 seconds
   page.setDefaultNavigationTimeout(timeOut)
   await page.goto(test1_url,
       {'waitUntil': 'load', 'timeout': timeOut})

   # Set the viewport - Apple MacBook Air 13-inch
   # Reference - https://codekbyte.com/devices-viewport-sizes/
   # await page.setViewport({'width': 1440, 'height': 770})


   await asyncio.sleep(2)


   if exec_platform == 'local':
       # Scroll until the element is detected
       elem_button = await scroll_to_element(page, loc_ecomm_1)


       # await page.click(elem_button)


       # Wait until the page is loaded
       # https://miyakogi.github.io/pyppeteer/reference.html#pyppeteer.page.Page.waitForNavigation
       navigationPromise = asyncio.ensure_future(page.waitForNavigation())
       await page.click(elem_button)
       await navigationPromise
   elif exec_platform == 'cloud':
       elem_button = await page.waitForSelector(loc_ecomm_1, {'visible': True})
       await asyncio.gather(
           elem_button.click(),
           page.waitForNavigation({'waitUntil': 'networkidle2', 'timeout': 30000}),
       )

   # Assert if required, since the test is a simple one; we leave as is :D
   current_url = page.url
   print('Current URL is: ' + current_url)


   try:
       assert current_url == target_url_1
       print("Test Success: Product checkout successful")
   except PageError as e:
       print("Test Failure: Could not checkout Product")
       print("Error Code" + str(e))


@pytest.mark.asyncio
@pytest.mark.order(2)
async def test_lazy_load_ecomm_2(page):
   carousel_len = 4


   # The time out can be set using the setDefaultNavigationTimeout
   # It is primarily used for overriding the default page timeout of 30 seconds
   page.setDefaultNavigationTimeout(timeOut)
   await page.goto(test1_url,
       {'waitUntil': 'load', 'timeout': timeOut})

   # Set the viewport - Apple MacBook Air 13-inch
   # Reference - https://codekbyte.com/devices-viewport-sizes/
   # await page.setViewport({'width': 1440, 'height': 770})


   await asyncio.sleep(2)


   # Approach 1: Directly click on the third button on the carousel
   # elem_carousel_banner = await page.querySelector("#mz-carousel-213240 &amp;gt; ul li:nth-child(3)")
   # await asyncio.sleep(1)
   # await elem_carousel_banner.click()


   # Approach 2 (Only for demo): Serially click on every button on carousel
   await scroll_carousel(page, carousel_len)

   await asyncio.sleep(1)


   # elem_prod_1 = await page.querySelector(loc_ecomm_2)
   elem_prod_1 = await page.waitForSelector(loc_ecomm_2, {'visible': True})
   await asyncio.gather(
       elem_prod_1.click(),
       page.waitForNavigation({'waitUntil': 'networkidle2', 'timeout': 60000}),
   )

   # Assert if required, since the test is a simple one; we leave as is :D
   current_url = page.url
   print('Current URL is: ' + current_url)


   try:
       assert current_url == target_url_2
       print("Test Success: Product checkout successful")
   except PageError as e:
       print("Test Failure: Could not checkout Product")
       print("Error Code" + str(e))




@pytest.mark.asyncio
@pytest.mark.order(3)
async def test_lazy_load_infinite_scroll_1(page):
   # The time out can be set using the setDefaultNavigationTimeout
   # It is primarily used for overriding the default page timeout of 30 seconds
   page.setDefaultNavigationTimeout(timeOut)
   await page.goto(test2_url,
       {'waitUntil': 'load', 'timeout': timeOut})

   # Set the viewport - Apple MacBook Air 13-inch
   # Reference - https://codekbyte.com/devices-viewport-sizes/
   # await page.setViewport({'width': 1440, 'height': 770})


   await asyncio.sleep(1)


   elem_prod1 = await page.querySelector(loc_infinite_src_prod1)


   await asyncio.gather(
       elem_prod1.click(),
       page.waitForNavigation({'waitUntil': 'networkidle2', 'timeout': 60000}),
   )


   # await asyncio.sleep(1)
   # await elem_carousel_banner.click()


   # elem_button = scroll_to_element(page, loc_infinite_src_prod1)
   # print(elem_button)
   # await asyncio.sleep(2)
   # await elem_button.click()

   # Assert if required, since the test is a simple one; we leave as is :D
   current_url = page.url
   print('Current URL is: ' + current_url)


   try:
       assert current_url == target_url_3
       print("Test Success: Product checkout successful")
   except PageError as e:
       print("Test Failure: Could not checkout Product")
       print("Error Code" + str(e))

@pytest.mark.asyncio
@pytest.mark.order(4)
async def test_lazy_load_infinite_scroll_2(page):
   # The time out can be set using the setDefaultNavigationTimeout
   # It is primarily used for overriding the default page timeout of 30 seconds
   page.setDefaultNavigationTimeout(timeOut)


   # Tested navigation using LambdaTest YouTube channel


   # await page.goto("https://www.youtube.com/@LambdaTest/videos",
   await page.goto(test2_url,
       {'waitUntil': 'load', 'timeout': timeOut})

   # Set the viewport - Apple MacBook Air 13-inch
   # Reference - https://codekbyte.com/devices-viewport-sizes/
   # await page.setViewport({'width': 1440, 'height': 770})


   await asyncio.sleep(1)


   await scroll_end_of_page(page)


   await page.evaluate('window.scrollTo(0, 0)')


   await asyncio.sleep(1)


   # elem_prod = await page.querySelector(loc_infinite_src_prod2)


   # asyncio.sleep(1)


   # await asyncio.gather(
   #     elem_prod.click(),
   #     page.waitForNavigation({'waitUntil': 'load', 'timeout': 60000}),
   # )


   elem_button = await scroll_to_element(page, loc_infinite_src_prod2)


   await asyncio.sleep(1)


   # await page.click(elem_button)


   await asyncio.gather(
       page.click(elem_button),
       page.waitForNavigation({'waitUntil': 'networkidle2', 'timeout': 60000}),
   )


   # Assert if required, since the test is a simple one; we leave as is :D
   current_url = page.url
   print('Current URL is: ' + current_url)


   try:
       assert current_url == target_url_4
       print("Test Success: Product checkout successful")
   except PageError as e:
       print("Test Failure: Could not checkout Product")
       print("Error Code" + str(e))
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;&lt;strong&gt;Code Walkthrough (Test Scenario — 1)&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;In the test &lt;a href="https://github.com/hjsblogger/web-automation-with-pyppeteer/blob/main/tests/handling-dynamic-content/test_page_class_lazy_loaded_content.py#L108" rel="noopener noreferrer"&gt;*test_lazy_load_ecomm_1()&lt;/a&gt;&lt;em&gt;, we first navigate to the test URL using the *goto()&lt;/em&gt; method. The navigation timeout is set to 60000 ms, overriding the default timeout of 30000 ms.&lt;/p&gt;

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

&lt;p&gt;Once the page is loaded, we use the &lt;em&gt;querySelector()&lt;/em&gt; method to get the element matching the specified CSS Selector.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.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%2AWh9Cy9N8dDJVyyug.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.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%2AWh9Cy9N8dDJVyyug.png" width="800" height="500"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Once the element is located, the &lt;em&gt;scrollIntoView()&lt;/em&gt; method of the Element’s interface (in JavaScript) is used to scroll into view of the specified element.&lt;/p&gt;

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

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

&lt;p&gt;The &lt;em&gt;asyncio.ensure_future()&lt;/em&gt; method of the &lt;em&gt;asyncio&lt;/em&gt; module is used to convert the promise (i.e., &lt;em&gt;navigationPromise&lt;/em&gt;) returned by &lt;em&gt;waitForNavigation()&lt;/em&gt; into an asynchronous task.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.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%2AXtR42ETmiyxnFqMN.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.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%2AXtR42ETmiyxnFqMN.png" width="800" height="188"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The &lt;em&gt;click()&lt;/em&gt; method clicks on the element in the previous step. Finally, &lt;em&gt;await navigationPromise&lt;/em&gt; waits for the completion of the navigation operation (i.e., the page would have loaded completely by the end of this step).&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.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%2AkRktGr2XFfjxIKiv.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.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%2AkRktGr2XFfjxIKiv.png" width="800" height="400"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.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%2Awz-sZzg7i5xqhvyr.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.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%2Awz-sZzg7i5xqhvyr.png" width="800" height="400"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Assert if the post-navigation URL does not match with the target URL.&lt;/p&gt;

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

&lt;p&gt;&lt;strong&gt;Code Walkthrough (Test Scenario — 2)&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;After navigating to the test URL, we invoke the user-defined &lt;em&gt;scroll_carousel()&lt;/em&gt; method that takes two input parameters — &lt;em&gt;page instance &amp;amp; carousel count&lt;/em&gt; (i.e., &lt;em&gt;carousel_len = 4&lt;/em&gt;).&lt;/p&gt;

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

&lt;p&gt;As shown below, the carousel contains 3 items, our target element is located in the final scroll.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.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%2Ahm4fXSU9NcZjHbHL.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.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%2Ahm4fXSU9NcZjHbHL.png" width="800" height="500"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Here is the implementation of the &lt;em&gt;scroll_carousel()&lt;/em&gt; method:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.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%2AdO_5oM_2PxLUQqZb.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.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%2AdO_5oM_2PxLUQqZb.png" width="800" height="262"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The end count of the carousel is set to 4. Hence, the for loop executes in the &lt;em&gt;1..4&lt;/em&gt; range. The **next **button on carousel is located using the CSS Selector:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Button 1:&lt;/strong&gt; #mz-carousel-213240 &amp;gt; ul li:nth-child(1)&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Button 2:&lt;/strong&gt; #mz-carousel-213240 &amp;gt; ul li:nth-child(2)&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Button 3:&lt;/strong&gt; #mz-carousel-213240 &amp;gt; ul li:nth-child(3)&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;a href="https://media2.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%2AgtOyp4jfGXD2RuFZ.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.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%2AgtOyp4jfGXD2RuFZ.png" width="800" height="400"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;At each step, the &lt;em&gt;click()&lt;/em&gt; method on the located button element invokes the click operation. Since the loop runs from &lt;em&gt;1&lt;/em&gt; through &lt;em&gt;3&lt;/em&gt;, the final click is performed on the last item (or button) in the carousel.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.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%2AoJc8bQmSR_x-g2zi.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.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%2AoJc8bQmSR_x-g2zi.png" width="800" height="400"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Now that we are at the target location in the carousel, the element is located via the CSS Selector. The option &lt;em&gt;visible&lt;/em&gt; is set to &lt;em&gt;True&lt;/em&gt; so that the &lt;em&gt;waitForSelector()&lt;/em&gt;&lt;br&gt;
method waits for the located element to be visible on the page.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.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%2AnF56MvkFA7Vx7y9A.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.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%2AnF56MvkFA7Vx7y9A.png" width="800" height="500"&gt;&lt;/a&gt;&lt;/p&gt;

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

&lt;p&gt;Finally, the &lt;em&gt;asyncio.gather()&lt;/em&gt; of the &lt;em&gt;asyncio&lt;/em&gt; library is called for executing the &lt;em&gt;click()&lt;/em&gt; method on the located element and waiting for the navigation to complete [i.e., &lt;em&gt;waitForNavigation()&lt;/em&gt; ].&lt;/p&gt;

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

&lt;p&gt;Like before, an &lt;em&gt;assert&lt;/em&gt; is raised if the current URL does not match with the &lt;a href="https://ecommerce-playground.lambdatest.io/index.php?route=product/product&amp;amp;product_id=63" rel="noopener noreferrer"&gt;target URL&lt;/a&gt;.&lt;/p&gt;

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

&lt;p&gt;&lt;strong&gt;Code Walkthrough (Test Scenario — 3)&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;After navigating to the &lt;a href="https://scrapingclub.com/exercise/list_infinite_scroll/" rel="noopener noreferrer"&gt;test URL&lt;/a&gt;, we locate the element via the CSS property — .&lt;em&gt;grid .p-4 [href=’/exercise/list_basic_detail/93926-C/’]&lt;/em&gt;. The &lt;em&gt;querySelector() *method returns the element (*i.e., elem_prod1&lt;/em&gt;) matching the selector.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.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%2AFdgUsgkb4eDRdUb0.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.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%2AFdgUsgkb4eDRdUb0.png" width="800" height="400"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The &lt;em&gt;click()&lt;/em&gt; method on &lt;em&gt;elem_prod1&lt;/em&gt; performs a click on the element. As always, we wait for a maximum of 60000 ms for the navigation to complete.&lt;/p&gt;

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

&lt;p&gt;&lt;strong&gt;Code Walkthrough (Test Scenario — 4)&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;In this test scenario, the target image (or element) is lazy-loaded and available only after 2~3 scrolls.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.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%2AynZrHVs53fAVU4-U.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.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%2AynZrHVs53fAVU4-U.png" width="800" height="400"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;After the test URL is loaded, the next step is to ensure that all the content on the page has loaded completely. For this, we perform a vertical scroll operation till the end of the page is reached. The logic of &lt;a href="https://www.lambdatest.com/blog/scroll-a-webpage-in-selenium-using-java/?utm_source=devto&amp;amp;utm_medium=organic&amp;amp;utm_campaign=feb_05&amp;amp;utm_term=bh&amp;amp;utm_content=blog" rel="noopener noreferrer"&gt;scrolling a page in Selenium&lt;/a&gt; is very similar to that used in Pyppeteer!&lt;/p&gt;

&lt;p&gt;&lt;em&gt;For vertical scrolling, we have created a user-defined method named &lt;a href="https://github.com/hjsblogger/web-automation-with-pyppeteer/blob/main/tests/handling-dynamic-content/test_page_class_lazy_loaded_content.py#L81" rel="noopener noreferrer"&gt;scroll_end_of_page()&lt;/a&gt;. I have ported the code from my &lt;a href="https://github.com/hjsblogger/web-scraping-with-python/blob/main/pageobject/helpers.py#L100" rel="noopener noreferrer"&gt;existing implementation&lt;/a&gt;, which I&lt;/em&gt; used for my other web scraping blog. 🙂&lt;/p&gt;

&lt;p&gt;Once the page is loaded (in maximized mode), we first get the scrollable height using the JavaScript function &lt;em&gt;document.documentElement.scrollHeight&lt;/em&gt;. The JavaScript logic is executed using the &lt;em&gt;page.evaluate()&lt;/em&gt; method.&lt;/p&gt;

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

&lt;p&gt;Next, a &lt;em&gt;While&lt;/em&gt; loop is run where a vertical scroll is performed using the &lt;em&gt;Window.scrollTo()&lt;/em&gt; method (in JS) with &lt;em&gt;document.documentElement.scrollHeight&lt;/em&gt; as the input argument. Since it is a JavaScript function, it is executed using the &lt;em&gt;evaluate()&lt;/em&gt; method of the *Page *class.&lt;/p&gt;

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

&lt;p&gt;To witness the &lt;em&gt;Window.scrollTo()&lt;/em&gt; method in action, open the Test URL in the browser. Then trigger the following commands in the browser console:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;&lt;em&gt;document.documentElement.scrollHeight&lt;/em&gt;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;em&gt;window.scrollTo(0, document.documentElement.scrollHeight)&lt;/em&gt;&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The above two actions are performed till we reach the end of the document (or page). This is when we break from the while loop!&lt;/p&gt;

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

&lt;p&gt;Now that the page load is complete, we scroll to the top of the page by executing the JS function &lt;em&gt;window.scrollTo(0, 0) *via the *evaluate()&lt;/em&gt; method of Pyppeteer.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.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%2Aw4-ueuDIkFT1nTeo.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.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%2Aw4-ueuDIkFT1nTeo.png" width="800" height="270"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Next, we locate the target element using the CSS Selector &lt;em&gt;div:nth-of-type(31) &amp;gt; .p-4 [href=’/exercise/list_basic_detail/94967-A/’]&lt;/em&gt; and scroll into the view using the user-defined function &lt;em&gt;scroll_to_element()&lt;/em&gt;.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.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%2AEdQzw9jmgO2yfuY1.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.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%2AEdQzw9jmgO2yfuY1.png" width="800" height="500"&gt;&lt;/a&gt;&lt;/p&gt;

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

&lt;p&gt;&lt;em&gt;Implementation of scroll_to_element() method&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.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%2AiudlQSXZNesM7eIL.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.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%2AiudlQSXZNesM7eIL.png" width="800" height="157"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Finally, a click is initiated on the element (i.e., &lt;em&gt;elem_button&lt;/em&gt;) located in the earlier step. As usual, the &lt;em&gt;waitForNavigation()&lt;/em&gt; helps in waiting for the page navigation to complete.&lt;/p&gt;

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

&lt;p&gt;The current page URL is obtained via the &lt;em&gt;page.url&lt;/em&gt; attribute. An assert is raised if the URL does not match with the expected URL!&lt;/p&gt;

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

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

&lt;p&gt;Invoke the command &lt;em&gt;make pyppeteer-dynamic-content&lt;/em&gt; on the terminal with the Chromium browser instantiated on the local machine (&lt;em&gt;EXEC_PLATFORM = local&lt;/em&gt;). You can also perform web scraping using Chromium (or Chrome) on the LambdaTest cloud grid (&lt;em&gt;EXEC_PLATFORM = cloud&lt;/em&gt;).&lt;/p&gt;

&lt;p&gt;As seen below, all four test scenarios are executed in parallel using the &lt;a href="https://pypi.org/project/pytest-xdist/" rel="noopener noreferrer"&gt;*pytest-xdist&lt;/a&gt;* plugin for parallel execution with pytest &amp;amp; Pyppeteer.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.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%2ARf33zM8ptz2-oAvp.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.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%2ARf33zM8ptz2-oAvp.png" width="800" height="400"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The execution snapshots of the test scenarios are below:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.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%2A6eKTmud8a0c1goW_.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.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%2A6eKTmud8a0c1goW_.png" width="800" height="434"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Execution: Test Scenario — 1&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.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%2AOJ8a2UrFYh88cjVk.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.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%2AOJ8a2UrFYh88cjVk.png" width="800" height="400"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Execution: Test Scenario — 2&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.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%2AZKNiwjaShJDXRNzL.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.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%2AZKNiwjaShJDXRNzL.png" width="800" height="400"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Execution: Test Scenario — 3&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.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%2AXPekmwJUrxW-IOi8.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.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%2AXPekmwJUrxW-IOi8.png" width="800" height="400"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Execution: Test Scenario — 4&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;We also executed the same tests on the LambdaTest Grid and the execution was successful!&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.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%2AXaNwdXxedKIBD0EF.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.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%2AXaNwdXxedKIBD0EF.png" width="800" height="400"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Shown below are the execution screenshots from &lt;a href="https://automation.lambdatest.com/build?utm_source=devto&amp;amp;utm_medium=organic&amp;amp;utm_campaign=feb_05&amp;amp;utm_term=bh&amp;amp;utm_content=webpage" rel="noopener noreferrer"&gt;LambdaTest Automation Dashboard&lt;/a&gt;:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.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%2ARe5nDs3-A4mlFYov.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.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%2ARe5nDs3-A4mlFYov.png" width="800" height="326"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Parallel Test Execution (In Progress)&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.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%2Acp_txYfWzbPGksse.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.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%2Acp_txYfWzbPGksse.png" width="800" height="400"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Test Execution (Completion)&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;Now that we have introduced the nuances of &lt;a href="https://www.lambdatest.com/blog/what-is-parallel-testing-and-why-to-adopt-it/?utm_source=devto&amp;amp;utm_medium=organic&amp;amp;utm_campaign=feb_05&amp;amp;utm_term=bh&amp;amp;utm_content=blog" rel="noopener noreferrer"&gt;parallel testing&lt;/a&gt; in this section let’s do a detailed deep-dive in the further sections of the blog!&lt;/p&gt;

&lt;h2&gt;
  
  
  Web scraping with Pyppeteer
&lt;/h2&gt;

&lt;p&gt;Python is one of the most prominent languages used extensively for web scraping. For starters, web scraping uses automated tools (or libraries) to extract meaningful information from websites.&lt;/p&gt;

&lt;p&gt;Competitor analysis, Data analysis, and Lead generation are some of the main uses of web scraping. Apart from Pyppeteer, Selenium, and Beautiful Soup can also scrap static and dynamic web content. If you want to learn about the fine nuances of web scraping, please refer to this detailed blog that deep-dives into &lt;a href="https://www.lambdatest.com/blog/web-scraping-with-python/?utm_source=devto&amp;amp;utm_medium=organic&amp;amp;utm_campaign=feb_05&amp;amp;utm_term=bh&amp;amp;utm_content=blog" rel="noopener noreferrer"&gt;web scraping using Python&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;For demonstration web scraping with Pyppeteer, I would be porting scraping logic that was used for scraping product content from LambdaTest eCommerce Playground.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Demonstration&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;As mentioned above, we would be scraping product meta-data (i.e., Name, Price, and Image Link) from the eCommerce website.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.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%2AsjuI-YyV5swD_jeq.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.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%2AsjuI-YyV5swD_jeq.png" width="800" height="492"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;em&gt;LambdaTest E-Commerce Playground&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;The scraping logic with Pyppeteer can be executed on local Chromium (i.e., &lt;em&gt;EXEC_PLATFORM=local&lt;/em&gt;) as well as Chrome/Chromium on LambdaTest Cloud Grid (i.e., &lt;em&gt;EXEC_PLATFORM=cloud)&lt;/em&gt;.&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;import asyncio
import pytest
from pyppeteer.errors import PageError
from urllib.parse import quote
import json
import os
import sys
from os import environ
from pyppeteer import connect, launch


test_url = 'https://ecommerce-playground.lambdatest.io/index.php?route=product/category&amp;amp;path=57'


@pytest.mark.asyncio
async def test_ecommerce_scraping(page):
   for iteration in range(1,6):
       meta_data_arr = []


       curr_test_url = test_url + "&amp;amp;page=" + str(iteration)
       meta_data_arr = await scrap_ecommerce(page, curr_test_url)
       print('\n')
       print("Product Page = " + curr_test_url)
       print("*********************************************************************************************************\n")
       await print_scrapped_content(meta_data_arr)


async def scrap_ecommerce(page, curr_test_url) -&amp;gt; list:
   await page.goto(curr_test_url, {'waitUntil': 'domcontentloaded'})


   rows = await page.querySelectorAll('.product-layout.product-grid.no-desc.col-xl-4.col-lg-4.col-md-4.col-sm-6.col-6')


   meta_data_arr = []
   # Extract information from the selected elements
   for row in rows:
       link = await row.querySelector('a.carousel.d-block.slide')
       name = await row.querySelector('h4.title')
       price = await row.querySelector('span.price-new')


       link_href = await page.evaluate('(element) =&amp;gt; element.getAttribute("href")', link)
       product_name = await page.evaluate('(element) =&amp;gt; element.textContent', name)
       product_price = await page.evaluate('(element) =&amp;gt; element.textContent', price)


       # Create a dictionary of the meta-data of the items on e-commerce store
       meta_data_dict = {
           'product link': link_href,
           'product name': product_name,
           'product price': product_price
       }


       meta_data_arr.append(meta_data_dict) 

   return meta_data_arr


# # Pagination - 1:5
# # Page 1: https://ecommerce-playground.lambdatest.io/index.php?route=product/category&amp;amp;path=57&amp;amp;page=1
# # Page 5: https://ecommerce-playground.lambdatest.io/index.php?route=product/category&amp;amp;path=57&amp;amp;page=5


# Replica of https://github.com/hjsblogger/web-scraping-with-python/blob/main/pageobject/helpers.py#L146
async def print_scrapped_content(meta_data):
   for elem_info in meta_data:
       print(elem_info)
&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;To get started, we set up the test URL that comprises of the base URL followed by the page number (i.e. &amp;amp;&lt;em&gt;page=&amp;lt; page-number &amp;gt;&lt;/em&gt;).&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.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%2Arr8UcymyT5sAeRLN.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.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%2Arr8UcymyT5sAeRLN.png" width="800" height="500"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.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%2Arhbq_RlfO1n94O7E.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.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%2Arhbq_RlfO1n94O7E.png" width="800" height="400"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;As seen above, there are a total of 5 pages in the product category. Hence, the scraping implementation is executed in an iterative loop [from 1 .. 6].&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.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%2AAgM1CycKQJAxoSzf.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.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%2AAgM1CycKQJAxoSzf.png" width="800" height="400"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Here are the two main coroutines (or methods) that are used in scraping the test website:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;&lt;a href="https://github.com/hjsblogger/web-automation-with-pyppeteer/blob/main/tests/web-scraping-content/test_scraping_with_pyppeteer.py#L25" rel="noopener noreferrer"&gt;*scrap_ecommerce()&lt;/a&gt;* — Method that scraps content [or meta-data] from the current URL and stores it in a list&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;a href="https://github.com/hjsblogger/web-automation-with-pyppeteer/blob/main/tests/web-scraping-content/test_scraping_with_pyppeteer.py#L57" rel="noopener noreferrer"&gt;*print_scrapped_content()&lt;/a&gt; *— Prints the content scrapped by the scrap_ecommerce() method&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Let’s look at each of them in more detail!&lt;/p&gt;

&lt;p&gt;The &lt;em&gt;scrap_ecommerce()&lt;/em&gt; method takes two parameters:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;&lt;em&gt;page&lt;/em&gt; — browser page that is created using the &lt;em&gt;newPage()&lt;/em&gt; method&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;em&gt;curr_test_url&lt;/em&gt; — URL (example) from which the content needs to be scrapped using Pyppeteer&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The &lt;em&gt;goto()&lt;/em&gt; method takes the test &lt;em&gt;URL&lt;/em&gt; as the input parameter. Along with it, the &lt;em&gt;waitUntil&lt;/em&gt; option is set to *domcontentloaded *(i.e. wait for a maximum of 30000 ms till the firing of the *DOMContentLoaded *event).&lt;/p&gt;

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

&lt;p&gt;The product page contains 15 products, where the &lt;em&gt;product meta-data&lt;/em&gt; is within a common CSS Selector&lt;br&gt;
&lt;em&gt;(.product-layout.product-grid.no-desc.col-xl-4.col-lg-4.col-md-4.col-sm-6.col-6)&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.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%2Ac5TWmxPxJc_lhKf6.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.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%2Ac5TWmxPxJc_lhKf6.png" width="800" height="500"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Hence, 15 elements (on every product page) match the element located using the above CSS Selector. The &lt;em&gt;querySelectorAll()&lt;/em&gt; method of the Page class returns a list that contains elements matching the provided CSS Selector. The list will be further used to iteratively scrap information (i.e., name, cost) of each product.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.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%2AitfkHj-RrwvJZMsU.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.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%2AitfkHj-RrwvJZMsU.png" width="800" height="121"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Next, we run a &lt;em&gt;for *loop (1 .. 16) to scrap information of all the 15 products (or elements) under the *div&lt;/em&gt; located in the previous step.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.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_qpi60xhp8_4HRW.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.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_qpi60xhp8_4HRW.png" width="800" height="558"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The &lt;strong&gt;product link&lt;/strong&gt; is obtained by locating the respective element using the &lt;em&gt;querySelector()&lt;/em&gt; method. As seen below, the first argument is the tag that must be searched for (i.e., a — anchor tag), and the second is the CSS *Class *attribute.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.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%2ARN2cztIWwhHzjvGE.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.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%2ARN2cztIWwhHzjvGE.png" width="800" height="400"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.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%2Aik3GxSvgQA8VXoS_.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.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%2Aik3GxSvgQA8VXoS_.png" width="800" height="558"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Now that we have located the element, the link (or href) to the product page is obtained by executing a JS-function (on browser) via the evaluate() method of the Page class.&lt;/p&gt;

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

&lt;p&gt;The JS function uses the arrow function syntax and takes a parameter named &lt;em&gt;element&lt;/em&gt;. The &lt;em&gt;getAttribute()&lt;/em&gt; method of the &lt;em&gt;Element&lt;/em&gt; interface returns the value of the &lt;em&gt;href&lt;/em&gt; attribute of the element.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.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%2AotF_W6yUMPdeOkeG.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.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%2AotF_W6yUMPdeOkeG.png" width="800" height="400"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.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%2A5o7QfMyuTzH4uaa4.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.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%2A5o7QfMyuTzH4uaa4.png" width="800" height="400"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The &lt;strong&gt;product name&lt;/strong&gt; is also scraped by first locating the respective element using the &lt;em&gt;querySelector()&lt;/em&gt; method. The first argument is the &lt;em&gt;h4&lt;/em&gt; tag, and the second argument is the Class Name locator &lt;em&gt;title&lt;/em&gt;.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.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%2A1iUIxnFiV6ARVP15.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.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%2A1iUIxnFiV6ARVP15.png" width="800" height="543"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.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%2AEDzyzzOgI32HuLSo.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.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%2AEDzyzzOgI32HuLSo.png" width="800" height="400"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Once the element is located, the text content is obtained using the JavaScript method — &lt;em&gt;textContent()&lt;/em&gt; on the located element (i.e., &lt;em&gt;element.textContent&lt;/em&gt;). It uses the arrow function syntax where the element (i.e., &lt;em&gt;name&lt;/em&gt;) is passed as an input parameter.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.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%2A0PlpoEyfUy2a-Z8P.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.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%2A0PlpoEyfUy2a-Z8P.png" width="800" height="500"&gt;&lt;/a&gt;&lt;/p&gt;

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

&lt;p&gt;Lastly, the &lt;strong&gt;price&lt;/strong&gt; of the respective product is scraped by first locating the element using the &lt;em&gt;querySelector()&lt;/em&gt; method. As seen below, the text under the element with Class Name &lt;em&gt;price-new&lt;/em&gt; provides the product price.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.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%2AsTNyeNUS8oX36-0V.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.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%2AsTNyeNUS8oX36-0V.png" width="800" height="500"&gt;&lt;/a&gt;&lt;/p&gt;

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

&lt;p&gt;Like before, the text content is obtained using the JavaScript method — &lt;em&gt;textContent()&lt;/em&gt; (or &lt;em&gt;element.textContent&lt;/em&gt;). It uses the arrow function syntax where the element (i.e., &lt;em&gt;price&lt;/em&gt;) is passed as an input parameter.&lt;/p&gt;

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

&lt;p&gt;Now that we have scraped the required information (or meta-data), a dictionary (i.e., &lt;em&gt;meta_data_dict&lt;/em&gt;) is created and appended to the list (i.e., &lt;em&gt;meta_data_arr&lt;/em&gt;).&lt;/p&gt;

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

&lt;p&gt;The above-mentioned steps are repeated for all the other product pages (i.e., Product Page 1 → Product Page 5), and the scraped data is displayed on the screen.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.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%2AT7LheIRTxCR7dd3i.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.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%2AT7LheIRTxCR7dd3i.png" width="800" height="251"&gt;&lt;/a&gt;&lt;/p&gt;

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

&lt;p&gt;Invoke the command &lt;em&gt;make pyppeteer-web-scraping&lt;/em&gt; on the terminal with the Chromium browser instantiated on the local machine (&lt;em&gt;EXEC_PLATFORM = local&lt;/em&gt;). You can also perform web scraping using Chromium (or Chrome) on the LambdaTest cloud grid (&lt;em&gt;EXEC_PLATFORM = cloud&lt;/em&gt;).&lt;/p&gt;

&lt;p&gt;As seen below, the Chrome browser was successfully instantiated on the local machine.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.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%2A9sqFZX09fg9j5Gci.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.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%2A9sqFZX09fg9j5Gci.png" width="800" height="400"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Here are the screenshots of the scrapped content of &lt;a href="https://ecommerce-playground.lambdatest.io/index.php?route=product/category&amp;amp;path=57&amp;amp;page=1" rel="noopener noreferrer"&gt;Product Page 1&lt;/a&gt; and &lt;a href="https://ecommerce-playground.lambdatest.io/index.php?route=product/category&amp;amp;path=57&amp;amp;page=5" rel="noopener noreferrer"&gt;Product Page 5&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.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%2AhyG8Je4qq3M0iLW7.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.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%2AhyG8Je4qq3M0iLW7.png" width="800" height="400"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.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%2AcxFLFDix_2pIMIQq.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.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%2AcxFLFDix_2pIMIQq.png" width="800" height="400"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;With this, we have successfully scraped a website using Pyppeteer! As always, use web scraping with utmost caution and responsibility. 🙂&lt;/p&gt;

&lt;h2&gt;
  
  
  Parallel execution with Pyppeteer
&lt;/h2&gt;

&lt;p&gt;Pyppeteer does not have built-in support for parallel test execution. However, tests can be run parallel with Pyppeteer by leveraging other test automation frameworks (or libraries) offering support.&lt;/p&gt;

&lt;p&gt;Before you parallelize the tests, it is important to follow the test execution best practices, the most important being test isolation (or independent tests).&lt;/p&gt;

&lt;p&gt;In the section where we demonstrated dynamic content handling with Pyppeteer, we used the pytest plugin &lt;em&gt;pytest-xdist&lt;/em&gt; to trigger the tests in parallel. If you are new to pytest, check out the &lt;a href="https://www.lambdatest.com/learning-hub/pytest-tutorial?utm_source=devto&amp;amp;utm_medium=organic&amp;amp;utm_campaign=feb_05&amp;amp;utm_term=bh&amp;amp;utm_content=learning_hub" rel="noopener noreferrer"&gt;pytest tutorial&lt;/a&gt; that deep-dives into the basic nitty-gritty of the framework.&lt;/p&gt;

&lt;p&gt;The video below shows how to use the &lt;em&gt;pytest-xdist&lt;/em&gt; plugin with Selenium, but the learnings also apply to Pyppeteer!&lt;br&gt;
&lt;iframe width="710" height="399" src="https://www.youtube.com/embed/m0XcQcb25p8"&gt;
&lt;/iframe&gt;
&lt;br&gt;
You can subscribe to the &lt;a href="https://www.youtube.com/c/LambdaTest?sub_confirmation=1?utm_source=devto&amp;amp;utm_medium=organic&amp;amp;utm_campaign=feb_05&amp;amp;utm_term=bh&amp;amp;utm_content=youtube" rel="noopener noreferrer"&gt;LambdaTest YouTube Channel&lt;/a&gt; for more tutorials on &lt;a href="https://www.lambdatest.com/selenium?utm_source=devto&amp;amp;utm_medium=organic&amp;amp;utm_campaign=feb_05&amp;amp;utm_term=bh&amp;amp;utm_content=webpage" rel="noopener noreferrer"&gt;Selenium testing&lt;/a&gt;, &lt;a href="https://www.lambdatest.com/playwright-testing?utm_source=devto&amp;amp;utm_medium=organic&amp;amp;utm_campaign=feb_05&amp;amp;utm_term=bh&amp;amp;utm_content=webpage" rel="noopener noreferrer"&gt;Playwright testing&lt;/a&gt;, &lt;a href="https://www.lambdatest.com/learning-hub/cypress-tutorial?utm_source=devto&amp;amp;utm_medium=organic&amp;amp;utm_campaign=feb_05&amp;amp;utm_term=bh&amp;amp;utm_content=youtube" rel="noopener noreferrer"&gt;Cypress testing&lt;/a&gt;, and more.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;pytest-parallel&lt;/em&gt; is another popular pytest plugin that can be used instead of &lt;em&gt;pytest-xdist&lt;/em&gt;. The major advantage of the &lt;em&gt;pytest-parallel&lt;/em&gt; plugin is its thread-safe approach and improved performance due to the usage of non-blocking IO for HTTP requests. Though we could have also chosen unittest (or PyUnit), features like fixtures, parameterized testing, and advanced assertions make pytest the de-facto choice for parallel testing.&lt;/p&gt;

&lt;p&gt;As a part of the installation, we had already installed the &lt;em&gt;pytest-xdist&lt;/em&gt; plugin via requirements.txt&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;pytest
pyppeteer
pytest-asyncio
pytest-xdist
pytest-order
py
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;&lt;strong&gt;Demonstration&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;For demonstrating parallel execution with Pyppeteer using pytest, we have created two Python files that have their implementation of fixtures and tests. Setting the &lt;em&gt;EXEC_PLATFORM&lt;/em&gt; to local lets you run tests in parallel on the local machine, whereas setting &lt;em&gt;EXEC_PLATFORM&lt;/em&gt; to cloud runs tests on the LambdaTest cloud grid.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.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%2AihiI9sBEsyvWqYdv5vQ23Q.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.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%2AihiI9sBEsyvWqYdv5vQ23Q.png" width="800" height="400"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Since the overall implementation steps remain largely unchanged, we would be avoiding code walkthrough for this section!&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;import asyncio
import pytest
from pyppeteer.errors import PageError
from urllib.parse import quote
import json
import os
from os import environ
from pyppeteer import connect, launch


exec_platform = os.getenv('EXEC_PLATFORM')


# Get username and access key of the LambdaTest Platform
username = environ.get('LT_USERNAME', None)
access_key = environ.get('LT_ACCESS_KEY', None)


cloud_capabilities = {
   'browserName': 'chrome',
   'browserVersion': 'latest',
   'LT:Options': {
       'platform': 'Windows 11',
       'build': '[Build] Duckduckgo Search using Pyppeteer',
       'name': 'Duckduckgo Search using Pyppeteer',
       'resolution': '1920x1080',
       'user': username,  # Replace with your LT_USERNAME
       'accessKey': access_key,  # Replace with your LT_ACCESS_KEY
       'network': True,
       'video': True,
       'console': True,
       'headless': False
   }
}


@pytest.fixture(scope='function')
async def browser():
   if exec_platform == 'local':
       browser = await launch(headless = False, args=['--start-maximized'])
   elif exec_platform == 'cloud':
       capability = quote(json.dumps(cloud_capabilities))
       print('Initializing test:: ', cloud_capabilities['LT:Options']['name'])


       browser = await connect(
           browserWSEndpoint=f'wss://cdp.lambdatest.com/puppeteer?capabilities={capability}'
       )
   yield browser
   await asyncio.sleep(1)   


# Pytest fixture for page setup
@pytest.fixture(scope='function')
async def page(browser):
   page = await browser.newPage()
   yield page

   await page.close()
   await asyncio.sleep(1)
   await browser.close()


@pytest.mark.usefixtures('page')


@pytest.mark.asyncio
async def test_search_1(page):
   # asyncio.set_event_loop(asyncio.new_event_loop())
   await page.goto('https://www.duckduckgo.com')
   # Maximize the page
   await page.setViewport({'width': 1920, 'height': 1080})
   element = await page.querySelector('[name="q"]')
   await element.click()
   await element.type('LambdaTest')
   await asyncio.gather(
       page.keyboard.press('Enter'),
       page.waitForNavigation()
   )


   await asyncio.sleep(1)


   title = await page.title()


   try:
       assert title == 'LambdaTest at DuckDuckGo', 'Expected page title is incorrect!'
       await page.evaluate('_ =&amp;gt; {}', f'lambdatest_action: {json.dumps({ "action": "setTestStatus", "arguments": { "status": "passed", "remark": "Title matched" } })}')
   except PageError as e:
       await page.evaluate('_ =&amp;gt; {}', f'lambdatest_action: {json.dumps({ "action": "setTestStatus", "arguments": { "status": "failed", "remark": str(e) } })}')


   await asyncio.sleep(1)


   return title

import asyncio
import pytest
from pyppeteer.errors import PageError
from urllib.parse import quote
import json
import os
from os import environ
from pyppeteer import connect, launch


exec_platform = os.getenv('EXEC_PLATFORM')


# Get username and access key of the LambdaTest Platform
username = environ.get('LT_USERNAME', None)
access_key = environ.get('LT_ACCESS_KEY', None)


cloud_capabilities = {
   'browserName': 'chrome',
   'browserVersion': 'latest',
   'LT:Options': {
       'platform': 'Windows 10',
       'build': '[Build] Brave Search using Pyppeteer',
       'name': 'Brave Search using Pyppeteer',
       'resolution': '1920x1080',
       'user': username,  # Replace with your LT_USERNAME
       'accessKey': access_key,  # Replace with your LT_ACCESS_KEY
       'geoLocation': 'US',
       'network': True,
       'video': True,
       'console': True,
       'headless': False
   }
}


@pytest.fixture(scope='function')
async def browser():
   if exec_platform == 'local':
       browser = await launch(headless = False, args=['--start-maximized'])
   elif exec_platform == 'cloud':
       capability = quote(json.dumps(cloud_capabilities))
       print('Initializing test:: ', cloud_capabilities['LT:Options']['name'])


       browser = await connect(
           browserWSEndpoint=f'wss://cdp.lambdatest.com/puppeteer?capabilities={capability}'
       )
   yield browser
   await asyncio.sleep(1)


# Pytest fixture for page setup
@pytest.fixture(scope='function')
async def page(browser):
   page = await browser.newPage()
   yield page

   await page.close()
   await asyncio.sleep(1)
   await browser.close()


@pytest.mark.usefixtures('page')       
@pytest.mark.asyncio
async def test_search_2(page):
   await page.goto('https://search.brave.com')

   # Maximize the page
   await page.setViewport({'width': 1920, 'height': 1080})

   element = await page.querySelector('#searchbox')
   await element.click()
   await element.type('LambdaTest')
   await asyncio.gather(
       page.keyboard.press('Enter'),
       page.waitForNavigation()
   )


   await asyncio.sleep(1)


   title = await page.title()


   try:
       assert title == 'LambdaTest - Brave Search', 'Expected page title is incorrect!'
       await page.evaluate('_ =&amp;gt; {}', f'lambdatest_action: {json.dumps({ "action": "setTestStatus", "arguments": { "status": "passed", "remark": "Title matched" } })}')
   except PageError as e:
       await page.evaluate('_ =&amp;gt; {}', f'lambdatest_action: {json.dumps({ "action": "setTestStatus", "arguments": { "status": "failed", "remark": str(e) } })}')


   await asyncio.sleep(1)


   return title
&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;Invoke the command &lt;em&gt;make pytest-pyppeteer&lt;/em&gt; on the terminal to trigger the execution. As seen in the execution snapshot on the &lt;strong&gt;&lt;em&gt;local machine&lt;/em&gt;&lt;/strong&gt;, the &lt;em&gt;-n 2&lt;/em&gt; option starts two processes in parallel, with tests in both files executing in separate processes.&lt;/p&gt;

&lt;p&gt;It is important to note that tests in separate files are executed in parallel, whereas multiple tests in the same file run in a serial manner.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.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%2AEvH70-a_LN3SEZ6l.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.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%2AEvH70-a_LN3SEZ6l.png" width="800" height="290"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Here is the browser output, which indicates that the tests were executed successfully.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.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%2AJP2XLENo0GbRDat0.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.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%2AJP2XLENo0GbRDat0.png" width="800" height="490"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Execution — Test Scenario: 1&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.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%2AYnQPa25U_zKtuzGW.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.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%2AYnQPa25U_zKtuzGW.png" width="800" height="490"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Execution — Test Scenario: 2&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;You can find the LambdaTest username and access key in the &lt;a href="https://accounts.lambdatest.com/security?utm_source=devto&amp;amp;utm_medium=organic&amp;amp;utm_campaign=feb_05&amp;amp;utm_term=bh&amp;amp;utm_content=webpage" rel="noopener noreferrer"&gt;LambdaTest Password &amp;amp; Security page&lt;/a&gt;. Shown below is the execution on the LambdaTest cloud, which was triggered by setting EXEC_PLATFORM to &lt;em&gt;cloud&lt;/em&gt;.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.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%2AyFGmPhXzCsCYstyZ.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.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%2AyFGmPhXzCsCYstyZ.png" width="800" height="325"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;As seen on the dashboard, the tests in both the Python files are running in parallel.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.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%2AR66gpEuYUNraV3L3.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.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%2AR66gpEuYUNraV3L3.png" width="800" height="400"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;As seen below, the test execution was successful.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.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%2A0miT34emO6pW2-zc.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.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%2A0miT34emO6pW2-zc.png" width="800" height="419"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Execution on LambdaTest — Test Scenario: 1&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.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%2AheAGwRNxuvcltYQr.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.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%2AheAGwRNxuvcltYQr.png" width="800" height="400"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Execution on LambdaTest — Test Scenario: 2&lt;/em&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Difference between Pyppeteer and Puppeteer
&lt;/h2&gt;

&lt;p&gt;As stated in the official Pyppeteer documentation, Pyppeteer is an unofficial port of Puppeteer. Both frameworks provide high-level APIs that let you interact with the Elements in a document (or page) when opened on Chrome (or Chromium browsers).&lt;/p&gt;

&lt;h2&gt;
  
  
  Language-specific differences
&lt;/h2&gt;

&lt;p&gt;Python-based Pyppeteer &amp;amp; JavaScript-based Puppeteer are very much similar, barring aside some high-level differences. For the Pyppeteer demonstration on LambdaTest, we used the endpoint that is also used for Puppeteer:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.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%2AtCdJUeNCaf7Cd28H.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.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%2AtCdJUeNCaf7Cd28H.png" width="800" height="400"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Since Pyppeteer is based on Python, it leverages the &lt;em&gt;asyncio&lt;/em&gt; library for writing concurrent code using the &lt;em&gt;async/await&lt;/em&gt; syntax. As Puppeteer is based on JavaScript, it leverages &lt;em&gt;Promises&lt;/em&gt; that are objects representing the completion (or failure) of an asynchronous operation.&lt;/p&gt;

&lt;h2&gt;
  
  
  Keyword arguments for options
&lt;/h2&gt;

&lt;p&gt;As seen so far, Pyppeteer accepts both dictionary &amp;amp; keyword arguments for options.&lt;/p&gt;

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

&lt;p&gt;On the other hand, Puppeteer uses object for passing options to methods.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.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%2AaOTkji1yKJYzZRTj.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.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%2AaOTkji1yKJYzZRTj.png" width="800" height="275"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  The evaluate() method
&lt;/h2&gt;

&lt;p&gt;The &lt;em&gt;page.evaluate()&lt;/em&gt; method in Pyppeteer takes a string of JavaScript.&lt;/p&gt;

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

&lt;p&gt;On the other hand, the &lt;em&gt;page.evaluate()&lt;/em&gt; method in Puppeteer takes JavaScript raw function (or string of JavaScript expression).&lt;/p&gt;

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

&lt;p&gt;There are a few more differences between Pyppeteer and Puppeteer, I recommend going through the Pyppeteer vs Puppeteer comparison for more information. Even though the Pyppeteer project is no longer under maintenance, it is still used by the community!&lt;/p&gt;

&lt;p&gt;If you are looking for options, you can switch to other frameworks like Puppeteer or Selenium. However, the choice depends on the project complexity and technical expertise of the said library (or framework).&lt;/p&gt;

&lt;h2&gt;
  
  
  It’s A Wrap
&lt;/h2&gt;

&lt;p&gt;Thanks for making it this far! In this super-exhaustive &lt;a href="https://www.lambdatest.com/learning-hub/python-tutorial?utm_source=devto&amp;amp;utm_medium=organic&amp;amp;utm_campaign=feb_05&amp;amp;utm_term=bh&amp;amp;utm_content=learning_hub" rel="noopener noreferrer"&gt;Python tutorial&lt;/a&gt; with Puppeteer, we deep-dived into almost all the use cases (or scenarios) that can be realized with the Pyppeteer library. Like other Python-based test automation frameworks like pytest, unittest, etc., Pyppeteer can be leveraged for &lt;a href="https://www.lambdatest.com/learning-hub/web-automation?utm_source=devto&amp;amp;utm_medium=organic&amp;amp;utm_campaign=feb_05&amp;amp;utm_term=bh&amp;amp;utm_content=learning_hub" rel="noopener noreferrer"&gt;web automation&lt;/a&gt; and scraping of pages.&lt;/p&gt;

&lt;p&gt;Lastly, switching (or porting) from Pyppeteer to Puppeteer/Selenium should be considered in scenarios where you want to harness the potential the other frameworks (or libraries) offer. You can also leverage Pyppeteer on LambdaTest since it lets you run tests at scale, that too on different versions of Chrome (or Chromium).&lt;/p&gt;

&lt;p&gt;The integration of Pyppeteer with LambdaTest provides a seamless environment for executing scripts, ensuring compatibility testing on a diverse set of browser versions. This combination not only enhances the scalability of your testing but also allows you to validate the performance and functionality of your applications in a wide range of browser environments. LambdaTest’s infrastructure, combined with Pyppeteer’s capabilities, provides a robust solution for efficient and comprehensive &lt;a href="https://www.lambdatest.com/learning-hub/web-application-testing?utm_source=devto&amp;amp;utm_medium=organic&amp;amp;utm_campaign=feb_05&amp;amp;utm_term=bh&amp;amp;utm_content=learning_hub" rel="noopener noreferrer"&gt;web application testing&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Happy Testing 🙂&lt;/p&gt;

&lt;h2&gt;
  
  
  Frequently Asked Questions (FAQs)
&lt;/h2&gt;

&lt;h3&gt;
  
  
  What is Pyppeteer in Python?
&lt;/h3&gt;

&lt;p&gt;Pyppeteer is a Python library that provides a high-level API to control headless browsers using the Chrome DevTools Protocol. It is essentially a Python port of the popular Node library Puppeteer, which is maintained by the Chrome team at Google.&lt;/p&gt;

&lt;h3&gt;
  
  
  What is the difference between Pyppeteer and Puppeteer?
&lt;/h3&gt;

&lt;p&gt;Pyppeteer and Puppeteer are sibling libraries designed for different programming languages, Python and JavaScript, respectively. Yet, they aim to provide a high-level interface to control headless browsers through the Chrome DevTools Protocol.&lt;/p&gt;

&lt;p&gt;Puppeteer, developed by Google for Node.js, has been widely adopted in the JavaScript community, offering features like web scraping and automated testing. On the other hand, Pyppeteer is the Python adaptation of Puppeteer, allowing Python developers to leverage similar capabilities using Python syntax and asyncio. Despite their language differences, both libraries empower developers to automate tasks in headless browsers effectively.&lt;/p&gt;

</description>
      <category>beginners</category>
      <category>programming</category>
      <category>tutorial</category>
      <category>webdev</category>
    </item>
    <item>
      <title>Selenium MSTest Tutorial: Setup Selenium WebDriver For MSTest Framework In C#</title>
      <dc:creator>himanshuseth004</dc:creator>
      <pubDate>Thu, 28 Dec 2023 06:32:43 +0000</pubDate>
      <link>https://forem.com/testmuai/selenium-mstest-tutorial-setup-selenium-webdriver-for-mstest-framework-in-c-mod</link>
      <guid>https://forem.com/testmuai/selenium-mstest-tutorial-setup-selenium-webdriver-for-mstest-framework-in-c-mod</guid>
      <description>&lt;p&gt;In the on-going ‘Selenium MSTest testing tutorial’ series, we earlier had a look at setting up the MSTest framework in Visual Studio. It was a getting started guide with a major focus on the installation of the Selenium MSTest framework and MSTest adapter for the project. In case you are starting up with the MSTest framework, we recommend that you check out the installation guide on &lt;a href="https://www.lambdatest.com/blog/mstest-tutorial-environment-setup-for-selenium-testing/?utm_source=devto&amp;amp;utm_medium=organic&amp;amp;utm_campaign=dec_28&amp;amp;utm_term=bh&amp;amp;utm_content=blog" rel="noopener noreferrer"&gt;MSTest Tutorial: Environment Setup For Selenium Testing&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;In this Selenium MSTest testing tutorial, we take a look at setting up &lt;a href="https://www.lambdatest.com/blog/selenium-webdriver-tutorial-with-examples/?utm_source=devto&amp;amp;utm_medium=organic&amp;amp;utm_campaign=dec_28&amp;amp;utm_term=bh&amp;amp;utm_content=blog" rel="noopener noreferrer"&gt;Selenium WebDriver&lt;/a&gt; with Visual Studio in C#. Before we install the Selenium WebDriver, we create a new project in C#.&lt;/p&gt;

&lt;p&gt;Starting your journey with Selenium WebDriver? Check out this step-by-step guide to perform Automation testing using Selenium &lt;a href="https://www.lambdatest.com/learning-hub/webdriver?utm_source=devto&amp;amp;utm_medium=organic&amp;amp;utm_campaign=dec_28&amp;amp;utm_term=bh&amp;amp;utm_content=learning_hub" rel="noopener noreferrer"&gt;WebDriver Tutorial&lt;/a&gt;.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;*&lt;em&gt;Need to convert hex to decimal? Our online &lt;a href="https://www.lambdatest.com/free-online-tools/hex-to-decimal?utm_source=devto&amp;amp;utm_medium=organic&amp;amp;utm_campaign=dec_28&amp;amp;utm_term=bh&amp;amp;utm_content=free_online_tools" rel="noopener noreferrer"&gt;Hex to Decimal&lt;/a&gt; Converter tool converts hexadecimal to decimal numbers quickly. Get your conversions done in no time. *&lt;/em&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2&gt;
  
  
  Creating a New Project in C
&lt;/h2&gt;

&lt;p&gt;Before installing Selenium WebDriver, we create a new project in C# by following the below-mentioned steps:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;1. Open Visual Studio and create a new project of the type ‘MSTest Test Project (.Net Core).’&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;a href="https://media2.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%2A3qtVNmZ3n4qBqPLi.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.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%2A3qtVNmZ3n4qBqPLi.png" width="512" height="362"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;As the created project is of type MSTest Test Project (.Net Core), the default C# class comes along with the attributes [TestMethod] and [TestClass] in it.&lt;/li&gt;
&lt;/ul&gt;

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

&lt;h2&gt;
  
  
  Steps to Download Selenium WebDriver
&lt;/h2&gt;

&lt;p&gt;Before running your &lt;a href="https://www.lambdatest.com/blog/mstest-tutorial-running-first-selenium-automation-script/?utm_source=devto&amp;amp;utm_medium=organic&amp;amp;utm_campaign=dec_28&amp;amp;utm_term=bh&amp;amp;utm_content=blog" rel="noopener noreferrer"&gt;first script in MSTest&lt;/a&gt;, you need to download and set up Selenium WebDriver.&lt;/p&gt;

&lt;p&gt;Selenium WebDriver on Visual Studio can be installed using NuGet, a free and open-source package manager that is specifically designed for the Microsoft Platform.&lt;br&gt;
There are two options for installing Selenium WebDriver — using the Visual Studio IDE and using the NuGet Package Manager (PM) Commands.&lt;/p&gt;
&lt;h2&gt;
  
  
  Using Visual Studio IDE
&lt;/h2&gt;

&lt;p&gt;For installing Selenium WebDriver using the VS IDE, please perform the following steps:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Navigate to Tools -&amp;gt; NuGet Package Manager -&amp;gt; Manager NuGet Packages for Solution and search for ‘Selenium.’&lt;/li&gt;
&lt;/ul&gt;

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

&lt;ul&gt;
&lt;li&gt;Select Selenium.WebDriver from the search list and click on the Install button.&lt;/li&gt;
&lt;/ul&gt;

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

&lt;p&gt;&lt;a href="https://media2.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%2AcQeF2rukJDBBtEqP.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.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%2AcQeF2rukJDBBtEqP.png" width="432" height="442"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;For &lt;a href="https://www.lambdatest.com/?utm_source=devto&amp;amp;utm_medium=organic&amp;amp;utm_campaign=dec_28&amp;amp;utm_term=bh&amp;amp;utm_content=webpage" rel="noopener noreferrer"&gt;automated browser testing&lt;/a&gt;, we make use of the Chrome web browser. Hence, we install the Selenium Chrome Driver. Select Selenium.WebDriver.ChromeDriver from the search list and click on the Install button.&lt;/li&gt;
&lt;/ul&gt;

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

&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;Now that the Selenium WebDriver installation is complete, you can use the package in the source code. Including the following statements in the source code to verify whether the Selenium WebDriver is installed or not.&lt;/p&gt;

&lt;p&gt;using Microsoft.VisualStudio.TestTools.UnitTesting;&lt;br&gt;
using OpenQA.Selenium;&lt;br&gt;
using OpenQA.Selenium.Chrome;&lt;/p&gt;

&lt;p&gt;namespace MSTest_Test_Project&lt;br&gt;
{&lt;br&gt;
    [TestClass]&lt;br&gt;
    public class UnitTest1&lt;br&gt;
    {&lt;br&gt;
        [TestMethod]&lt;br&gt;
        public void TestMethod1()&lt;br&gt;
        {&lt;br&gt;
        }&lt;br&gt;
    }&lt;br&gt;
}&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Simplify your work with our &lt;a href="https://www.lambdatest.com/free-online-tools/decimal-to-bcd?utm_source=devto&amp;amp;utm_medium=organic&amp;amp;utm_campaign=dec_28&amp;amp;utm_term=bh&amp;amp;utm_content=free_online_tools" rel="noopener noreferrer"&gt;Decimal to BCD&lt;/a&gt; Converter. Accurately convert decimal numbers to binary-coded decimal values quickly and easily! Try it now and simplify your work!&lt;/strong&gt;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;
  
  
  Using Package Manager (PM) Commands
&lt;/h2&gt;

&lt;p&gt;If you are more comfortable with the command-line terminal, Selenium WebDriver can also be installed by executing the relevant commands on the Package Manager (PM) console.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;In order to execute commands from the PM console, navigate to Tools -&amp;gt; NuGet Package Manager -&amp;gt; Package Manager Console.&lt;/li&gt;
&lt;/ul&gt;

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

&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;The following commands have to be executed on the Package Manager (PM) console:&lt;/p&gt;

&lt;p&gt;Install-Package Selenium.WebDriver&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;As the Chrome browser is used for testing, we install the Selenium Chrome Driver as we did in the earlier case.&lt;/p&gt;

&lt;p&gt;Install-Package Selenium.Chrome.WebDriver&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Shown below is the snapshot of the Package Manager Console:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.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%2AHm3MfUbTK8-3lIR1.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.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%2AHm3MfUbTK8-3lIR1.png" width="647" height="405"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.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%2AziQPkZwG4MrxgUN4.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.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%2AziQPkZwG4MrxgUN4.png" width="728" height="199"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;To verify whether the necessary packages are installed, execute the Get-Package command on the PM console.&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;PM&amp;gt; Get-Package

Id                                  Versions
--                                  --------                                          
Selenium.WebDriver                  {3.141.0}
Selenium.Chrome.WebDriver           {79.0.0}
...... ......
...... ......
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;
&lt;p&gt;This MSTest Tutorial for beginners and professionals will help you learn how to use MSTest framework with Selenium C# for performing Selenium automation testing.&lt;br&gt;
&lt;iframe width="710" height="399" src="https://www.youtube.com/embed/2XUQdN-Bi2o"&gt;
&lt;/iframe&gt;
&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Need to encode your text into UTF-8 format? Use our &lt;a href="https://www.lambdatest.com/free-online-tools/utf8-encode?utm_source=devto&amp;amp;utm_medium=organic&amp;amp;utm_campaign=dec_28&amp;amp;utm_term=bh&amp;amp;utm_content=free_online_tools" rel="noopener noreferrer"&gt;UTF-8 Encode&lt;/a&gt; tool to encode your text for seamless communication across all platforms and devices. Try it for free today.&lt;/strong&gt;&lt;/p&gt;
&lt;/blockquote&gt;

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

&lt;p&gt;&lt;a href="https://media2.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%2AYLQoBC80fgIvUyPZ.gif" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.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%2AYLQoBC80fgIvUyPZ.gif" width="435" height="202"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;With this Selenium MSTest setup example, we had a look at setting up &lt;a href="https://www.lambdatest.com/blog/selenium-webdriver-tutorial-with-examples/?utm_source=devto&amp;amp;utm_medium=organic&amp;amp;utm_campaign=dec_28&amp;amp;utm_term=bh&amp;amp;utm_content=blog" rel="noopener noreferrer"&gt;Selenium WebDriver&lt;/a&gt; for automated testing with MSTest and C#. The steps mentioned in this &lt;a href="https://www.lambdatest.com/learning-hub/selenium-c-sharp-tutorial?utm_source=devto&amp;amp;utm_medium=organic&amp;amp;utm_campaign=dec_28&amp;amp;utm_term=bh&amp;amp;utm_content=learning_hub" rel="noopener noreferrer"&gt;Selenium C# tutorial&lt;/a&gt; are essential to get started with automation testing with Selenium and C#. With the completion of the Selenium MSTest framework &amp;amp; Selenium installation, you are all set to perform &lt;a href="https://www.lambdatest.com/?utm_source=devto&amp;amp;utm_medium=organic&amp;amp;utm_campaign=dec_28&amp;amp;utm_term=bh&amp;amp;utm_content=webpage" rel="noopener noreferrer"&gt;cross browser testing&lt;/a&gt; or &lt;a href="https://www.lambdatest.com/selenium-automation-testing-with-MSTest?utm_source=devto&amp;amp;utm_medium=organic&amp;amp;utm_campaign=dec_28&amp;amp;utm_term=bh&amp;amp;utm_content=webpage" rel="noopener noreferrer"&gt;automated browser testing with MSTest&lt;/a&gt;, Selenium, and C# ☺.&lt;/p&gt;

&lt;h3&gt;
  
  
  Frequently Asked Questions
&lt;/h3&gt;

&lt;h3&gt;
  
  
  What is SpecFlow Selenium?
&lt;/h3&gt;

&lt;p&gt;SpecFlow is a BDD tool that helps automate acceptance tests, exploit the full power of .NET, and help improve your development process. It’s an interactive testing framework for .NET and C&lt;/p&gt;

&lt;h3&gt;
  
  
  Which is better: NUnit or MSTest?
&lt;/h3&gt;

&lt;p&gt;MsTest is similar to NUnit which is another unit testing framework with a focus on simplicity. The difference between the two lies in how they execute tests. In MSTest, tests are executed in parallel whereas in NUnit the test runner will wait for all tests to finish before moving on to the next test. This causes a delay when executing more than one test. Hence, MSTest offers advantages in speed and robustness when compared to NUnit.&lt;/p&gt;

</description>
      <category>selenium</category>
      <category>beginners</category>
      <category>tutorial</category>
      <category>opensource</category>
    </item>
    <item>
      <title>22 Practical Tips To Test Automation With Selenium WebDriver</title>
      <dc:creator>himanshuseth004</dc:creator>
      <pubDate>Tue, 26 Dec 2023 08:15:54 +0000</pubDate>
      <link>https://forem.com/testmuai/dkf-3in6</link>
      <guid>https://forem.com/testmuai/dkf-3in6</guid>
      <description>&lt;p&gt;Test automation with &lt;a href="https://www.lambdatest.com/selenium?utm_source=devto&amp;amp;utm_medium=organic&amp;amp;utm_campaign=dec_26&amp;amp;utm_term=bh&amp;amp;utm_content=webpage" rel="noopener noreferrer"&gt;Selenium&lt;/a&gt; has empowered website testers over the globe to perform automated website testing with ease. Webdriver is a core component of the Selenium framework using which you can perform automated &lt;a href="https://www.lambdatest.com/?utm_source=devto&amp;amp;utm_medium=organic&amp;amp;utm_campaign=dec_26&amp;amp;utm_term=bh&amp;amp;utm_content=webpage" rel="noopener noreferrer"&gt;cross browser testing&lt;/a&gt; of your website or web application against different types of browsers e.g. Google Chrome, Mozilla Firefox, Safari, Opera, Internet Explorer, Microsoft Edge, etc.&lt;/p&gt;

&lt;p&gt;The primary advantage of performing test automation with Selenium Webdriver over other web automation tools/frameworks is the support for a wide number of programming languages namely Python, Java, C#, Ruby, PHP, JavaScript, .Net, Perl, etc. If you are new to automation testing with Selenium WebDriver, then you can have a look at our &lt;a href="https://www.lambdatest.com/blog/selenium-webdriver-tutorial-for-cross-browser-testing/?utm_source=devto&amp;amp;utm_medium=organic&amp;amp;utm_campaign=dec_26&amp;amp;utm_term=bh&amp;amp;utm_content=blog" rel="noopener noreferrer"&gt;Selenium WebDriver tutorial for automated cross browser testing&lt;/a&gt; where we talk about the overall architecture of Selenium and how the framework can be used with popular programming languages. You can also check my previous article on &lt;a href="https://www.lambdatest.com/blog/selenium-grid-setup-tutorial-for-cross-browser-testing/?utm_source=devto&amp;amp;utm_medium=organic&amp;amp;utm_campaign=dec_26&amp;amp;utm_term=bh&amp;amp;utm_content=blog" rel="noopener noreferrer"&gt;Selenium Grid setup Tutorial for cross browser testing&lt;/a&gt;, to leverage the ability of &lt;a href="https://www.lambdatest.com/blog/speed-up-automated-parallel-testing-in-selenium-with-testng/?utm_source=devto&amp;amp;utm_medium=organic&amp;amp;utm_campaign=dec_26&amp;amp;utm_term=bh&amp;amp;utm_content=blog" rel="noopener noreferrer"&gt;parallel testing with Selenium&lt;/a&gt;. Irrespective of the programming language being used, there are certain best practices that are applicable for performing test automation with Selenium Webdriver (independent of the development language).&lt;/p&gt;

&lt;p&gt;In this article, I will share with you some key tips for Selenium automation testing, that touch upon aspects of code optimization, performance improvements, dynamic web-page loading, handling CSS and HTML code, etc.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Note&lt;/strong&gt; — Majority of these coding tips for test automation with &lt;a href="https://www.lambdatest.com/blog/selenium-webdriver-tutorial-with-examples/?utm_source=devto&amp;amp;utm_medium=organic&amp;amp;utm_campaign=dec_26&amp;amp;utm_term=bh&amp;amp;utm_content=blog" rel="noopener noreferrer"&gt;Selenium WebDriver&lt;/a&gt; are generic in nature and can be applied irrespective of the programming language used for the development of test scripts. However, for below demonstration, we have made use of Selenium with Python language.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Perform native app testing on real mobile devices online. Test on Android and iOS &lt;a href="https://www.lambdatest.com/test-on-mobile-devices?utm_source=devto&amp;amp;utm_medium=organic&amp;amp;utm_campaign=dec_26&amp;amp;utm_term=bh&amp;amp;utm_content=webpage" rel="noopener noreferrer"&gt;test devices online&lt;/a&gt; with LambdaTest. Run automated or manual native app tests. Start testing for free today!&lt;/strong&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2&gt;
  
  
  Setting The Executable Path Of The Selenium Webdriver
&lt;/h2&gt;

&lt;p&gt;In order to communicate with the browser under test, you need to first download the corresponding plugin/webdriver from their official website. This plugin would be responsible for communicating with the browser and it should be present in your machine (on which you are developing the tests). The plugin/webdriver path has to be set in the Selenium Webdriver configuration.&lt;/p&gt;

&lt;p&gt;Though the plugin/webdriver can be placed in any location since you can provide the static/relative path in the Selenium Webdriver configuration, the approach can be error prone and you need to keep track of the file path. A better &amp;amp; more reliable approach is to place the corresponding Selenium Webdriver in the location where the driver executable is present, in which case you need not specify the executable path in the Selenium Webdriver configuration.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.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%2A6masmdcM2c6gPXKv.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.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%2A6masmdcM2c6gPXKv.png" width="686" height="229"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;If the &lt;strong&gt;geckodriver&lt;/strong&gt; is not present in the Browser location, you need to manually add the path of the same in the source code. We import the &lt;strong&gt;selenium.webdriver.firefox.firefox_binary&lt;/strong&gt; module to provide the path to the Firefox executable.&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;from selenium import webdriver
from selenium.webdriver.firefox.firefox_binary import FirefoxBinary

ff_binary = FirefoxBinary('path/to/gecko driver')
browser = webdriver.Firefox(firefox_binary=ff_binary)
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;As seen in the code snippet below, we have not specified the location of the geckodriver (Firefox Webdriver) since it is placed in the same location where Firefox browser is present. This is more reliable approach compared to the previous one and can help in reducing basic errors in implementation of test automation with Selenium.&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;''' Import the required modules for development '''
from selenium import webdriver
from selenium.webdriver.common.keys import Keys
from time import sleep

'''Creation of Firefox Webdriver '''
driver = webdriver.Firefox()
driver.get("https://www.lambdatest.com/")
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;
&lt;h2&gt;
  
  
  Capture Screenshot Of Test Automation With Selenium WebDriver
&lt;/h2&gt;

&lt;p&gt;While performing tests, you would have come across requirements where a screenshot has to be captured for verifying results of the tests. &lt;a href="https://www.lambdatest.com/selenium-automation-testing-with-webdriverio-framework?utm_source=devto&amp;amp;utm_medium=organic&amp;amp;utm_campaign=dec_26&amp;amp;utm_term=bh&amp;amp;utm_content=webpage" rel="noopener noreferrer"&gt;Selenium WebDriver&lt;/a&gt; offers three APIs through which you can take a screenshot of a web-page.&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;&lt;p&gt;save_screenshot(‘path-where-screenshot-should-be-saved/filename.png’)&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;get_screenshot_as_file(‘path-where-screenshot-should-be-saved/filename.png’)&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;get_screenshot_as_png()&lt;/p&gt;&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;The first two APIs lets you save the screen of the current window in a .png file. The API returns False if there is an IOError, else it returns True. These APIs would only work if the file extension is .png, else Python throws an error &amp;amp; the saved content might not be viewable. If you wish to capture a screen of your current window in a binary format then make use of get_screenshot_as_png() API.&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;''' Import the required modules for development '''
from selenium import webdriver
import StringIO
from PIL import Image

'''Creation of Firefox Webdriver '''
driver = webdriver.Firefox()
driver.get("https://www.lambdatest.com/")

'''Taking screenshot of the web-page. File would be saved in the location where the source code is present '''

'''Option - 1'''
driver.save_screenshot('screenshot_1.png');


'''Option - 2'''
driver.get_screenshot_as_file('screenshot_2.png');
'''Option - 3'''
screenshot = driver.get_screenshot_as_png();

screenshot_size = (20, 10, 480, 600)
image = Image.open (StringIO.StringIO(screen))
region = image.crop(screenshot_size)
region.save('screenshot_3.jpg', 'JPEG', optimize=True)
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Test your native app and website on real iOS and Android devices hosted on the cloud. LambdaTest is a convenient, cost-effective and centralised solution for running realtime and Automated &lt;a href="https://www.lambdatest.com/real-device-cloud?utm_source=devto&amp;amp;utm_medium=organic&amp;amp;utm_campaign=dec_26&amp;amp;utm_term=bh&amp;amp;utm_content=webpage" rel="noopener noreferrer"&gt;test mobile application&lt;/a&gt; on real device cloud.&lt;/strong&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2&gt;
  
  
  Refreshing WebPage While Automation Testing With Selenium WebDriver
&lt;/h2&gt;

&lt;p&gt;There might be scenarios where there is a requirement for refreshing a web page, especially while waiting for a specific condition. There are a number of ways through which a webpage can be refreshed while performing test automation with Selenium Webdriver, the popular one is listed below.&lt;/p&gt;

&lt;h2&gt;
  
  
  1. driver.refresh() method
&lt;/h2&gt;

&lt;p&gt;As the name signifies, the &lt;strong&gt;refresh()&lt;/strong&gt; method is used to refresh the web page. It is asynchronous in nature hence; you should make use of this API in conjunction with &lt;strong&gt;document.readyState()&lt;/strong&gt;.&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;''' Import the required modules for development '''
from selenium import webdriver

'''Creation of Firefox Webdriver '''
driver = webdriver.Firefox()
driver.get("https://www.lambdatest.com/")
driver.refresh()
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;
&lt;h2&gt;
  
  
  2. ActionChains() method
&lt;/h2&gt;

&lt;p&gt;ActionChains() are another way to automate low-level interactions for automation testing with Selenium, such as key press, mouse button actions, etc. In order to refresh the webpage, we make use of the ‘CTRL + F5’ combination.&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;import time
from selenium import webdriver
from selenium.webdriver.common.action_chains import ActionChains
from selenium.webdriver.common.keys import Keys

'''Creation of Firefox Webdriver '''
# driver = webdriver.Chrome()
driver = webdriver.Firefox()
driver.get("https://www.lambdatest.com/")

time.sleep(5)

print("Before refresh")

ActionChains(driver) \
    .key_down(Keys.CONTROL) \
    .send_keys(Keys.F5) \
    .key_up(Keys.CONTROL) \
    .perform()

print("After refresh")

sleep(5)
driver.quit()
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;
&lt;h2&gt;
  
  
  Open A Webpage In A New Tab With Selenium Webdriver
&lt;/h2&gt;

&lt;p&gt;execute_script can be used to synchronously execute JavaScript code in current window/frame. Argument (a JavaScript) to open the webpage is passed as the argument to execute_script&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;from selenium import webdriver
from selenium.webdriver.common.keys import Keys
from time import sleep

driver = webdriver.Firefox()
driver.get("http://www.google.com/")

driver.implicitly_wait(10)

#open tab
driver.execute_script("window.open('https://www.lambdatest.com', 'new tab')")

sleep(5)
driver.quit()
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;
&lt;h2&gt;
  
  
  Saving Partial Screenshot Of A Web page With Selenium Webdriver
&lt;/h2&gt;

&lt;p&gt;There are cases where you might need to take a partial screenshot of a webpage as your perform test automation with Selenium. In such cases, you can make use of the Pillow module. You need to first install the Pillow/PIL module using the command&lt;/p&gt;

&lt;p&gt;pip install pillow&lt;/p&gt;

&lt;p&gt;Screenshot of the entire webpage is taken using the &lt;strong&gt;get_screenshot_as_png()&lt;/strong&gt; API. Once the screenshot is ready, the PIL library is used to open the captured image in memory, after which the image (which contains the entire webpage screenshot) is cropped to get the resultant image.&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;from selenium import webdriver
''' Install the Pillow module using the command pip install pillow '''
from PIL import Image
from io import BytesIO

driver = webdriver.Firefox()
driver.get('http://google.com/')

# Use the Inspection tool to find the location of the logo
element = driver.find_element_by_id('hplogo')
image_location = element.location
size = element.size

png = driver.get_screenshot_as_png()

''' Since the webpage screenshot is ready, we can exit the browser.'''
driver.quit()

''' PIL Library is used to open the image in memory '''
crop_image = Image.open(BytesIO(png))

''' Extract the Left, Right, Top, and Bottom co-ordinates ''' 

left = image_location['x']
top = image_location['y']
right = image_location['x'] + size['width']
bottom = image_location['y'] + size['height']

crop_image = crop_image.crop((left, top, right, bottom))
crop_image.save('logo-screenshot.png')
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Evaluate your native app and website by testing them on actual iOS and Android devices available on the &lt;a href="https://www.lambdatest.com/real-device-cloud?utm_source=devto&amp;amp;utm_medium=organic&amp;amp;utm_campaign=dec_26&amp;amp;utm_term=bh&amp;amp;utm_content=webpage" rel="noopener noreferrer"&gt;cloud android device&lt;/a&gt;. LambdaTest provides a convenient, cost-effective, and centralized solution for conducting real-time and automated tests on a cloud-based platform with real devices.&lt;/strong&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2&gt;
  
  
  Execute JavaScript Code In Selenium Webdriver
&lt;/h2&gt;

&lt;p&gt;execute_script is used to execute JavaScript code as you perform test automation with Selenium WebDriver. The syntax is &lt;strong&gt;driver.execute_script(“javascript code here”)&lt;/strong&gt;.&lt;/p&gt;

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

&lt;p&gt;As shown in the example below, a on_click action of Register is performed [Class name is home-cta].&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;from selenium import webdriver
from time import sleep

driver = webdriver.Firefox()
driver.get("https://www.lambdatest.com")

driver.execute_script("document.getElementsByClassName('home-cta')[0].click()")

sleep(10)

driver.close()
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;
&lt;h2&gt;
  
  
  Extracting Results Of JavaScript Code With Selenium Webdriver
&lt;/h2&gt;

&lt;p&gt;After invoking the JavaScript code for automation testing with Selenium, you need to extract the results of these JavaScript codes. You can make use of the return keyword in order to get the result of a JavaScript code as shown in the extended example where we explained JavaScript.&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;from selenium import webdriver
from time import sleep

driver = webdriver.Firefox()
driver.get("https://www.lambdatest.com")

driver.execute_script("document.getElementsByClassName('home-cta')[0].click()")

result = driver.execute_script("return 0")
print(result)

sleep(10)

driver.close()
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;
&lt;h2&gt;
  
  
  Handling Multiple Browser Types For Automated Cross Browser Testing Using Selenium
&lt;/h2&gt;

&lt;p&gt;There are multiple scenarios that you might need to test your code against different browsers e.g. Firefox, Chrome, Internet Explorer. The practice of testing a website across different browsers is termed as &lt;a href="https://www.lambdatest.com/?utm_source=devto&amp;amp;utm_medium=organic&amp;amp;utm_campaign=dec_26&amp;amp;utm_term=bh&amp;amp;utm_content=webpage" rel="noopener noreferrer"&gt;automated browser testing&lt;/a&gt;. To perform automated browser testing with Selenium automation testing, you should incorporate selective handling of those browsers in your unittest code or pytest code. A code snippet (which makes use of pytest) to handle multiple browsers is shown below:&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;# Import the 'modules' that are required for execution

import pytest
from selenium import webdriver
from selenium.webdriver.chrome.options import Options
from selenium.webdriver.common.keys import Keys
from time import sleep

#Fixture for Firefox
@pytest.fixture(params=["chrome", "firefox"],scope="class")
def driver_init(request):
    if request.param == "chrome":
        # Perform necessary actions here
    if request.param == "firefox":
    # Perform necessary actions here
    yield
    web_driver.close()
    ...........
    ...........
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Check your app and website by trying them out on real iOS and Android devices in the &lt;a href="https://www.lambdatest.com/real-device-cloud?utm_source=devto&amp;amp;utm_medium=organic&amp;amp;utm_campaign=dec_26&amp;amp;utm_term=bh&amp;amp;utm_content=webpage" rel="noopener noreferrer"&gt;cloud device testing&lt;/a&gt;. LambdaTest offers an easy, budget-friendly, and centralized way to run both real-time and automated tests on a cloud-based platform using real devices.&lt;/strong&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2&gt;
  
  
  Locating Elements On A Web Page Using CSS Locators
&lt;/h2&gt;

&lt;p&gt;As you perform test automation with Selenium, locating web elements on a page comes as a foundation to your automation scripts. In case you want to perform conditional execution based on the presence of a particular kind of web element like Tag, Class, ID, etc. you can make use of find_elements_*** API. Some of them are mentioned below&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;find_elements_by_class_name — Find elements by Class Name&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;find_elements — Find elements by strategy and locator&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;find_element_by_link_text — Find element by link text&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;find_element_by_partial_link_text — Find element by partial match of link text&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Shown below is the usage of find_element_by_partial_link_text &amp;amp; find_elements_by_class_name where the elements are searched on &lt;a href="https://www.lambdatest.com/?utm_source=devto&amp;amp;utm_medium=organic&amp;amp;utm_campaign=dec_26&amp;amp;utm_term=bh&amp;amp;utm_content=webpage" rel="noopener noreferrer"&gt;https://www.lambdatest.com/&lt;/a&gt; which is the URL under test.&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;from selenium import webdriver
from selenium.webdriver.chrome.options import Options
from selenium.webdriver.common.keys import Keys
from time import sleep
from selenium.common.exceptions import NoSuchElementException

driver = webdriver.Firefox()
driver.get("https://www.lambdatest.com")

try:
    element = driver.find_element_by_partial_link_text("START TESTING")
    print("Partial text Element found")
    element = driver.find_elements_by_class_name('home-btn-2')
    print("Button Element found")
except NoSuchElementException:
    print("No element found")

sleep(10)
driver.close()
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;
&lt;h2&gt;
  
  
  HTML Source Of WebElement In Selenium Webdriver
&lt;/h2&gt;

&lt;p&gt;innerHTML property can be used to capture the source code of a WebPage. innerHTML is also used to examine any changes in the page since the page was first loaded by the web browser. You can write the entire source code in a .html file for future reference.&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;from selenium import webdriver
from selenium.webdriver.chrome.options import Options
from selenium.webdriver.common.keys import Keys
from time import sleep
import io

driver = webdriver.Firefox()
driver.get("https://www.lambdatest.com")

elem = driver.find_element_by_xpath("//*")
source_code = elem.get_attribute("innerHTML")

filename = open('lambdatest_page_source.html', 'w')
filename.write(source_code)
filename.close()

sleep(10)

driver.close()
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;
&lt;h2&gt;
  
  
  Perform Mouse Over Actions In Selenium Webdriver
&lt;/h2&gt;

&lt;p&gt;There would be scenarios where you might need to perform a click on an item which is a part of the menu or an item which is a part of a multi-level menu. First, we locate the Menu item and then perform a click operation on the intended menu item.&lt;/p&gt;

&lt;p&gt;In the example below, the URL under test is &lt;a href="https://www.lambdatest.com/?utm_source=devto&amp;amp;utm_medium=organic&amp;amp;utm_campaign=dec_26&amp;amp;utm_term=bh&amp;amp;utm_content=webpage" rel="noopener noreferrer"&gt;https://www.lambdatest.com/&lt;/a&gt;. The intent is to navigate to the &lt;a href="https://www.lambdatest.com/selenium-automation?utm_source=devto&amp;amp;utm_medium=organic&amp;amp;utm_campaign=dec_26&amp;amp;utm_term=bh&amp;amp;utm_content=webpage" rel="noopener noreferrer"&gt;Selenium&lt;/a&gt; on the home page. The first task is to locate the Menu which matches the ID &lt;strong&gt;bs-example-navbar-collapse-1&lt;/strong&gt;. By using the Inspect Tool, we get the correct element-id, details is as shown in the snapshot&lt;/p&gt;

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

&lt;p&gt;We move to the Menu using the move_to_element operation which is a part of the action_chains module. Next task is to locate the Menu item which contains the text &lt;strong&gt;‘Automation’&lt;/strong&gt; for which we make use of &lt;strong&gt;find_element_by_xpath(“//a[contains(text(),’Automation’)]”)&lt;/strong&gt; after which Click operation is performed.&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;from selenium import webdriver
from selenium.webdriver.common.action_chains import ActionChains
from time import sleep

driver = webdriver.Firefox()
driver.get("https://www.lambdatest.com")

action = ActionChains(driver);

# Head on the top-level menu on Lambdatest website
parent_level_menu = driver.find_element_by_id("bs-example-navbar-collapse-1")
action.move_to_element(parent_level_menu).perform()

# Perform a click operation on the Menu item which contains text Automation
child_level_menu = driver.find_element_by_xpath("//a[contains(text(),'Automation')]")
child_level_menu.click();

sleep(10)

driver.close()
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Tired of unorganized YAML code? Our &lt;a href="https://www.lambdatest.com/free-online-tools/yaml-beautifier?utm_source=devto&amp;amp;utm_medium=organic&amp;amp;utm_campaign=dec_26&amp;amp;utm_term=bh&amp;amp;utm_content=free_tools_online" rel="noopener noreferrer"&gt;YAML Beautifier&lt;/a&gt; tool makes YAML code easy to read and understand. Simply paste your code and produce well-formatted document in seconds!&lt;/strong&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2&gt;
  
  
  Closing Tabs Without Closing The Browser In Selenium Webdriver
&lt;/h2&gt;

&lt;p&gt;One of the most basic yet mandatory tips for any test automation Selenium script is to realize how to close tabs without closing the entire browser. &lt;strong&gt;driver.close()&lt;/strong&gt; closes the focused tab and &lt;strong&gt;driver.quit()&lt;/strong&gt; will close all the tabs (of the browser) along with quitting of the driver. In case you need to keep the browser window open (and quitting all other tabs), you can make use of &lt;strong&gt;switch_to.window&lt;/strong&gt; method which has the input parameter as window &lt;strong&gt;handle-id&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Note&lt;/strong&gt; — There are other methods to approach this problem. &lt;strong&gt;window.open&lt;/strong&gt; method can be used with appropriate options (i.e. opening new window, opening new tab, etc.). Sending the right key combinations using &lt;strong&gt;send_keys&lt;/strong&gt; can be used, but the behavior has a dependency on the version of geckodriver (for Firefox), version of chromedriver, etc. Hence, &lt;strong&gt;send_keys approach is not preferable since the output can vary based on WebDriver versions.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;In the below example, we open one new window which contains the test URL and close other windows. We only make use of window_handles to achieve the requirement.&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;from selenium import webdriver
import time

driver = webdriver.Firefox()
driver.get('https://www.google.com')
# Open a new window
driver.execute_script("window.open('');")
time.sleep(5)
# Switch to the new window since the focus would still be on the old window
driver.switch_to.window(driver.window_handles[1])
driver.get("https://lambdatest.com")
time.sleep(5)
# close the active tab
driver.close()
time.sleep(5)
# Switch back to the first tab
driver.switch_to.window(driver.window_handles[0])
driver.get("https://www.yahoo.com")
time.sleep(5)
# Close the only tab, will also close the browser.
#driver.close()
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;
&lt;h2&gt;
  
  
  Handling Drop-Down Menu In A Page With Selenium Webdriver
&lt;/h2&gt;

&lt;p&gt;There is a requirement where you have to select a particular option from a drop-down menu present in a web-page. There are a number of ways in which you can select the desired option from the drop-down menu.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;select_by_index(desired_index_value)&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;select_by_visible_text(“text_to_be_selected_from_drop_down_menu”)&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;select_by_value(value)&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;We will use &lt;a href="http://demos.dojotoolkit.org/dijit/tests/test_Menu.html" rel="noopener noreferrer"&gt;http://demos.dojotoolkit.org/dijit/tests/test_Menu.html&lt;/a&gt; for Selenium automation testing on this requirement. Before we select the required element from the drop-down menu, it is important to get the ID of the element under test. We use find_element_by_xpath method to locate the element and once we have located the element (using the ID), we select the value from the drop-down menu.&lt;/p&gt;

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

&lt;p&gt;In the example below, we have shown the different methods through which you can select an element from the menu (&lt;strong&gt;@aria-label=’select’&lt;/strong&gt;)&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;from selenium import webdriver
from selenium.webdriver.support.ui import Select
from time import sleep
from selenium.common.exceptions import NoSuchElementException
from pip._vendor.distlib import resources

driver = webdriver.Firefox()
driver.get("http://demos.dojotoolkit.org/dijit/tests/test_Menu.html")

''' Let the page load completely '''
sleep(5)

try:
    ''' You can derive these details by using Web Inspector '''
    select_element = Select(driver.find_element_by_xpath("//select[@aria-label='select']"))
    # Option 1 - Selecting the drop-down item by using the text
    select_element.select_by_visible_text("bleed through")
    sleep(5)
    # Option 2 - Selecting the drop-down item by using the index value
    select_element.select_by_index(0)
    sleep(5)
    # Option 3 - Selection of desired option using value
    ''' This option would fail since there is no value in the page
     which we are testing right now ''' 
    # select_element.select_by_value('2')
except NoSuchElementException:
    print("Element not found")

''' Addition of delay so that we can have a look at the output '''
sleep(5)

''' Release the resources '''
driver.quit()
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Struggling with messy Lua scripts? Our &lt;a href="https://www.lambdatest.com/free-online-tools/lua-beautifier?utm_source=devto&amp;amp;utm_medium=organic&amp;amp;utm_campaign=dec_26&amp;amp;utm_term=bh&amp;amp;utm_content=free_tools_online" rel="noopener noreferrer"&gt;Lua Beautifier&lt;/a&gt; tool provides clean and structured formatting. Beautify your code and Improve readability today!&lt;/strong&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2&gt;
  
  
  Handling Operations With Check Boxes With Selenium Webdriver
&lt;/h2&gt;

&lt;p&gt;Check boxes are common elements in web pages that are used in scenarios where you have to select only one option from a number of options. Like drop-down menu handling, we locate the required checkbox using &lt;strong&gt;find_element_by_xpath&lt;/strong&gt; method and once we find the checkbox, a click operation is performed.&lt;/p&gt;

&lt;p&gt;We will use &lt;a href="http://demos.dojotoolkit.org/dijit/tests/form/test_CheckBox.html" rel="noopener noreferrer"&gt;http://demos.dojotoolkit.org/dijit/tests/form/test_CheckBox.html&lt;/a&gt; for Selenium automation testing and the requirement is to ‘check’ the checkbox which has the value cb7: normal checkbox. The matching is done using driver.find_elements_by_xpath(“//*[contains(text(), ‘text to be searched’)]”).&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;from selenium import webdriver
from selenium.webdriver.support.ui import Select
from time import sleep
from selenium.common.exceptions import NoSuchElementException
from pip._vendor.distlib import resources

driver = webdriver.Firefox()
driver.get("http://demos.dojotoolkit.org/dijit/tests/form/test_CheckBox.html")

''' Let the page load completely '''
sleep(20)

try:
    ''' You can derive these details by using Web Inspector '''
    driver.find_element_by_xpath("//*[contains(text(), 'cb7: normal checkbox')]").click()
except NoSuchElementException:
    print("Element not found")

''' Addition of delay so that we can have a look at the output '''
sleep(5)

''' Release the resources '''
driver.quit()
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;
&lt;h2&gt;
  
  
  Selecting Element via CSS Selector In Selenium Webdriver
&lt;/h2&gt;

&lt;p&gt;There is a provision to locate elements on a webpage using the CSS locator as you perform test automation with Selenium. &lt;strong&gt;find_elements_by_css_selector&lt;/strong&gt; can be used for locating elements where details of element (label, link, ID, etc.) to be located have to passed as the input argument. It finds a list of elements within this element’s children by CSS Selector.&lt;/p&gt;

&lt;p&gt;The intent is to locate the Login button on &lt;a href="https://www.lambdatest.com/?utm_source=devto&amp;amp;utm_medium=organic&amp;amp;utm_campaign=dec_26&amp;amp;utm_term=bh&amp;amp;utm_content=webpage" rel="noopener noreferrer"&gt;https://lambdatest.com/&lt;/a&gt; using find_elements_by_css_selector and perform click operation. The code associated with Login is below. Code inspection tool snapshot also gives the required information.&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;li class="login"&amp;gt;
&amp;lt;a href="https://accounts.lambdatest.com/register"&amp;gt;Free Sign Up&amp;lt;/a&amp;gt;
&amp;lt;/li&amp;gt;
.....
&amp;lt;/html&amp;gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

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

&lt;p&gt;Hence, we pass li.login as an argument to find_element_by_css_selector and once it locates the element, Click operation is performed.&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;from selenium import webdriver
from selenium.webdriver.support.ui import Select
from time import sleep
from selenium.common.exceptions import NoSuchElementException
from pip._vendor.distlib import resources

driver = webdriver.Firefox()
driver.get("https://www.lambdatest.com/")

''' Let the page load completely '''
sleep(20)

try:
    ''' You can derive these details by using Web Inspector '''
    driver.find_element_by_css_selector("li.login").click()
except NoSuchElementException:
    print("Element not found")

''' Addition of delay so that we can have a look at the output '''
sleep(5)

''' Release the resources '''
driver.quit()
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Struggling with messy &lt;a href="https://www.lambdatest.com/free-online-tools/php-beautifier?utm_source=devto&amp;amp;utm_medium=organic&amp;amp;utm_campaign=dec_26&amp;amp;utm_term=bh&amp;amp;utm_content=free_tools_online" rel="noopener noreferrer"&gt;PHP Beautifier&lt;/a&gt; &amp;amp; Formatter Online scripts? Our PHP Formatter &amp;amp; Beautifier Online tool provides clean and structured formatting. Beautify your code and Improve readability today!&lt;/strong&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2&gt;
  
  
  Explicit Wait For Handling Different Scenarios In Selenium Webdriver
&lt;/h2&gt;

&lt;p&gt;It is very normal to observe a scenario in Selenium automation testing where a webpage may take time to load or you want a particular web element on the page to be visible before triggering your test code. In such cases, you need to perform &lt;strong&gt;Explicit Wait&lt;/strong&gt; which is a piece of code through which define a condition to occur before you proceed further in the code.&lt;/p&gt;

&lt;p&gt;Selenium has &lt;strong&gt;WebDriverWait&lt;/strong&gt; which can be applied on any web element with a condition &amp;amp; time duration. It can throw an exception in case the element on which the wait is performed is not present or a timeout occurs.&lt;/p&gt;

&lt;p&gt;In the example below, we wait for link_text ‘Sitemap’ to load on the page and the timeout is specified in the &lt;strong&gt;WebDriverWait&lt;/strong&gt; method. If the element is not loaded within the timeout duration, we throw an exception.&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;from selenium import webdriver
from selenium.common.exceptions import TimeoutException
from selenium.common.exceptions import NoSuchElementException
from selenium.webdriver.common.by import By
from pip._vendor.distlib import resources
from selenium.webdriver.support.ui import WebDriverWait
from selenium.webdriver.support import expected_conditions as EC

driver = webdriver.Firefox()
driver.get("https://www.lambdatest.com/")
timeout = 10

try:
    ''' We wait till the time link with text SITEMAP is loaded '''
    ''' If that link text is not present on the page, it gives a time out '''
    ''' Try replacing Sitemap with Sitemap123 and you can encounter a timeout '''
    element_present = EC.presence_of_element_located((By.LINK_TEXT, 'Sitemap'))
    WebDriverWait(driver, timeout).until(element_present)
except TimeoutException:
    print("Timed out while waiting for page to load")
driver.quit()
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;
&lt;h2&gt;
  
  
  Scroll Operations In A Web Page With Selenium Webdriver
&lt;/h2&gt;

&lt;p&gt;You may have a requirement where you need to perform scroll-up/scroll-down operation on a page while performing test automation with Selenium. You can achieve the same using execute_script with window.scrollTo JS code as the argument. In the example below, we scroll to the end of the page after the website under test is loaded.&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;from selenium import webdriver
from time import sleep

driver = webdriver.Firefox()
driver.get("https://www.lambdatest.com/")
timeout = 10

''' Scroll to the end of the page '''
driver.execute_script("window.scrollTo(0, document.body.scrollHeight);")

''' Sleep is added so that you can have a look at the output '''
sleep(10)

''' Scroll again to the top of the page '''
driver.execute_script("window.scroll(0, 0);")

sleep(10)
driver.quit()
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Struggling with messy &lt;a href="https://www.lambdatest.com/free-online-tools/python-beautifier?utm_source=devto&amp;amp;utm_medium=organic&amp;amp;utm_campaign=dec_26&amp;amp;utm_term=bh&amp;amp;utm_content=free_tools_online" rel="noopener noreferrer"&gt;Python Beautifier&lt;/a&gt; &amp;amp; Formatter Online scripts? Our Python Formatter &amp;amp; Beautifier Online tool provides clean and structured formatting. Beautify your code and Improve readability today!&lt;/strong&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2&gt;
  
  
  Zoom In &amp;amp; Zoom out using Selenium
&lt;/h2&gt;

&lt;p&gt;In order to zoom in or zoom out while Selenium automation testing, you should use the &lt;strong&gt;transform CSS property&lt;/strong&gt; (for the corresponding browser) that lets you perform zoom in, zoom out, rotate, skew, etc. operations on the page.&lt;/p&gt;

&lt;p&gt;The CSS parameters for different types of browsers are below&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.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%2AK5zeG3xDGER9y8Cy.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.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%2AK5zeG3xDGER9y8Cy.png" width="800" height="400"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;In the example below, we zoom-out the web page that is loaded in the browser by 200% and later zoom in by 100% (i.e. back to normal). Since we are using the Firefox browser, we have made use of MozTransform CSS property.&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;from selenium import webdriver
from time import sleep

driver = webdriver.Firefox()
driver.get("https://www.lambdatest.com/")
timeout = 10

''' Zoom in by 200% '''
driver.execute_script('document.body.style.MozTransform = "scale(2.0)";')
driver.execute_script('document.body.style.MozTransformOrigin = "0 0";')

sleep(10)

''' Zoom out by 100% '''

driver.execute_script('document.body.style.MozTransform = "scale(1.0)";')
driver.execute_script('document.body.style.MozTransformOrigin = "0 0";')

''' Sleep is added so that you can have a look at the output '''
sleep(10)

''' Release all the resources '''
driver.quit()
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;
&lt;h2&gt;
  
  
  Find Size Of An Element In A Web Page With Selenium
&lt;/h2&gt;

&lt;p&gt;You have to first search for the element by ID and then use the .size property to calculate the size of the searched element. In the example below, we calculate the size of the button create_programmatic_menu (ID = createDestoryButton) in the page &lt;a href="http://demos.dojotoolkit.org/dijit/tests/test_Menu.html" rel="noopener noreferrer"&gt;http://demos.dojotoolkit.org/dijit/tests/test_Menu.html&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fcdn-images-1.medium.com%2Fmax%2F2728%2F0%2As43vzTg5BuIuGjt2.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fcdn-images-1.medium.com%2Fmax%2F2728%2F0%2As43vzTg5BuIuGjt2.png" width="800" height="383"&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 selenium import webdriver
from time import sleep

driver = webdriver.Firefox()
driver.get("http://demos.dojotoolkit.org/dijit/tests/test_Menu.html")
timeout = 10

search_element = driver.find_element_by_id("createDestroyButton")

print(search_element.size)

''' Release all the resources '''
driver.quit()
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;When you execute the above code, it would output the size of the button (ID — CreateDestroyButton).&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.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%2ARqhfpidTFFHITEDq.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.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%2ARqhfpidTFFHITEDq.png" width="504" height="136"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Get X &amp;amp; Y coordinates Of An Element In A Web Page In Selenium Webdriver
&lt;/h2&gt;

&lt;p&gt;You have to follow a similar approach which you used for calculating the size of an element. You have to first search for the element by ID and then use the &lt;strong&gt;.location property&lt;/strong&gt; to calculate the X &amp;amp; Y coordinates of the searched element.&lt;/p&gt;

&lt;p&gt;The test URL is &lt;a href="http://demos.dojotoolkit.org/dijit/tests/test_Menu.html" rel="noopener noreferrer"&gt;http://demos.dojotoolkit.org/dijit/tests/test_Menu.html&lt;/a&gt; and we calculate the X &amp;amp; Y co-ordinates of the button create_programmatic_menu (ID = createDestoryButton)&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;from selenium import webdriver
from time import sleep

driver = webdriver.Firefox()
driver.get("http://demos.dojotoolkit.org/dijit/tests/test_Menu.html")
timeout = 10

search_element = driver.find_element_by_id("createDestroyButton")

print(search_element.location)

''' Release all the resources '''
driver.quit()
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;When you execute the above code, it would output the X, Y coordinates e of the button (ID — CreateDestroyButton).&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.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%2AZN-HPOgsx5X4O6tL.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.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%2AZN-HPOgsx5X4O6tL.png" width="540" height="149"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Disable JavaScript Using Custom Profile Using Selenium Webdriver
&lt;/h2&gt;

&lt;p&gt;In case you want to disable JavaScript support of the browser to validate automated cross browser compatibility with Selenium automation testing, you need to change the profile settings of the browser under test (in our case, it is Firefox) and apply the changes to the profile. We make use of DEFAULT_PREFERENCES[‘frozen’][‘javascript.enabled’] = False to disable the JavaScript support for the browser.&lt;/p&gt;

&lt;p&gt;Once the code is executed, you should verify the profile changes by typing about:config in the address bar and searching for the value of javascript.enabled property.&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;from selenium import webdriver

''' Since we have the geckodriver &amp;amp; Firefox browser in same location '''
''' We do not pass the location of the Firefox profile '''
ff_profile = webdriver.FirefoxProfile()

ff_profile.DEFAULT_PREFERENCES['frozen']['javascript.enabled'] = False
ff_profile.set_preference("app.update.auto", False)
ff_profile.set_preference("app.update.enabled", False)

''' Update the preferences '''
ff_profile.update_preferences()

''' Load the Firefox browser with the updated profile '''
driver = webdriver.Firefox(ff_profile)

''' Verify whether the changes are working fine or not '''
driver.get("about:config")
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;Below is the screenshot of about:config settings (after the code execution) in Firefox&lt;/p&gt;

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

&lt;h2&gt;
  
  
  Setting Manual Proxy Settings With Selenium
&lt;/h2&gt;

&lt;p&gt;In some scenarios, you may want to change the Proxy Settings in order to execute your tests. For changing the proxy settings, you need to first import the module &lt;strong&gt;selenium.webdriver.common.proxy&lt;/strong&gt;. You have to set the proxy type to &lt;strong&gt;MANUAL&lt;/strong&gt;, after which you change the proxy settings and apply to the new settings to the browser under test (in our case it is Firefox).&lt;/p&gt;

&lt;p&gt;You need to replace ip_address &amp;amp; port_number with the IP address &amp;amp; port number that you plan to use for your testing.&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;from selenium import webdriver
from selenium.webdriver.common.proxy import Proxy, ProxyType

proxy_settings = Proxy()

''' The proxy settings are first changed to Manual '''
proxy_settings.proxy_type = ProxyType.MANUAL

''' Replace ip_address with Proxy IP address and '''
''' port_number with the port number which you plan to use '''
proxy_settings.http_proxy = "ip_address:port_number"
proxy_settings.socks_proxy = "ip_address:port_number"
proxy_settings.ssl_proxy = "ip_address:port_number"

''' You can add capabilties for different browsers &amp;amp; devices '''
capabilities = webdriver.DesiredCapabilities.FIREFOX
proxy_settings.add_to_capabilities(capabilities)

driver = webdriver.Firefox(desired_capabilities=capabilities)
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;&lt;a href="https://www.lambdatest.com/free-online-tools/json-to-xml-converter?utm_source=devto&amp;amp;utm_medium=organic&amp;amp;utm_campaign=dec_26&amp;amp;utm_term=bh&amp;amp;utm_content=free_tools_online" rel="noopener noreferrer"&gt;JSON to XML Converter&lt;/a&gt; is a simple and fast tool that converts JSON to XML data. Use this tool to convert your JSON documents into valid XML for easy data exchange.&lt;/strong&gt;&lt;/p&gt;
&lt;/blockquote&gt;

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

&lt;p&gt;We have covered the majority of the Selenium tips to help you perform test automation with Selenium like a professional. Though you can verify your website/web-app on different browsers, devices, and operating systems using your local machine, the extent of testing will be limited since you cannot cover the complete spectrum of devices + operating systems + browsers (&amp;amp; browser versions). This is where you should make use of these tips &amp;amp; tricks to perform automated cross browser testing for your website/web application.&lt;/p&gt;

&lt;p&gt;Rather than building a local infrastructure to execute the task of automated cross browser testing, you can make use of a scalable Selenium Grid on-cloud offered by LambdaTest. &lt;a href="https://accounts.lambdatest.com/register?utm_source=devto&amp;amp;utm_medium=organic&amp;amp;utm_campaign=dec_26&amp;amp;utm_term=bh&amp;amp;utm_content=webpage" rel="noopener noreferrer"&gt;Get started for free&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;You have to make minimal changes in order to move your local Selenium automation test code to &lt;a href="https://www.lambdatest.com/selenium-automation?utm_source=devto&amp;amp;utm_medium=organic&amp;amp;utm_campaign=dec_26&amp;amp;utm_term=bh&amp;amp;utm_content=webpage" rel="noopener noreferrer"&gt;LambdaTest Selenium&lt;/a&gt; as you begin to expand your test coverage by testing your web-app on more than 2000 real browsers + OS + devices combinations. You also get integrations to numerous CI CD tools, project management tools and more. Check out all of the &lt;a href="https://www.lambdatest.com/integrations?utm_source=devto&amp;amp;utm_medium=organic&amp;amp;utm_campaign=dec_26&amp;amp;utm_term=bh&amp;amp;utm_content=webpage" rel="noopener noreferrer"&gt;LambdaTest integrations&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Test automation with Selenium can be a tough task but with the actionable insights, rather practical tips for Selenium automation testing that I listed above in this article. You can be proficient with test automation in Selenium. Let me know if there are any other tips for test automation with Selenium that have helped you fast track your test cycles. Cheers &amp;amp; happy testing! 🙂&lt;/p&gt;

</description>
      <category>testing</category>
      <category>automation</category>
      <category>test</category>
      <category>automaton</category>
    </item>
    <item>
      <title>Web Scraping with Python Tutorial - A Complete Guide with Examples</title>
      <dc:creator>himanshuseth004</dc:creator>
      <pubDate>Wed, 22 Nov 2023 07:41:14 +0000</pubDate>
      <link>https://forem.com/testmuai/web-scraping-with-python-tutorial-a-complete-guide-with-examples-34d8</link>
      <guid>https://forem.com/testmuai/web-scraping-with-python-tutorial-a-complete-guide-with-examples-34d8</guid>
      <description>&lt;p&gt;We live in an era where we are surrounded by data that can be harnessed by extracting meaningful insights from it. As quoted by Tim Berners-Lee, inventor of the World Wide Web — “&lt;em&gt;Data is a precious thing and will last longer than the systems themselves&lt;/em&gt;.”&lt;/p&gt;

&lt;p&gt;If the data is the new oil, web scraping (or web harvesting) is the expeller that helps squeeze more oil.🙂Web scraping can be leveraged for analyzing and deriving actionable insights from tons of information available on the internet.&lt;/p&gt;

&lt;p&gt;Irrespective of the business domain (e.g., eCommerce, EdTech, Fintech, etc.), scraping can be used for market research, pricing intelligence, lead generation, and sentiment analysis, to name a few! Though web scraping is immensely useful, it comes with a caveat — &lt;em&gt;Scraping should be done legally and ethically, respecting the website’s T&amp;amp;C and data privacy regulations&lt;/em&gt;.&lt;/p&gt;

&lt;p&gt;Popular programming languages like Python, Java, JavaScript, etc., are well-equipped with tools and libraries that can ease the job of web scraping. In this blog, I will limit the discussion of web scraping with Python — a popular programming language for &lt;a href="https://www.lambdatest.com/learning-hub/automation-testing?utm_source=devto&amp;amp;utm_medium=organic&amp;amp;utm_campaign=nov_22&amp;amp;utm_term=bh&amp;amp;utm_content=learning_hub" rel="noopener noreferrer"&gt;automated testing&lt;/a&gt;. You can check my earlier blog on &lt;a href="https://www.lambdatest.com/blog/python-automation-testing/?utm_source=devto&amp;amp;utm_medium=organic&amp;amp;utm_campaign=nov_22&amp;amp;utm_term=bh&amp;amp;utm_content=blog" rel="noopener noreferrer"&gt;Python automation testing&lt;/a&gt;, highlighting why Python is the best-suited language for &lt;a href="https://www.lambdatest.com/automation-testing?utm_source=devto&amp;amp;utm_medium=organic&amp;amp;utm_campaign=nov_22&amp;amp;utm_term=bh&amp;amp;utm_content=webpage" rel="noopener noreferrer"&gt;automation testing&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;By the end of this blog on web scraping with Python, you will learn to scrap static and dynamic websites using the best Python tools (or libraries) like PyUnit, pytest, and Beautiful Soup. The actionable examples will help you harness the capabilities of web scraping with Python to extract meaningful insights from websites.&lt;/p&gt;

&lt;p&gt;Without further ado, let’s get started…&lt;/p&gt;

&lt;p&gt;**&lt;em&gt;Note: **Scrap &amp;amp; Scrape and document &amp;amp; page are used interchangeably throughout the blog.&lt;/em&gt;&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;&lt;a href="https://www.lambdatest.com/free-online-tools/text-uppercase?utm_source=devto&amp;amp;utm_medium=organic&amp;amp;utm_campaign=nov_22&amp;amp;utm_term=bh&amp;amp;utm_content=free_online_tools" rel="noopener noreferrer"&gt;Text Uppercase&lt;/a&gt; is a free online tool for converting text into uppercase. Enter your text and click the convert button to make all lowercase letters upper case.&lt;/strong&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2&gt;
  
  
  What is Web Scraping?
&lt;/h2&gt;

&lt;p&gt;To set the ball rolling, let’s do a quick recap of the &lt;em&gt;What&lt;/em&gt; &amp;amp; &lt;em&gt;Why&lt;/em&gt; of web scraping. In simple terms, web scraping, or web harvesting (or data extraction), is the technique for deriving information (&lt;em&gt;that matters&lt;/em&gt;) from websites.&lt;/p&gt;

&lt;p&gt;Shown below is the simplistic representation of web scraping that shows that input is a website that needs to be scrapped by the scraping logic.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.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%2An6QnUk1zqWGYuVsP.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.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%2An6QnUk1zqWGYuVsP.png" width="584" height="319"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Web Scraping Architecture&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;Once the information from the HTML of the page is scraped, the raw data can be stored in a more presentable (or readable) format in Excel sheets, databases, etc.&lt;/p&gt;

&lt;p&gt;With scraping options in the arsenal, it becomes imperative to opt for efficient and responsible scraping! In the examples presented later in this blog on web scraping with Python, the scraped information will be presented on the terminal.&lt;/p&gt;

&lt;h2&gt;
  
  
  Prominent Use Cases of Web Scraping
&lt;/h2&gt;

&lt;p&gt;Most new-age websites leverage JavaScript for loading and updating content on the page. AJAX (Asynchronous JavaScript and XML) is used to retrieve data from the server and update relevant parts of the page (without a full page reload).&lt;/p&gt;

&lt;p&gt;For instance, videos on the &lt;a href="https://www.youtube.com/@LambdaTest/videos?utm_source=devto&amp;amp;utm_medium=organic&amp;amp;utm_campaign=nov_22&amp;amp;utm_term=bh&amp;amp;utm_content=youtube" rel="noopener noreferrer"&gt;LambdaTest YouTube channel&lt;/a&gt; are dynamically loaded when the user performs a scroll operation. As seen in the below screenshots, the URL remains unchanged, but the videos are loaded on a dynamic basis.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.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%2AQszbUKuavRA2fau8.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.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%2AQszbUKuavRA2fau8.png" width="800" height="501"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.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%2Aw_OzabPSmjk_6H4a.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.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%2Aw_OzabPSmjk_6H4a.png" width="800" height="501"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Example — Dynamic Website Content&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;Popular Python frameworks like pytest and PyUnit, in conjunction with Selenium, can be used for dynamic web scraping websites (and SPAs) like YouTube, Netflix, &lt;a href="https://ecommerce-playground.lambdatest.io/?utm_source=devto&amp;amp;utm_medium=organic&amp;amp;utm_campaign=nov_22&amp;amp;utm_term=bh&amp;amp;utm_content=webpage" rel="noopener noreferrer"&gt;LambdaTest eCommerce Playground&lt;/a&gt;, and more.&lt;/p&gt;

&lt;p&gt;It goes without saying that scraping can be done on static websites where the entire HTML page content is downloaded and parsed to extract the desired information. You can check out my earlier blog highlighting the &lt;a href="https://www.lambdatest.com/blog/scraping-dynamic-web-pages/#staticdynamicdifference?utm_source=devto&amp;amp;utm_medium=organic&amp;amp;utm_campaign=nov_22&amp;amp;utm_term=bh&amp;amp;utm_content=blog" rel="noopener noreferrer"&gt;difference between Static and Dynamic Web Scraping&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Now that we know that it is possible to scrap static and dynamic websites, let’s look at some of the primary use cases of web scraping:&lt;/p&gt;

&lt;h2&gt;
  
  
  Competitor Analysis
&lt;/h2&gt;

&lt;p&gt;Irrespective of the size (or scale) of the business, businesses must keep a close watch on their competition. Insights into competitor’s products and services can be instrumental in having an upper edge over the competition.&lt;/p&gt;

&lt;p&gt;Web scraping can effectively scrape relevant information (e.g., products, services, pricing, etc.) from competitors’ websites. The scraped data can be leveraged for tweaking product &amp;amp; pricing-specific information on the website.&lt;/p&gt;

&lt;h2&gt;
  
  
  Lead Generation
&lt;/h2&gt;

&lt;p&gt;Web scraping can gather details (e.g., names, phone numbers, email addresses, etc.) from websites to generate leads for your business.&lt;/p&gt;

&lt;p&gt;The scraped data is then fed to CRM software, so the sales team can reach out to those leads.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Rotate your text and add some creativity to your words with our &lt;a href="https://www.lambdatest.com/free-online-tools/text-rotater?utm_source=devto&amp;amp;utm_medium=organic&amp;amp;utm_campaign=nov_22&amp;amp;utm_term=bh&amp;amp;utm_content=free_online_tools" rel="noopener noreferrer"&gt;Text Rotater&lt;/a&gt; tool. Try it out now and see the results in just a few clicks!&lt;/strong&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2&gt;
  
  
  Data Analysis
&lt;/h2&gt;

&lt;p&gt;Many websites have products that span across different categories. For instance, a horizontal eCommerce website might have products across electronics, apparel, baby care, etc.&lt;/p&gt;

&lt;p&gt;Web scraper is instrumental in extracting product metadata, seller details, number of SKUs, etc., from such websites. Whether it is a SPA (Single Page Application) or paginated content, relevant web scraping tools &amp;amp; libraries let you scrape information from them.&lt;/p&gt;

&lt;p&gt;Apart from the use cases mentioned above, you can also use web scraping for academic research and training data for ML projects!&lt;/p&gt;

&lt;h2&gt;
  
  
  Python Libraries and Tools for Web Scraping
&lt;/h2&gt;

&lt;p&gt;I have also tried &lt;a href="https://www.lambdatest.com/blog/scraping-dynamic-web-pages/?utm_source=devto&amp;amp;utm_medium=organic&amp;amp;utm_campaign=nov_22&amp;amp;utm_term=bh&amp;amp;utm_content=blog" rel="noopener noreferrer"&gt;web scraping using C#&lt;/a&gt;, but the web scraping ecosystem is nothing like Python. One of the major benefits of using Python is the availability of many tools and libraries for web scraping.&lt;/p&gt;

&lt;p&gt;It is recommended to perform the package installation in a virtual environment (venv) to isolate it from the packages in the base environment. Run the commands &lt;em&gt;virtualenv venv&lt;/em&gt; and &lt;em&gt;source venv/bin/activate&lt;/em&gt; on the terminal to create the virtual environment.&lt;/p&gt;

&lt;p&gt;Though I will be covering a few libraries (or tools) using a detailed demonstration, let’s list some of the popular ones below:&lt;/p&gt;

&lt;h2&gt;
  
  
  Selenium
&lt;/h2&gt;

&lt;p&gt;For starters, &lt;a href="https://www.lambdatest.com/selenium?utm_source=devto&amp;amp;utm_medium=organic&amp;amp;utm_campaign=nov_22&amp;amp;utm_term=bh&amp;amp;utm_content=webpage" rel="noopener noreferrer"&gt;Selenium&lt;/a&gt; is a popular open-source framework that helps with web browser automation. The tools and libraries offered by Selenium are instrumental in automating interactions with the elements in the document (or page). When writing this blog on web scraping with Python, the latest version was 4.14.0.&lt;/p&gt;

&lt;p&gt;Selenium 3, the earlier version of Selenium, used the JSON-Wire protocol. On the other hand, Selenium 4 uses the W3C protocol because automated tests are less flaky (or more stable) when compared to tests implemented using Selenium 3. For a quick recap, you can check out our blog highlighting the &lt;a href="https://www.lambdatest.com/blog/selenium-3-vs-selenium-4/?utm_source=devto&amp;amp;utm_medium=organic&amp;amp;utm_campaign=nov_22&amp;amp;utm_term=bh&amp;amp;utm_content=blog" rel="noopener noreferrer"&gt;differences between Selenium 3 and Selenium 4&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Most modern-day web pages use AJAX &amp;amp; JavaScript to load and display dynamic content. Consider &lt;a href="https://ecommerce-playground.lambdatest.io/?utm_source=devto&amp;amp;utm_medium=organic&amp;amp;utm_campaign=nov_22&amp;amp;utm_term=bh&amp;amp;utm_content=webpage" rel="noopener noreferrer"&gt;LambdaTest eCommerce playground&lt;/a&gt;. The content is loaded when the user scrolls the page. As seen in the screenshot below, the images are lazy loaded and appear in the DOM once the user scrolls the page.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.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%2AxJFKheutZqozUgLj.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.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%2AxJFKheutZqozUgLj.png" width="800" height="451"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.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%2AGsqBE7OtgWP3b13S.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.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%2AGsqBE7OtgWP3b13S.png" width="800" height="400"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Example of Lazy Loading of Images&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;This is where Selenium proves to be an instrumental option for web scraping due to its ability to handle dynamically loaded content using JavaScript.&lt;/p&gt;

&lt;p&gt;In the case of Python, &lt;a href="https://www.lambdatest.com/blog/best-test-automation-frameworks/?utm_source=devto&amp;amp;utm_medium=organic&amp;amp;utm_campaign=nov_22&amp;amp;utm_term=bh&amp;amp;utm_content=blog" rel="noopener noreferrer"&gt;popular test automation frameworks&lt;/a&gt; like pytest and PyUnit can be leveraged for scraping static and dynamic page content. Run the command &lt;em&gt;pip install selenium *(or *pip3 install selenium&lt;/em&gt;) depending on the Python version installed in the machine.&lt;/p&gt;

&lt;p&gt;PyUnit (or unittest ) is the default test automation framework, part of the Python standard library.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.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%2ADR_pR_rHrrJaGuyU.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.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%2ADR_pR_rHrrJaGuyU.png" width="800" height="400"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;PyUnit is inspired by the &lt;a href="https://www.lambdatest.com/blog/xunit-testing-tutorial/?utm_source=devto&amp;amp;utm_medium=organic&amp;amp;utm_campaign=nov_22&amp;amp;utm_term=bh&amp;amp;utm_content=blog" rel="noopener noreferrer"&gt;xUnit framework in C#&lt;/a&gt; and is also compatible with older versions of Python. For pytest, check out our zero-to-hero &lt;a href="https://www.lambdatest.com/learning-hub/selenium-pytest-tutorial?utm_source=devto&amp;amp;utm_medium=organic&amp;amp;utm_campaign=nov_22&amp;amp;utm_term=bh&amp;amp;utm_content=learning_hub" rel="noopener noreferrer"&gt;Selenium pytest tutorial &lt;/a&gt;for a quick recap on running pytest with Selenium.&lt;/p&gt;

&lt;p&gt;Run the command &lt;em&gt;pip install pytest&lt;/em&gt; (or &lt;em&gt;pip3 install pytest&lt;/em&gt;) to install the pytest framework on the machine.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.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%2AQRLulTUo51tLwVLB.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.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%2AQRLulTUo51tLwVLB.png" width="800" height="227"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://www.lambdatest.com/blog/selenium-webdriverwait/?utm_source=devto&amp;amp;utm_medium=organic&amp;amp;utm_campaign=nov_22&amp;amp;utm_term=bh&amp;amp;utm_content=blog" rel="noopener noreferrer"&gt;Explicit waits in Selenium&lt;/a&gt; will be leveraged for complex scenarios like waiting for the content to load, navigating through multiple pages, and more. In further sections of this blog on web scraping with Python, I will be using appropriate &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_22&amp;amp;utm_term=bh&amp;amp;utm_content=blog" rel="noopener noreferrer"&gt;locators in Selenium&lt;/a&gt; along with &lt;em&gt;find_element()&lt;/em&gt; / &lt;em&gt;find_elements()&lt;/em&gt; methods to scrape content on the document (or web page).&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Need to know how many characters are in your text? Get an accurate count of the characters in your text with our free online &lt;a href="https://www.lambdatest.com/free-online-tools/character-count?utm_source=devto&amp;amp;utm_medium=organic&amp;amp;utm_campaign=nov_22&amp;amp;utm_term=bh&amp;amp;utm_content=free_online_tools" rel="noopener noreferrer"&gt;character count&lt;/a&gt; tool. Try it now and see how easy it is!&lt;/strong&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2&gt;
  
  
  Beautiful Soup
&lt;/h2&gt;

&lt;p&gt;Beautiful Soup is a popular Python library primarily built for web scraping. As specified in the official documentation, Beautiful Soup can navigate and parse through HTML &amp;amp; XML documents. Unlike Selenium, which can be used for static and dynamic web scraping, Beautiful Soup is apt for static web scraping with Python.&lt;/p&gt;

&lt;p&gt;It supports unit test discovery using pytest. When writing this blog on web scraping with Python, the latest version of Beautiful Soup is 4.12.1. Hence, the library is also referred to as BS4. In case you are using BS3, simply changing the package name from &lt;em&gt;BeautifulSoup&lt;/em&gt; to &lt;em&gt;bs4&lt;/em&gt; will help in porting the code to BS4. For more details, you can refer to the official BS4 porting guide.&lt;/p&gt;

&lt;p&gt;BS4 uses the &lt;em&gt;html.parser&lt;/em&gt; module by default, which can be swapped out with &lt;em&gt;lxml&lt;/em&gt; or &lt;em&gt;html5lib&lt;/em&gt; libraries in Python. Web elements on the HTML page can be located using the CSS Selector and XPath locators. Akin to the &lt;a href="https://www.lambdatest.com/blog/findelement-and-findelements-in-selenium/" rel="noopener noreferrer"&gt;*find_element() *method&lt;/a&gt; in Selenium Python, the &lt;em&gt;find()&lt;/em&gt; method in BS4 returns the element located using the appropriate selector.&lt;/p&gt;

&lt;p&gt;Similarly, the &lt;em&gt;find_all()&lt;/em&gt; method in BS4 (like the &lt;em&gt;find_elements()&lt;/em&gt; method in Selenium) scans through the entire page and returns a list of all the descendants that match the filters. You can refer to the official documentation of Beautiful Soup (or BS4) to get insights into all the methods provided by the library.&lt;/p&gt;

&lt;p&gt;Before using BS4 for &lt;strong&gt;&lt;em&gt;static web scraping&lt;/em&gt;&lt;/strong&gt;, the built-in &lt;em&gt;Requests&lt;/em&gt; library makes HTTP requests. Text (or text) that contains the HTML content from the HTTP response object is then subjected to HTML parsing (or &lt;em&gt;html.parser&lt;/em&gt;) using BS4. Once parsed, the Beautiful Soup object can be used for navigating &amp;amp; searching through the document’s structure.&lt;/p&gt;

&lt;p&gt;BS4 can also be used with Selenium to give wings to &lt;strong&gt;&lt;em&gt;dynamic web scraping&lt;/em&gt;&lt;/strong&gt;. In this case, BS4 is used to parse and extract data from the loaded (or rendered) HTML page, whereas Selenium is used to navigate to the relevant pages.&lt;/p&gt;

&lt;p&gt;Run the command &lt;em&gt;pip3 install beautifulsoup4&lt;/em&gt; or &lt;em&gt;pip3 install bs4&lt;/em&gt; on the terminal to install Beautiful Soup 4 (or BS4). As seen below, the Beautiful Soup 4.12.2 installation was successful.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.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%2Az1cBIC43dOggEDBw.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.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%2Az1cBIC43dOggEDBw.png" width="800" height="111"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;In further sections of the blog, I will demonstrate the usage of Beautiful Soup and Selenium for scraping content on various pages (1 through 5) on the &lt;a href="https://ecommerce-playground.lambdatest.io/index.php?route=product/category&amp;amp;path=57&amp;amp;page=1?utm_source=devto&amp;amp;utm_medium=organic&amp;amp;utm_campaign=nov_22&amp;amp;utm_term=bh&amp;amp;utm_content=webpage" rel="noopener noreferrer"&gt;LambdaTest eCommerce playground&lt;/a&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  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=nov_22&amp;amp;utm_term=bh&amp;amp;utm_content=webpage" rel="noopener noreferrer"&gt;Playwright&lt;/a&gt; is another popular test automation framework suited for web scraping. It is primarily used for &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_22&amp;amp;utm_term=bh&amp;amp;utm_content=learning_hub" rel="noopener noreferrer"&gt;end-to–end testing&lt;/a&gt; of modern web applications. Like Selenium, it also provides APIs that let you perform advanced interactions on the page using headless versions of Chrome, Chromium-based browsers, and WebKit.&lt;/p&gt;

&lt;p&gt;Run the command &lt;em&gt;pip3 install playwright&lt;/em&gt; (or &lt;em&gt;pip install playwright&lt;/em&gt;) to install Playwright for Python on the machine.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.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%2AOTCbK1WbmhPQKxwy.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.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%2AOTCbK1WbmhPQKxwy.png" width="800" height="255"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;When writing this blog on web scraping with Python, the latest version of Playwright is 1.39.0. Auto-waiting in Playwright ensures that waits on elements are performed before they become actionable. This eliminates the need to add artificial timeouts, making the scraping code much more maintainable &amp;amp; readable.&lt;/p&gt;

&lt;p&gt;Over and above, Playwright plugins like Pyppeteer (Python port of Puppeteer) and scrapy-playwright can also be leveraged for static and dynamic web scraping with Python.&lt;/p&gt;

&lt;h2&gt;
  
  
  Scrapy
&lt;/h2&gt;

&lt;p&gt;Scrapy is a full-fledged web crawling and scraping framework that can be used to crawl websites to extract structured data from the page(s). Scrapy can be leveraged for data mining, monitoring, and automated testing.&lt;/p&gt;

&lt;p&gt;When writing this blog, the latest version of Scrapy is 2.11.0. Run the command &lt;em&gt;pip3 install scrapy&lt;/em&gt; (or &lt;em&gt;pip install scrapy&lt;/em&gt;) on the terminal to install Scrapy.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.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%2A5-BgV-YUTzaAGmXT.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.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%2A5-BgV-YUTzaAGmXT.png" width="800" height="237"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.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%2A_Uglz__5dzw7L0lU.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.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%2A_Uglz__5dzw7L0lU.png" width="800" height="190"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.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%2AQj1px4xGSuYQkePv.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.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%2AQj1px4xGSuYQkePv.png" width="800" height="149"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;As stated in the Scrapy documentation, Scrapy can extract data using APIs like Amazon Web Services (AWS). Though Scrapy is mainly used for static web scraping, it can also be used for scraping dynamic web pages (e.g. QuotestoScrape JS).&lt;/p&gt;

&lt;p&gt;Similar to Beautiful Soup, Scrapy along with frameworks like Selenium or Playwright, can be instrumental in scraping dynamic web content.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.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%2ACSjlBkTW95ZpJMiw.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.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%2ACSjlBkTW95ZpJMiw.png" width="800" height="501"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;In such cases, the &lt;em&gt;scrapy-playwright&lt;/em&gt; plugin is handy for scraping dynamic web content using Playwright and Scrapy.&lt;/p&gt;

&lt;p&gt;PyQuery, lxml, and Mechanical Soup are other popular libraries for scraping with Python.&lt;/p&gt;

&lt;p&gt;In the further sections of the blog on web scraping with Python, the discussion &amp;amp; demonstration will be limited to the following libraries (or tools):&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;Web scraping using Selenium PyUnit&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Web scraping using Selenium pytest&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Web scraping using Beautiful Soup&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The learnings from the demonstration will be useful for creating sample code for scraping with other Python-based tools. So, let’s get our hands dirty with some code. 🧑‍💻&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;&lt;a href="https://www.lambdatest.com/free-online-tools/sentence-count?utm_source=devto&amp;amp;utm_medium=organic&amp;amp;utm_campaign=nov_22&amp;amp;utm_term=bh&amp;amp;utm_content=free_online_tools" rel="noopener noreferrer"&gt;Sentence Count&lt;/a&gt; is a free, easy-to-use tool for counting sentences in text. Simply upload your text, click Count and you’ll get the number of sentences in seconds!&lt;/strong&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2&gt;
  
  
  Demonstration: Web Scraping With Python
&lt;/h2&gt;

&lt;p&gt;For demonstrating web scraping using Selenium PyUnit (&amp;amp; pytest) and Beautiful Soup, I will be using the following websites:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.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%2A89rHuqEnOuYqldEmPj5UNg.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.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%2A89rHuqEnOuYqldEmPj5UNg.png" width="800" height="400"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Directory Structure
&lt;/h2&gt;

&lt;p&gt;As seen in the project structure, scraping using PyUnit, pytest, and Beautiful Soup is driven via a &lt;em&gt;Makefile&lt;/em&gt;. Let’s look at the important directories and files in more detail below:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.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%2AjBtAb4hR_-al5PIa.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.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%2AjBtAb4hR_-al5PIa.png" width="800" height="400"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Here is a closer look at the overall structure:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;pageobject&lt;/strong&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;a href="https://www.lambdatest.com/blog/page-object-model-in-selenium-python/?utm_source=devto&amp;amp;utm_medium=organic&amp;amp;utm_campaign=nov_22&amp;amp;utm_term=bh&amp;amp;utm_content=blog" rel="noopener noreferrer"&gt;Page Object Model&lt;/a&gt; (POM) design pattern is used for separate web locators from the core test logic. &lt;em&gt;locators.py&lt;/em&gt; contains details of locators used for interacting with the test websites mentioned earlier.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.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%2A1FU3Ya8laU6TBzTS.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.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%2A1FU3Ya8laU6TBzTS.png" width="800" height="487"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;em&gt;helpers.py&lt;/em&gt; contains the implementation of the wrapper functions for triggering actions, performing &lt;a href="https://www.lambdatest.com/blog/selenium-wait-for-page-to-load/?utm_source=devto&amp;amp;utm_medium=organic&amp;amp;utm_campaign=nov_22&amp;amp;utm_term=bh&amp;amp;utm_content=blog" rel="noopener noreferrer"&gt;explicit waits&lt;/a&gt;, and more. Since the use cases for scraping with PyUnit and pytest are the same, two wrapper functions are created for reduced code duplication and improved maintenance:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.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%2AbQVB6VuuuB9dk1JiRem0RA.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.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%2AbQVB6VuuuB9dk1JiRem0RA.png" width="800" height="400"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Both the functions return a list where the scraped information is stored for further operations.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.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%2AcLb8YDzc1OciOiaU.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.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%2AcLb8YDzc1OciOiaU.png" width="800" height="785"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;tests&lt;/strong&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;As the name suggests, the tests folder contains the implementation of the core test methods that are responsible for scraping the content using the respective scraping library (or tool).&lt;/p&gt;

&lt;p&gt;To keep the code more modular and maintainable, we have created subfolders (or sub-directories) for the libraries used for scraping the test websites:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.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%2AonXisAIC3_dYgnYmQCggYQ.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.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%2AonXisAIC3_dYgnYmQCggYQ.png" width="800" height="541"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;We will cover more about the implementation in the code walkthrough section of the blog.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Configuration (or setup) files&lt;/strong&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The conftest.py file in pytest helps to fixture and share fixtures, hooks, and other configuration options used in the test code. For demonstration, scraping with Selenium and/or Beautiful Soup is performed using browsers on the local machine and cloud grid like LambdaTest.&lt;br&gt;
LambdaTest is an AI-powered test orchestration and execution platform that lets you perform automated testing using Selenium Python 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_22&amp;amp;utm_term=bh&amp;amp;utm_content=webpage" rel="noopener noreferrer"&gt;online browser farm&lt;/a&gt; of 3000+ real browsers and operating systems.&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 tutorials around &lt;a href="https://www.lambdatest.com/selenium?utm_source=devto&amp;amp;utm_medium=organic&amp;amp;utm_campaign=nov_22&amp;amp;utm_term=bh&amp;amp;utm_content=webpage" rel="noopener noreferrer"&gt;Selenium automation 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_22&amp;amp;utm_term=bh&amp;amp;utm_content=webpage" rel="noopener noreferrer"&gt;Appium automation&lt;/a&gt;, and more. Subscribe to the &lt;a href="https://www.youtube.com/c/LambdaTest?sub_confirmation=1?utm_source=devto&amp;amp;utm_medium=organic&amp;amp;utm_campaign=nov_22&amp;amp;utm_term=bh&amp;amp;utm_content=youtube" rel="noopener noreferrer"&gt;LambdaTest YouTube Channel&lt;/a&gt;.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Want to decode a messy URL? Unleash the full potential of your website with our online &lt;a href="https://www.lambdatest.com/free-online-tools/url-parse?utm_source=devto&amp;amp;utm_medium=organic&amp;amp;utm_campaign=nov_22&amp;amp;utm_term=bh&amp;amp;utm_content=free_online_tools" rel="noopener noreferrer"&gt;URL Parse&lt;/a&gt; tool. Optimize your website’s URLs to improve your site’s visibility.&lt;/strong&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;&lt;a href="https://media2.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%2ATNvA-F4TKBnL5Qcu.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.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%2ATNvA-F4TKBnL5Qcu.png" width="800" height="400"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The scope of the &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_22&amp;amp;utm_term=bh&amp;amp;utm_content=blog" rel="noopener noreferrer"&gt;pytest fixture&lt;/a&gt; (i.e., &lt;em&gt;@pytest.fixture&lt;/em&gt;) is set to &lt;em&gt;function&lt;/em&gt;, which means that the fixture is set up and torn down before &amp;amp; after executing a test function (or method). As seen below, execution is controlled using the environment variable &lt;em&gt;EXEC_PLATFORM&lt;/em&gt;, which can be set to &lt;em&gt;local *or *cloud&lt;/em&gt;.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.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%2AZvXoEPhAhF3ovXMs.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.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%2AZvXoEPhAhF3ovXMs.png" width="800" height="590"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Username (&lt;em&gt;LT_USERNAME&lt;/em&gt;) and Access Key (&lt;em&gt;LT_ACCESS_KEY&lt;/em&gt;) for using the &lt;a href="https://www.lambdatest.com/selenium-grid-online?utm_source=devto&amp;amp;utm_medium=organic&amp;amp;utm_campaign=nov_22&amp;amp;utm_term=bh&amp;amp;utm_content=webpage" rel="noopener noreferrer"&gt;cloud Selenium Grid &lt;/a&gt;on LambdaTest can be obtained by navigating to &lt;a href="https://accounts.lambdatest.com/security?utm_source=devto&amp;amp;utm_medium=organic&amp;amp;utm_campaign=nov_22&amp;amp;utm_term=bh&amp;amp;utm_content=webpage" rel="noopener noreferrer"&gt;LambdaTest Profile Page&lt;/a&gt;. Set them using the &lt;em&gt;export&lt;/em&gt; (for macOS and Linux) or &lt;em&gt;set&lt;/em&gt; (for Windows) command to export the environment variables.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.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%2AtyoQjIBsacp5iGoS.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.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%2AtyoQjIBsacp5iGoS.png" width="800" height="400"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;In the &lt;em&gt;teardown&lt;/em&gt; function, the &lt;em&gt;quit()&lt;/em&gt; method is invoked to terminate the browser session. The &lt;a href="https://www.lambdatest.com/blog/how-to-use-javascriptexecutor-in-selenium-webdriver/?utm_source=devto&amp;amp;utm_medium=organic&amp;amp;utm_campaign=nov_22&amp;amp;utm_term=bh&amp;amp;utm_content=blog" rel="noopener noreferrer"&gt;JavaScriptExecutor in Selenium&lt;/a&gt; [i.e., &lt;em&gt;execute_script()&lt;/em&gt;] is used to update the &lt;em&gt;lambda-status&lt;/em&gt; variable, which indicates the test execution status on LambdaTest Selenium Grid.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.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%2AArm17vzlxUrY5vIT.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.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%2AArm17vzlxUrY5vIT.png" width="800" height="376"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Similarly, we have created puynitsetup.py that contains configurations related to the PyUnit (or unittest ) framework. Implementation under the &lt;em&gt;&lt;strong&gt;init&lt;/strong&gt;()&lt;/em&gt; method is used for initializing the test cases.&lt;/p&gt;

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

&lt;p&gt;Irrespective of whether the tests are executed on a local grid or cloud Selenium grid, the browser is initialized in headless mode. If the environment variable &lt;em&gt;EXEC_PLATFORM&lt;/em&gt; is set to the &lt;em&gt;cloud&lt;/em&gt;, Chrome browser on the LambdaTest grid is instantiated in headless mode.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.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%2ABz7VVtWa5tqEYMh_.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.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%2ABz7VVtWa5tqEYMh_.png" width="734" height="376"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The said capability (i.e., &lt;em&gt;headless&lt;/em&gt;), along with the other capabilities, can be set using the &lt;a href="https://www.lambdatest.com/capabilities-generator/?utm_source=devto&amp;amp;utm_medium=organic&amp;amp;utm_campaign=nov_22&amp;amp;utm_term=bh&amp;amp;utm_content=webpage" rel="noopener noreferrer"&gt;LambdaTest Capabilities Generator&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.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%2AAcK5SQq2Tjfx9BFv.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.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%2AAcK5SQq2Tjfx9BFv.png" width="800" height="468"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;As stated in the official Selenium documentation, Chrome is instantiated in the headless mode by adding &lt;em&gt;–headless=new&lt;/em&gt; to Chrome options.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.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%2ApCV8VB4AHk4njrtF.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.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%2ApCV8VB4AHk4njrtF.png" width="800" height="246"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Lastly, the &lt;em&gt;setUp()&lt;/em&gt; and &lt;em&gt;tearDown()&lt;/em&gt; methods are implemented to set up and tear down the test environment. In fact, &lt;em&gt;&lt;strong&gt;init&lt;/strong&gt;()&lt;/em&gt; is optional, and implementation under it can be moved under the &lt;em&gt;setUp()&lt;/em&gt; method.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.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%2AN_aC094ee1lvI4cW.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.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%2AN_aC094ee1lvI4cW.png" width="800" height="550"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Makefile&lt;/strong&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;As stated earlier, scraping using the respective libraries (or tools) is controlled via a Makefile. Typing &lt;em&gt;make help&lt;/em&gt; provide all the options available for execution.&lt;/p&gt;

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

&lt;p&gt;The &lt;em&gt;make install&lt;/em&gt; command will install the packages/libraries specified in requirements.txt.&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;pytest-selenium
selenium&amp;gt;=4.6.0
urllib3==1.26.12
requests
py
bs4
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;&lt;a href="https://media2.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%2AttUO-UmFeUWbYTqlhvhk4g.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.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%2AttUO-UmFeUWbYTqlhvhk4g.png" width="800" height="189"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Pre Requisites
&lt;/h2&gt;

&lt;p&gt;First and foremost, trigger &lt;em&gt;virtualenv venv&lt;/em&gt; and &lt;em&gt;source venv/bin/activate&lt;/em&gt; on the terminal to create the virtual environment.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.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%2ASfMCUVTsZGZx1ZFc.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.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%2ASfMCUVTsZGZx1ZFc.png" width="800" height="400"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Since there is a provision to perform scraping on the cloud Selenium grid, it is recommended to have an account on LambdaTest. To scrap websites using Selenium on LambdaTest Grid, you must update LT_USERNAME and LT_ACCESS_KEY (in Makefile) from the &lt;a href="https://accounts.lambdatest.com/security?utm_source=devto&amp;amp;utm_medium=organic&amp;amp;utm_campaign=nov_22&amp;amp;utm_term=bh&amp;amp;utm_content=webpage" rel="noopener noreferrer"&gt;LambdaTest Profile Page&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.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%2A7nn0Yhv5g2yLuU1p.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.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%2A7nn0Yhv5g2yLuU1p.png" width="800" height="400"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Install the required frameworks and libraries (mentioned in &lt;em&gt;requirements.txt&lt;/em&gt;) by triggering the &lt;em&gt;make install&lt;/em&gt; command on the terminal.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.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%2AAP8L_kZrpdi8eLHS.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.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%2AAP8L_kZrpdi8eLHS.png" width="800" height="400"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;With this, the stage is finally set to scrap the test websites. Let’s dive in!&lt;/p&gt;

&lt;p&gt;Since the scraping logic with Selenium &amp;amp; Python remains the same for PyUnit and pytest, we have combined both frameworks in this section. To begin with, we look into the configuration aspects of the framework, after which we would look into the common scraping logic.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Need to &lt;a href="https://www.lambdatest.com/free-online-tools/extract-text-from-html?utm_source=devto&amp;amp;utm_medium=organic&amp;amp;utm_campaign=nov_22&amp;amp;utm_term=bh&amp;amp;utm_content=free_online_tools" rel="noopener noreferrer"&gt;extract text from HTML&lt;/a&gt; code? Our free online tool makes it easy and fast. Save time by extracting only the text you need from HTML code in no time.&lt;/strong&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2&gt;
  
  
  Web Scraping using Selenium and PyUnit
&lt;/h2&gt;

&lt;p&gt;For simplification, all setup-related code (i.e., instantiating the browser, setting explicit timeouts, etc.) is in &lt;em&gt;pyunitsetup.py&lt;/em&gt;. Essential aspects of the setup are already discussed in the earlier section of this blog on web scraping with Python.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.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%2AXWEjNljlMh-1m_4w.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.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%2AXWEjNljlMh-1m_4w.png" width="618" height="714"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Let’s deep dive into some of the integral parts of the configuration file pyunitsetup.py&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;import sys
import os
from os import environ
sys.path.append(sys.path[0] + "/../..")
from pageobject.locators import locators
from pageobject.locators import *

exec_platform = os.getenv('EXEC_PLATFORM')

class pyunit_setup:    
    def __init__(self):
        if exec_platform == 'cloud':
            username = environ.get('LT_USERNAME', None)
            access_key = environ.get('LT_ACCESS_KEY', None)

            ch_options = webdriver.ChromeOptions()

            lt_options = {}
            lt_options["build"] = "Build: Web Scraping with Selenium &amp;amp; Pyunit"
            lt_options["project"] = "Project: Web Scraping with Selenium &amp;amp; Pyunit"
            lt_options["name"] = "Test: Web Scraping with Selenium &amp;amp; Pyunit"

            lt_options["browserName"] = "Chrome"
            lt_options["browserVersion"] = "latest"
            lt_options["platformName"] = "Windows 11"

            lt_options["console"] = "error"
            lt_options["w3c"] = True
            lt_options["headless"] = True

            ch_options.set_capability('LT:Options', lt_options)

            gridURL = "@hub.lambdatest.com/wd/hub"&amp;gt;https://{}:{}@hub.lambdatest.com/wd/hub".format(username, access_key)
            self.browser = webdriver.Remote(
                command_executor = gridURL,
                options = ch_options
            )
        elif exec_platform == 'local':
            ch_options = ChromeOptions()
            ch_options.add_argument("--headless=new")
            self.browser = webdriver.Chrome(options=ch_options)

    def setUp(self):
        self.browser.implicitly_wait(10)
        self.browser.maximize_window()

    def tearDown(self):
        if (self.browser != None):
            self.browser.quit()
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;The value of the &lt;em&gt;EXEC_PLATFORM&lt;/em&gt; environment variable (i.e., &lt;em&gt;cloud&lt;/em&gt; or &lt;em&gt;local&lt;/em&gt;) decides whether the instantiation of the browser is on the local machine or cloud grid on LambdaTest.&lt;/p&gt;

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

&lt;p&gt;&lt;a href="https://www.lambdatest.com/blog/selenium-remotewebdriver/?utm_source=devto&amp;amp;utm_medium=organic&amp;amp;utm_campaign=nov_22&amp;amp;utm_term=bh&amp;amp;utm_content=blog" rel="noopener noreferrer"&gt;Remote WebDriver&lt;/a&gt; with grid URL &amp;amp; browser options as parameters are instantiated using the *webdriver.Remote() *method. Capabilities (or browser options) can be obtained from &lt;a href="https://www.lambdatest.com/capabilities-generator/?utm_source=devto&amp;amp;utm_medium=organic&amp;amp;utm_campaign=nov_22&amp;amp;utm_term=bh&amp;amp;utm_content=webpage" rel="noopener noreferrer"&gt;LambdaTest Capabilities Generator&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.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%2Awd2swgcaojaVN2JB.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.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%2Awd2swgcaojaVN2JB.png" width="800" height="481"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Chrome in headless mode is instantiated as we do not require GUI for web scraping with Python. The &lt;em&gt;w3c&lt;/em&gt; flag is set to true since we are using Selenium 4 (which is W3C compliant) for the tests.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.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%2ASvUDJwzFZtS3tbqx.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.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%2ASvUDJwzFZtS3tbqx.png" width="734" height="376"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;When using a local grid (or machine), we set the &lt;em&gt;–headless=new&lt;/em&gt; argument for Chrome Options.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.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%2AsWFj3rTye8Wvuju1.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.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%2AsWFj3rTye8Wvuju1.png" width="800" height="310"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Finally, the *setUp() *and *tearDown() *methods contain the implementation of maximizing the browser and releasing the resources post text execution respectively.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.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%2A5Wzw_x2dmGBe5Qph.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.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%2A5Wzw_x2dmGBe5Qph.png" width="800" height="550"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Perform browser &lt;a href="https://www.lambdatest.com/automation-testing?utm_source=devto&amp;amp;utm_medium=organic&amp;amp;utm_campaign=nov_22&amp;amp;utm_term=bh&amp;amp;utm_content=free_online_tools" rel="noopener noreferrer"&gt;test automation&lt;/a&gt; on the most powerful cloud infrastructure. Leverage LambdaTest automation testing for faster, reliable and scalable experience on cloud.&lt;/strong&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2&gt;
  
  
  Web Scraping using Selenium and pytest
&lt;/h2&gt;

&lt;p&gt;Since the demo websites scraped with PyUnit and pytest are the same, there are changes only in the setup-related code. Like web scraping with PyUnit, all setup-related code (i.e., instantiating the browser, setting explicit timeouts, etc.) is segregated in conftest.py.&lt;/p&gt;

&lt;p&gt;For starters, &lt;em&gt;conftest.py&lt;/em&gt; is a special file in pytest that contains the configuration of the test suite. Hence, hooks, fixtures, and other configurations are all a part of &lt;em&gt;conftest.py&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.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%2AzF-BQqsxi8COe3UA.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.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%2AzF-BQqsxi8COe3UA.png" width="800" height="400"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Let’s deep dive into some of the integral parts of the configuration file conftest.py&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;# Import the locators file
import sys
import os
from os import environ
sys.path.append(sys.path[0] + "/../..")
from pageobject.locators import locators
from pageobject.locators import *

exec_platform = os.getenv('EXEC_PLATFORM')

@pytest.fixture(scope='function')
def driver(request):
    if exec_platform == 'cloud':
        username = environ.get('LT_USERNAME', None)
        access_key = environ.get('LT_ACCESS_KEY', None)

        gridURL = "@hub.lambdatest.com/wd/hub"&amp;gt;https://{}:{}@hub.lambdatest.com/wd/hub".format(username, access_key)

        ch_options = webdriver.FirefoxOptions()
        ch_options.browser_version = "latest"
        ch_options.platform_name = "Windows 11"

        lt_options = {}
        lt_options["build"] = "Build: Web Scraping with Selenium &amp;amp; Pytest"
        lt_options["project"] = "Project: Web Scraping with Selenium &amp;amp; Pytest"
        lt_options["name"] = "Test: Web Scraping with Selenium &amp;amp; Pytest"

        lt_options["browserName"] = "Firefox"
        lt_options["browserVersion"] = "latest"
        lt_options["platformName"] = "Windows 11"

        lt_options["console"] = "error"
        lt_options["w3c"] = True
        lt_options["headless"] = True

        ch_options.set_capability('LT:Options', lt_options)

        browser = webdriver.Remote(
            command_executor = gridURL,
            options = ch_options
        )

        yield browser

        def fin():
            if request.node.rep_call.failed:
                browser.execute_script("lambda-status=failed")
            else:
                browser.execute_script("lambda-status=passed")
                browser.quit()

        request.addfinalizer(fin)
    elif exec_platform == 'local':
        options = ChromeOptions()
        options.add_argument("--headless=new")
        browser = webdriver.Chrome(options=options)

        yield browser

        def fin():
            browser.quit()

@pytest.hookimpl(tryfirst=True, hookwrapper=True)
def pytest_runtest_makereport(item, call):
    outcome = yield
    rep = outcome.get_result()

    setattr(item, "rep_" + rep.when, rep)
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;The &lt;em&gt;@pytest.fixture(scope=’function’)&lt;/em&gt; decorator is used for defining a fixture with a &lt;em&gt;function&lt;/em&gt; scope.&lt;/p&gt;

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

&lt;p&gt;Depending on the value of the &lt;em&gt;EXEC_PLATFORM&lt;/em&gt; environment variable (i.e. &lt;em&gt;cloud&lt;/em&gt; or &lt;em&gt;local&lt;/em&gt;), the browser is instantiated on the local machine or cloud grid on LambdaTest.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.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%2AhD-0wga4DMLSktWp.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.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%2AhD-0wga4DMLSktWp.png" width="800" height="400"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;In case you are planning to run the &lt;a href="https://www.lambdatest.com/selenium-python-testing?utm_source=devto&amp;amp;utm_medium=organic&amp;amp;utm_campaign=nov_22&amp;amp;utm_term=bh&amp;amp;utm_content=webpage" rel="noopener noreferrer"&gt;Selenium Python tests&lt;/a&gt; (i.e., scrap website under test) on browsers in LambdaTest cloud, instantiate Remote WebDriver with grid URL &amp;amp; browser options as parameters to the &lt;em&gt;webdriver.Remote()&lt;/em&gt; method. Capabilities (or browser options) can be obtained from LambdaTest Capabilities Generator.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.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%2AqEvFgJRn2qndYIS4.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.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%2AqEvFgJRn2qndYIS4.png" width="800" height="400"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Since we are performing web scraping with Python, the browser (i.e., Chrome) is instantiated in the headless mode. &lt;a href="https://www.lambdatest.com/blog/headless-chrome/?utm_source=devto&amp;amp;utm_medium=organic&amp;amp;utm_campaign=nov_22&amp;amp;utm_term=bh&amp;amp;utm_content=blog" rel="noopener noreferrer"&gt;Headless Chrome&lt;/a&gt; is faster than the real browser (with the GUI). Hence, it is best suited for web scraping with Python. Since Selenium 4 (W3C compliant) is used for testing, the &lt;em&gt;w3c *flag is set to *true&lt;/em&gt;.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.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%2AXg5zKoTDorklbBlB.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.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%2AXg5zKoTDorklbBlB.png" width="734" height="376"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;For scraping using Selenium on local headless browsers (e.g., Chrome) on a local grid (or machine), simply set the &lt;em&gt;–headless=new&lt;/em&gt; argument for Chrome Options.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.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%2ABq-4_xsXnlhKqgIE.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.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%2ABq-4_xsXnlhKqgIE.png" width="800" height="310"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Lastly, the &lt;em&gt;yield&lt;/em&gt; statement provides the resource (i.e., &lt;em&gt;browser&lt;/em&gt;) in the &lt;em&gt;setUp&lt;/em&gt; and &lt;em&gt;tearDown&lt;/em&gt; sections of the code. Hence, all the resources the browser uses will be cleared once the execution is complete.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.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%2A_EIZRX7WO9-757sd.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.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%2A_EIZRX7WO9-757sd.png" width="800" height="594"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;With this, we are all set to dive deep into the scraping implementation!&lt;/p&gt;

&lt;h3&gt;
  
  
  Test Scenario 1 — Scraping LambdaTest YouTube Channel
&lt;/h3&gt;

&lt;p&gt;In this example, meta-data (i.e., title, views, duration, etc.) associated with &lt;a href="https://www.youtube.com/@LambdaTest/videos?utm_source=devto&amp;amp;utm_medium=organic&amp;amp;utm_campaign=nov_22&amp;amp;utm_term=bh&amp;amp;utm_content=youtube" rel="noopener noreferrer"&gt;LambdaTest YouTube Videos&lt;/a&gt; is scraped using the PyUnit (or unittest framework).&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.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%2AxW79DZZkDzoSSN66.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.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%2AxW79DZZkDzoSSN66.png" width="800" height="400"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Since the LambdaTest YouTube channel has more than 600+ videos, a vertical scroll (till the end of the page) is performed so that required information can be scraped from those videos!&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Need a great solution for Safari browser testing on Windows? Forget about emulators or simulators — use real online browsers for testing on &lt;a href="https://www.lambdatest.com/safari-browser-for-windows?utm_source=devto&amp;amp;utm_medium=organic&amp;amp;utm_campaign=nov_22&amp;amp;utm_term=bh&amp;amp;utm_content=webpage" rel="noopener noreferrer"&gt;safari for windows&lt;/a&gt;. Try LambdaTest for free!&lt;/strong&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;&lt;strong&gt;Implementation (pyUnit)&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;The file test_yt_scraping.py contains the test logic where the &lt;em&gt;scrap_youtube()&lt;/em&gt; method scraps the channel. It returns a list that contains the metadata of the videos.&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;# Import the locators file
import sys
sys.path.append(sys.path[0] + "/../..")
from pageobject.locators import locators
from pageobject.locators import *

from pageobject.helpers import helpers
from pageobject.helpers import *

from pyunitsetup import pyunit_setup

test_setup = pyunit_setup()

def scrap_youtube(test_url) -&amp;gt; list:
    meta_data_arr=[]
    test_setup.setUp()
    test_setup.browser.get(test_url)

    # Click on 'Accept All' in case the said window comes up
    # This occurs since cookies are cleared and machines are sanitized each run

    # Stack Overflow - https://stackoverflow.com/questions/66902404/selenium-python-click-agree-to-youtube-cookie
    # Locators were located using the below link
    # https://consent.youtube.com/m?continue=https%3A%2F%2Fwww.youtube.com%2F&amp;amp;gl=FR&amp;amp;m=0&amp;amp;pc=yt&amp;amp;uxe=23983172&amp;amp;hl=en&amp;amp;src=1

    # Below issue is not observed in local machines since there is no clean-up of cookies

    try:
        # Wait until the "Accept all" button is clickable
        accept_all = WebDriverWait(test_setup.browser, 5).until(EC.element_to_be_clickable(
            (By.CSS_SELECTOR, "form:nth-child(3) &amp;gt; div &amp;gt; div &amp;gt; button[aria-label= 'Accept all']")))

        # Click the "Accept all" button
        accept_all.click()

        print("Click on Accept all successful")
    except Exception as e:
        # Even if this exception is raised, we can still continue test execution
        # This means that the button was not found &amp;amp; the YouTube channel link has opened successfully
        print(f"'Accept All' button not present, proceed with the tests: {str(e)}")

    meta_data_arr = helpers.scrap_yt_content(test_setup.browser)

    test_setup.tearDown()
    return meta_data_arr

def main():
    meta_data_arr = scrap_youtube(locators.test_yt_url)
    helpers.print_scrapped_content(meta_data_arr)

if __name__ == '__main__':
    main()
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;To get started, we create an object of the &lt;em&gt;pyunit_setup&lt;/em&gt; class which contains implementation of &lt;em&gt;setUp()&lt;/em&gt; and &lt;em&gt;tearDown()&lt;/em&gt; methods.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.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-TCTig2-xDNIrWCQ.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.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-TCTig2-xDNIrWCQ.png" width="800" height="392"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The browser instantiation is done in the &lt;em&gt;&lt;strong&gt;init&lt;/strong&gt;()&lt;/em&gt; method implemented in &lt;em&gt;pyunitsetup.py&lt;/em&gt;. An empty list named *meta_data_arr *is created, and the browser is navigated to the URL under test.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.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%2Abr8tX7NgdTkzmaRY.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.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%2Abr8tX7NgdTkzmaRY.png" width="800" height="431"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Scraping YouTube content is legal as long as we comply with regulations that deal with personal data and copyright protection. After repeated scraping, we observed that YouTube showed the Consent Page, where we had to programmatically click the Accept all button to navigate to the LambdaTest YouTube channel.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.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%2Aatth7nsRv4o7mmD0.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.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%2Aatth7nsRv4o7mmD0.png" width="800" height="741"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Since the above consent page was shown intermittently, we used the &lt;em&gt;try…catch *loop to counter the above situation. We first locate the Accept all button using the &lt;a href="https://www.lambdatest.com/learning-hub/css-selectors?utm_source=devto&amp;amp;utm_medium=organic&amp;amp;utm_campaign=nov_22&amp;amp;utm_term=bh&amp;amp;utm_content=learning_hub" rel="noopener noreferrer"&gt;CSS Selector&lt;/a&gt; *form:nth-child(3) &amp;gt; div &amp;gt; div &amp;gt; button[aria-label= ‘Accept all’] *Alternatively, we could have also located the element using the &lt;a href="https://www.lambdatest.com/blog/python-xpath/?utm_source=devto&amp;amp;utm_medium=organic&amp;amp;utm_campaign=nov_22&amp;amp;utm_term=bh&amp;amp;utm_content=blog" rel="noopener noreferrer"&gt;XPath locator&lt;/a&gt; *//div[&lt;a class="mentioned-user" href="https://dev.to/class"&gt;@class&lt;/a&gt;=’csJmFc’]/form[2]//div[&lt;a class="mentioned-user" href="https://dev.to/class"&gt;@class&lt;/a&gt;=’VfPpkd-RLmnJb’]&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.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%2A69apY2_MSoDAn28r.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.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%2A69apY2_MSoDAn28r.png" width="800" height="290"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;To locate elements with ease, we used the &lt;em&gt;POM Builder *that provides CSS Selector &amp;amp; XPath of elements at the click of a button. The &lt;a href="https://www.lambdatest.com/blog/webdriverwait-in-selenium-c-sharp/?utm_source=devto&amp;amp;utm_medium=organic&amp;amp;utm_campaign=nov_22&amp;amp;utm_term=bh&amp;amp;utm_content=blog" rel="noopener noreferrer"&gt;*WebDriverWait class&lt;/a&gt;&lt;/em&gt; is used along with the *element_to_be_clickable &lt;a href="https://www.lambdatest.com/blog/expected-conditions-in-selenium-examples/?utm_source=devto&amp;amp;utm_medium=organic&amp;amp;utm_campaign=nov_22&amp;amp;utm_term=bh&amp;amp;utm_content=blog" rel="noopener noreferrer"&gt;*expected condition in Selenium&lt;/a&gt; to realize an explicit wait for 5 seconds.&lt;/p&gt;

&lt;p&gt;Once the element is located, the &lt;a href="https://www.lambdatest.com/blog/selenium-click-button-with-examples/?utm_source=devto&amp;amp;utm_medium=organic&amp;amp;utm_campaign=nov_22&amp;amp;utm_term=bh&amp;amp;utm_content=blog" rel="noopener noreferrer"&gt;*click *method in Selenium&lt;/a&gt; is invoked to perform a click operation on the Click all button.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.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%2AJDQTXM5U1MQJCK_b.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.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%2AJDQTXM5U1MQJCK_b.png" width="800" height="400"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.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%2AFL68NvvD-k808jmW.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.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%2AFL68NvvD-k808jmW.png" width="800" height="505"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;In case the &lt;em&gt;YouTube Consent Page&lt;/em&gt; does not show up, we simply handle the exception in the &lt;em&gt;expect&lt;/em&gt; block and print the exception.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.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%2AtSarDDWME8l08S33.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.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%2AtSarDDWME8l08S33.png" width="800" height="400"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Now that we are on the LambdaTest YouTube Channel, we invoke the &lt;a href="https://github.com/hjsblogger/web-scraping-with-python/blob/main/pageobject/helpers.py#L79" rel="noopener noreferrer"&gt;*scrap_yt_content()&lt;/a&gt;* method that is a part of the &lt;a href="https://github.com/hjsblogger/web-scraping-with-python/blob/main/pageobject/helpers.py#L79" rel="noopener noreferrer"&gt;*helpers&lt;/a&gt; *class. We will look into its implementation in the further sections of the blog.&lt;/p&gt;

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

&lt;p&gt;Upon successful scraping, a list &lt;em&gt;meta_data_arr&lt;/em&gt; containing the scraped content is returned, and the content is printed on the terminal using the &lt;a href="https://github.com/hjsblogger/web-scraping-with-python/blob/main/pageobject/helpers.py#L171" rel="noopener noreferrer"&gt;*print_scrapped_content()&lt;/a&gt;* method.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Implementation (Pytest)&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Like PyUnit, the core logic for scraping the YouTube channel is implemented in the &lt;em&gt;scrap_youtube()&lt;/em&gt; method in &lt;a href="https://github.com/hjsblogger/web-scraping-with-python/blob/main/tests/pytest/test_yt_scraping.py" rel="noopener noreferrer"&gt;test_yt_scraping.py&lt;/a&gt;. It returns a list that contains the metadata of the videos.&lt;/p&gt;

&lt;p&gt;Since the majority of the implementation remains the same, we would be focussing only on the changes that are specific to the pytest framework.&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;# Import the locators file
import sys
sys.path.append(sys.path[0] + "/..")
from pageobject.locators import locators
from pageobject.locators import *

from pageobject.helpers import helpers
from pageobject.helpers import *

@pytest.mark.usefixtures('driver')

class TestYoutubeScraping:
    def test_scrap_youtube(self, driver) -&amp;gt; list:
        meta_data_arr=[]
        driver.get(locators.test_yt_url)

        try:
            # Wait until the "Accept all" button is clickable
            accept_all = WebDriverWait(driver, 5).until(EC.element_to_be_clickable(
                (By.CSS_SELECTOR, "form:nth-child(3) &amp;gt; div &amp;gt; div &amp;gt; button[aria-label= 'Accept all']")))

            # Click the "Accept all" button
            accept_all.click()

            print("Click on Accept all successful")
        except Exception as e:
            print(f"'Accept All' button not present, proceed with the tests: {str(e)}")

        meta_data_arr = helpers.scrap_yt_content(driver)
        helpers.print_scrapped_content(meta_data_arr)
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;The &lt;em&gt;@pytest.mark.usefixtures(‘driver’)&lt;/em&gt; decorator indicates that the &lt;em&gt;driver&lt;/em&gt; (i.e., instantiated Chrome browser) fixture must be used in the execution of the test methods.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.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%2ASpihqUCzYVnH84WW.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.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%2ASpihqUCzYVnH84WW.png" width="800" height="619"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;As mentioned in the earlier section, the click to Accept all button is initiated in case the YouTube consent form is displayed on the screen.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.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%2Adm0knTOWzDQwVZgp.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.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%2Adm0knTOWzDQwVZgp.png" width="800" height="741"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Once you are on the LambdaTest YouTube channel, the &lt;a href="https://github.com/hjsblogger/web-scraping-with-python/blob/main/pageobject/helpers.py#L79" rel="noopener noreferrer"&gt;*scrap_yt_content()&lt;/a&gt;* method is invoked to scrap the channel content. Lastly, the scraped content is printed on the screen.&lt;/p&gt;

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

&lt;p&gt;Since the rest of the implementation is the same as in the PyUnit framework, hence we are not touching upon those aspects in this section.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Run your &lt;a href="https://www.lambdatest.com/selenium-automation?utm_source=devto&amp;amp;utm_medium=organic&amp;amp;utm_campaign=nov_22&amp;amp;utm_term=bh&amp;amp;utm_content=webpage" rel="noopener noreferrer"&gt;Selenium Automation&lt;/a&gt; Testing scripts on the LambdaTest cloud grid. Test on 3000+ desktop &amp;amp; mobile environments. Try it for free&lt;/strong&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;&lt;strong&gt;Implementation (Core Scraping Logic)&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;The &lt;a href="https://github.com/hjsblogger/web-scraping-with-python/blob/main/pageobject/helpers.py" rel="noopener noreferrer"&gt;helpers.py&lt;/a&gt; file contains the core implementation for scraping the LambdaTest YouTube channel.&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;# Import the locators file
import sys
from pageobject.locators import locators
from pageobject.locators import *

def create_actions(driver):
    actions = ActionChains(driver)
    return actions

def create_waits(driver, duration):
    # Explicit wait of 10 seconds
    wait = WebDriverWait(driver, duration)
    return wait

class helpers(object):    
    def scrap_yt_content(driver)-&amp;gt;list:
        meta_data_arr=[]
        # Explicit wait of 10 seconds
        wait = create_waits(driver, 10)

        actions = create_actions(driver)

        # Explicit wait of 10 seconds
        wait = WebDriverWait(driver, 10)

        # Wait for 10 seconds till the Document State is not complete
        wait.until(lambda driver: driver.execute_script('return document.readyState') == 'complete')

        # Once the page has loaded, scroll to the end of the page to load all the videos
        # Scroll to the end of the page to load all the videos in the channel
        # Reference - https://stackoverflow.com/a/51702698/126105
        # Get scroll height
        start_height = driver.execute_script("return document.documentElement.scrollHeight")

        # Repeat scrolling until reaching the end of the page
        # Taking cues from my own blog https://www.lambdatest.com/blog/scraping-dynamic-web-pages/
        while True:
            # Scroll to the bottom of the page
            driver.execute_script("window.scrollTo(0, " + str(start_height) + ")")
            # Wait for the content to load
            time.sleep(2)
            scroll_height = driver.execute_script("return document.documentElement.scrollHeight")
            if (scroll_height == start_height):
                # If heights are the same, we reached the end of page
                break
            # print("scroll_height = " + str(scroll_height))
            time.sleep(2)
            start_height = scroll_height

        time.sleep(2)

        elem_1 = driver.find_elements(By.CSS_SELECTOR,
            "#dismissible &amp;gt; #details")

        for video_metadata_1 in elem_1:
            elem_2 = video_metadata_1.find_element(By.CSS_SELECTOR,
                "#meta")

            elem_3 = elem_2.find_element(By.CSS_SELECTOR,
                "#video-title")

            elem_4 = elem_2.find_element(By.CSS_SELECTOR,
                "#metadata &amp;gt; #metadata-line &amp;gt; span:nth-child(3)")

            elem_5 = elem_2.find_element(By.CSS_SELECTOR,
                "#metadata &amp;gt; #metadata-line &amp;gt; span:nth-child(4)")

            video_title = elem_3.get_attribute('innerText')
            video_views = elem_4.get_attribute('innerText')
            video_time = elem_5.get_attribute('innerText')

            # Create a dictionary of the video meta-data
            meta_data_dict = {
                'video title': video_title,
                'video views': video_views,
                'video duration': video_time
            }

            meta_data_arr.append(meta_data_dict)

        return meta_data_arr

    def print_scrapped_content(meta_data):
        for elem_info in meta_data:
            print(elem_info)
&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;To get started, we wait for a maximum duration of 10 seconds till the document (or page) is fully loaded (i.e. &lt;em&gt;Document.readyState&lt;/em&gt; is equal to &lt;em&gt;complete&lt;/em&gt;). Feel free to check out the detailed &lt;a href="https://www.lambdatest.com/blog/document-object-model/?utm_source=devto&amp;amp;utm_medium=organic&amp;amp;utm_campaign=nov_22&amp;amp;utm_term=bh&amp;amp;utm_content=blog" rel="noopener noreferrer"&gt;DOM tutorial&lt;/a&gt; if you want a quick refresher about the nuances of DOM.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.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%2Awh9m1PjTIH4XyM8Z.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.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%2Awh9m1PjTIH4XyM8Z.png" width="800" height="223"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Once the page is loaded, we first get the scrollable height of the document using the JavaScript function &lt;em&gt;document.documentElement.scrollHeight&lt;/em&gt;. Before doing so, we maximize the browser window since that is considered one of the &lt;a href="https://www.lambdatest.com/blog/selenium-best-practices-for-web-testing/?utm_source=devto&amp;amp;utm_medium=organic&amp;amp;utm_campaign=nov_22&amp;amp;utm_term=bh&amp;amp;utm_content=blog" rel="noopener noreferrer"&gt;best practices in Selenium automation&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.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%2AI5vAhh9K0vl-cM9K.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.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%2AI5vAhh9K0vl-cM9K.png" width="800" height="400"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Next, we run a &lt;em&gt;while&lt;/em&gt; loop where a vertical scroll is performed using the &lt;em&gt;window.scrollTo()&lt;/em&gt; method with *document.documentElement.scrollHeight *as the input argument.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.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%2ApEqA6jOsXeU1Vpix.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.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%2ApEqA6jOsXeU1Vpix.png" width="800" height="400"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;In order to see the &lt;em&gt;window.scrollTo()&lt;/em&gt; method in action, open the &lt;a href="https://www.youtube.com/@LambdaTest/videos?utm_source=devto&amp;amp;utm_medium=organic&amp;amp;utm_campaign=nov_22&amp;amp;utm_term=bh&amp;amp;utm_content=youtube" rel="noopener noreferrer"&gt;LambdaTest YouTube channel&lt;/a&gt; on your browser. Then trigger the following commands in the browser console:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;&lt;em&gt;document.documentElement.scrollHeight&lt;/em&gt;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;em&gt;window.scrollTo(0, document.documentElement.scrollHeight)&lt;/em&gt;&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;a href="https://media2.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%2AKV990hXXhQ0AVMrW.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.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%2AKV990hXXhQ0AVMrW.png" width="800" height="503"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;In the implementation shown above, we perform the above two actions till the end of the page is reached. This is when we break from the while loop!&lt;/p&gt;

&lt;p&gt;The video meta-data (i.e., &lt;em&gt;name&lt;/em&gt;, &lt;em&gt;views&lt;/em&gt;, &lt;em&gt;duration&lt;/em&gt;, etc.) is present in the div with ID &lt;em&gt;details, *which is under another div *dismissible&lt;/em&gt;. The said element(s) are located using the CSS Selector (i.e., &lt;em&gt;dismissible &amp;gt; #details).&lt;/em&gt;&lt;/p&gt;

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

&lt;p&gt;As seen in the Inspect Tools screenshot, there are 581 entries (or elements) when searched with CSS Selector (i.e., *dismissible &amp;gt; #details). *In summary, the LambdaTest YouTube channel has 581 videos when writing this blog on web scraping with Python.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.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%2A27bJWC8oGb4i9b4N.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.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%2A27bJWC8oGb4i9b4N.png" width="800" height="196"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Next up, we locate the element(s) using CSS Selector &lt;em&gt;#meta,&lt;/em&gt; which is nested under the div &lt;em&gt;#details.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.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%2A9v-BB8_MhVDX1Mta.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.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%2A9v-BB8_MhVDX1Mta.png" width="800" height="400"&gt;&lt;/a&gt;&lt;/p&gt;

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

&lt;p&gt;Now that we have located the &lt;em&gt;#meta&lt;/em&gt; tag, we procure the video title, which is nested in the #meta tag. The title of the video is located using the &lt;em&gt;#video-title&lt;/em&gt; CSS Selector.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.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%2ACBWh0gAJZ4fsDrW0.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.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%2ACBWh0gAJZ4fsDrW0.png" width="800" height="500"&gt;&lt;/a&gt;&lt;/p&gt;

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

&lt;p&gt;As seen below, the &lt;em&gt;innerText&lt;/em&gt; attribute of the element (CSS Selector &lt;em&gt;#video-title&lt;/em&gt;) provides the title of the video!&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.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%2AIUSQHIa3YsIuz7m-.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.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%2AIUSQHIa3YsIuz7m-.png" width="800" height="400"&gt;&lt;/a&gt;&lt;/p&gt;

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

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Automate &lt;a href="https://www.lambdatest.com/cypress-testing?utm_source=devto&amp;amp;utm_medium=organic&amp;amp;utm_campaign=nov_22&amp;amp;utm_term=bh&amp;amp;utm_content=webpage" rel="noopener noreferrer"&gt;Cypress testing&lt;/a&gt; and perform browser automation testing with LambdaTest. Our cloud infrastructure has 3000+ desktop &amp;amp; mobile environments. Try for free!&lt;/strong&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;The next task is to find the video views, which are located using the nested CSS Selector &lt;em&gt;#metadata &amp;gt; #metadata-line &amp;gt; span:nth-child(3)&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;As seen in the Inspect Tools screenshot, &lt;em&gt;#metadata-line&lt;/em&gt; is nested inside &lt;em&gt;#metadata&lt;/em&gt; (i.e., Element with ID &lt;em&gt;#metadata-line&lt;/em&gt; is a child of an element with ID &lt;em&gt;#metadata&lt;/em&gt;). To find the element’s locator that gives &lt;em&gt;video views&lt;/em&gt;, simply hover over the views (of the first video) and copy the selector from the Inspect tools in the browser.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.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%2Aje5UZrAbnLbAg_WI.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.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%2Aje5UZrAbnLbAg_WI.png" width="800" height="499"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The pseudo class &lt;em&gt;nth-child(3)&lt;/em&gt; selects the third child span element among the direct children of the element with the ID &lt;em&gt;metadata-line&lt;/em&gt;. Hence, the element located using CSS Selector &lt;em&gt;#metadata &amp;gt; #metadata-line &amp;gt; span:nth-child(3)&lt;/em&gt; provides the views of the video in the earlier step.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.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%2AsC3Et0ygVuQlmrL6.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.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%2AsC3Et0ygVuQlmrL6.png" width="800" height="400"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Locating the Video Views element using Inspect Tools&lt;/em&gt;&lt;/p&gt;

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

&lt;p&gt;The &lt;em&gt;innerText&lt;/em&gt; attribute of the located element provides the number of views of the said video.&lt;/p&gt;

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

&lt;p&gt;On similar lines, the publishing date of the respective video is also procured using the &lt;em&gt;innerText&lt;/em&gt; attribute of the element located using the CSS Selector &lt;em&gt;#metadata &amp;gt; #metadata-line &amp;gt; span:nth-child(4).&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.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%2AtE8PqgkjmMVIvt0W.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.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%2AtE8PqgkjmMVIvt0W.png" width="800" height="499"&gt;&lt;/a&gt;&lt;/p&gt;

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

&lt;p&gt;All the above-mentioned steps are executed in a for loop that iterates through the elements that were located using &lt;em&gt;driver.find_elements(By.CSS_SELECTOR, “#dismissible &amp;gt; #details”&lt;/em&gt;)&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.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%2AqDcTXdxH2oM2o7cM.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.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%2AqDcTXdxH2oM2o7cM.png" width="800" height="400"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Automate Cypress testing and perform browser automation testing with LambdaTest. Our &lt;a href="https://www.lambdatest.com/cypress-testing?utm_source=devto&amp;amp;utm_medium=organic&amp;amp;utm_campaign=nov_22&amp;amp;utm_term=bh&amp;amp;utm_content=webpage" rel="noopener noreferrer"&gt;Cypress cloud&lt;/a&gt; infrastructure has 3000+ desktop &amp;amp; mobile environments. Try for free!&lt;/strong&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h3&gt;
  
  
  Test Scenario 2 — Scraping LambdaTest eCommerce Playground
&lt;/h3&gt;

&lt;p&gt;In this example, we scrap the product meta-data (i.e., product name and price) from &lt;a href="https://ecommerce-playground.lambdatest.io/index.php?route=product/category&amp;amp;path=57" rel="noopener noreferrer"&gt;LambdaTest eCommerce Playground&lt;/a&gt;. As mentioned earlier, the scraping logic works seamlessly on the local grid and cloud Selenium grid like LambdaTest.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.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%2AMPgGUGGxWB3HGb9h.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.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%2AMPgGUGGxWB3HGb9h.png" width="800" height="400"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Implementation (Core Scraping Logic)&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Since the overall setup &amp;amp; configuration-related implementation remain unchanged, we would directly deep dive into the scraping implementation.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.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%2Ax8cJ4rOro5wAFlJZvyXMPw.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.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%2Ax8cJ4rOro5wAFlJZvyXMPw.png" width="800" height="136"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Akin to the YouTube test scenario, the &lt;em&gt;helpers.py&lt;/em&gt; file contains the core implementation where the &lt;a href="https://github.com/hjsblogger/web-scraping-with-python/blob/main/pageobject/helpers.py#L16" rel="noopener noreferrer"&gt;*scrap_ecomm_content()&lt;/a&gt;* method is responsible for scraping the eCommerce Playground. It returns a list that contains the metadata of the SKUs on the page.&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;# Import the locators file
import sys
from pageobject.locators import locators
from pageobject.locators import *

def create_actions(driver):
    actions = ActionChains(driver)
    return actions

def create_waits(driver, duration):
    # Explicit wait of 10 seconds
    wait = WebDriverWait(driver, duration)
    return wait

class helpers(object):
    def scrap_ecomm_content(driver)-&amp;gt;list:
        meta_data_arr=[]
        # Explicit wait of 10 seconds
        wait = create_waits(driver, 10)

        actions = create_actions(driver)

        element_cat = wait.until(EC.visibility_of_element_located((By.XPATH,
                locators.shopcategory)))

        # Move to the element and perform click operation
        actions.move_to_element(element_cat).click().perform()

        element_phcat = wait.until(EC.visibility_of_element_located((By.XPATH,
                locators.phonecategory)))

        actions.move_to_element(element_phcat).click().perform()

        nested_elements = wait.until(EC.visibility_of_element_located((By.XPATH,
            "//div[@id='entry_212391']//div[@id='entry_212408']//div[@class='row']")))

        # Tough nut : 2 - nested locators!
        actual_items = nested_elements.find_elements(By.CLASS_NAME,
                "product-layout.product-grid.no-desc.col-xl-4.col-lg-4.col-md-4.col-sm-6.col-6")

        count = len(actual_items)

        for ind_elem_props in actual_items:
            nested_product_name_elem = ind_elem_props.find_element(By.CSS_SELECTOR,
                "div.product-thumb &amp;gt; div.caption")

            ################ Product Name ################
            nested_title_elem = nested_product_name_elem.find_element(By.CSS_SELECTOR,
                    ".title .text-ellipsis-2")

            ################ Price #######################
            nested_price_elem = nested_product_name_elem.find_element(By.CSS_SELECTOR,
                    ".price .price-new")

            # Create a dictionary of the meta-data of the items on e-commerce store
            meta_data_dict = {
                'product image': nested_title_elem.get_attribute('href'),
                'product name': nested_title_elem.text,
                'product price': nested_price_elem.text
            }

            meta_data_arr.append(meta_data_dict)

        return meta_data_arr

    def print_scrapped_content(meta_data):
        for elem_info in meta_data:
            print(elem_info)
&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 directly look into the major aspects of the &lt;em&gt;scrap_ecomm_content()&lt;/em&gt; method!&lt;/p&gt;

&lt;p&gt;First, we create an instance of the &lt;em&gt;ActionChains&lt;/em&gt; class to perform mouse movement operations on the page. We suggest having a look at our detailed blog on &lt;a href="https://www.lambdatest.com/blog/how-to-automate-mouse-clicks-with-selenium-python/?utm_source=devto&amp;amp;utm_medium=organic&amp;amp;utm_campaign=nov_22&amp;amp;utm_term=bh&amp;amp;utm_content=blog" rel="noopener noreferrer"&gt;*ActionChains *in Selenium Python&lt;/a&gt; in case you are new to &lt;em&gt;ActionChains&lt;/em&gt; or want to have a quick refresher of this important concept!&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.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%2ART47N7hd3je6xAk0.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.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%2ART47N7hd3je6xAk0.png" width="800" height="400"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://www.lambdatest.com/blog/selenium-webdriverwait/?utm_source=devto&amp;amp;utm_medium=organic&amp;amp;utm_campaign=nov_22&amp;amp;utm_term=bh&amp;amp;utm_content=blog" rel="noopener noreferrer"&gt;*WebDriverWait &lt;/a&gt;&lt;em&gt;and &lt;a href="https://www.lambdatest.com/blog/expected-conditions-in-selenium-examples/?utm_source=devto&amp;amp;utm_medium=organic&amp;amp;utm_campaign=nov_22&amp;amp;utm_term=bh&amp;amp;utm_content=blog" rel="noopener noreferrer"&gt;*ExpectedConditions *in Selenium&lt;/a&gt; are used to initiate an explicit wait till the visibility of a specified element is true. The element in question is the menu item that is located using the XPath *“//a[contains(.,’Shop by Category’)]&lt;/em&gt;”&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.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%2AZDmjGlYFvBXZi9QP.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.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%2AZDmjGlYFvBXZi9QP.png" width="800" height="400"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Once the menu is visible, the &lt;em&gt;move_to_element()&lt;/em&gt; method of &lt;em&gt;ActionChains&lt;/em&gt; class is used to move (or focus) on the located menu item. Since only operation (i.e., click) is involved in the chain, we invoke the &lt;em&gt;click()&lt;/em&gt; and &lt;em&gt;perform()&lt;/em&gt; methods to trigger the required action.&lt;/p&gt;

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

&lt;p&gt;Now that we are on the Menu, the menu item containing &lt;em&gt;Phone, Tablets &amp;amp; Ipod&lt;/em&gt; uses the XPath selector. Once the menu item is located, the combination of &lt;em&gt;move_to_element()&lt;/em&gt; and &lt;em&gt;click&lt;/em&gt; operations is performed to open the &lt;a href="https://ecommerce-playground.lambdatest.io/index.php?route=product/category&amp;amp;path=57" rel="noopener noreferrer"&gt;Product Page&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.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%2AaLyRoeXxtuwXNJY8.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.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%2AaLyRoeXxtuwXNJY8.png" width="800" height="500"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.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%2ApS_TiWBy5efIBq7L.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.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%2ApS_TiWBy5efIBq7L.png" width="800" height="318"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Now that we are on the Product Page, we wait for the visibility of the grid containing the respective products. By default, 15 products are available in a single go.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.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%2AkS-nNQOzhtBhQYIj.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.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%2AkS-nNQOzhtBhQYIj.png" width="800" height="482"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Nested XPath locator &lt;em&gt;//div[&lt;a class="mentioned-user" href="https://dev.to/id"&gt;@id&lt;/a&gt;=’entry_212391′]//div[&lt;a class="mentioned-user" href="https://dev.to/id"&gt;@id&lt;/a&gt;=’entry_212408′]//div[&lt;a class="mentioned-user" href="https://dev.to/class"&gt;@class&lt;/a&gt;=’row’]&lt;/em&gt; is used for locating the grid containing the products. Alternatively, you can also use the CSS Selector to locate the grid.&lt;/p&gt;

&lt;p&gt;Simply right-click on the row and choose &lt;em&gt;Copy → Copy CSS Selector&lt;/em&gt; to locate the grid using CSS Selector. You can check out our &lt;a href="https://www.lambdatest.com/blog/xpath-vs-css-selectors/?utm_source=devto&amp;amp;utm_medium=organic&amp;amp;utm_campaign=nov_22&amp;amp;utm_term=bh&amp;amp;utm_content=blog" rel="noopener noreferrer"&gt;XPath vs CSS Selectors&lt;/a&gt; guide to get more insights into the advanced features of these selectors.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.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%2A3Fn_GFvEzYWkVTRn.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.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%2A3Fn_GFvEzYWkVTRn.png" width="800" height="400"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Now that we have located the grid (containing the 15 products), the next step is to locate every row housing the corresponding product. As shown below, we have &amp;lt;&lt;em&gt;div&lt;/em&gt;&amp;gt;’s for each nested product under the locator used in the previous step.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.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%2AXS7NgUhQuHohuu9S.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.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%2AXS7NgUhQuHohuu9S.png" width="800" height="472"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The &lt;a href="https://www.lambdatest.com/blog/findelement-and-findelements-in-selenium/?utm_source=devto&amp;amp;utm_medium=organic&amp;amp;utm_campaign=nov_22&amp;amp;utm_term=bh&amp;amp;utm_content=blog" rel="noopener noreferrer"&gt;*find_elements() *method in Selenium&lt;/a&gt; is used along with the Class Name selector to find the elements containing product(s) information. Since there are 15 products on the page, the length of the list returned by the &lt;em&gt;find_elements()&lt;/em&gt; method will also be 15!&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.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%2AvAUzEQ1BGhXCD9S2.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.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%2AvAUzEQ1BGhXCD9S2.png" width="800" height="488"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.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%2AYwf4lR8r6veypQ0J.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.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%2AYwf4lR8r6veypQ0J.png" width="800" height="213"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;This Cypress automation testing tutorial will help you learn the benefits of &lt;a href="https://www.lambdatest.com/blog/cypress-test-automation-framework/?utm_source=devto&amp;amp;utm_medium=organic&amp;amp;utm_campaign=nov_22&amp;amp;utm_term=bh&amp;amp;utm_content=blog" rel="noopener noreferrer"&gt;Cypress test automation&lt;/a&gt;, and how to install Cypress and execute Cypress automation testing over scores of browsers and operating systems online.&lt;/strong&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Next, we run a &lt;em&gt;for&lt;/em&gt; loop and iterate through the list to scrap details of every product in the list. Since there are 15 products on the page, the loop will be executed 15 times.&lt;/p&gt;

&lt;p&gt;As seen below, The element(s) located using CSS locator &lt;em&gt;div.product-thumb &amp;gt; div.caption&lt;/em&gt; provides the meta-data (i.e., product name &amp;amp; price) of every product on the page.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.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%2A5p7EtPzQN1jU1t5X.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.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%2A5p7EtPzQN1jU1t5X.png" width="800" height="496"&gt;&lt;/a&gt;&lt;/p&gt;

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

&lt;p&gt;With the meta-data handy, the next step is to find the &lt;em&gt;Product Name&lt;/em&gt; from the element located in the previous step.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.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%2A271R-ypg81CMe_vW.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.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%2A271R-ypg81CMe_vW.png" width="800" height="400"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;As seen from the DOM layout, the element located using nested CSS Selector &lt;em&gt;.title .text-ellipsis-2&lt;/em&gt; provides product details.&lt;/p&gt;

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

&lt;p&gt;The text attribute of the element provides the Product Name of the respective product.&lt;/p&gt;

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

&lt;p&gt;On similar lines, the href attribute of the located element provides the link to the product (e.g., &lt;a href="https://ecommerce-playground.lambdatest.io/index.php?route=product/product&amp;amp;path=57&amp;amp;product_id=28&amp;amp;limit=15" rel="noopener noreferrer"&gt;Product Link&lt;/a&gt;).&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.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%2A7qBGSrtnLQvZDiGb.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.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%2A7qBGSrtnLQvZDiGb.png" width="800" height="554"&gt;&lt;/a&gt;&lt;/p&gt;

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

&lt;p&gt;Like the earlier step, the price of the current product is scraped by locating the element using the nested CSS Selector .&lt;em&gt;price .price-new&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.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%2A0_tyGJT3Qfgw7Sro.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.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%2A0_tyGJT3Qfgw7Sro.png" width="800" height="554"&gt;&lt;/a&gt;&lt;/p&gt;

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

&lt;p&gt;Now that the element is located, the &lt;em&gt;text&lt;/em&gt; attribute of the element gives the price of the product.&lt;/p&gt;

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

&lt;p&gt;Finally, the &lt;em&gt;scrap_ecomm_content()&lt;/em&gt; method returns a dictionary of the scraped content of the page under test!&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Test your website or web app online for iOS browser compatibility. Perform seamless cross browser testing on the latest &lt;a href="https://www.lambdatest.com/test-on-iphone-simulator?utm_source=devto&amp;amp;utm_medium=organic&amp;amp;utm_campaign=nov_22&amp;amp;utm_term=bh&amp;amp;utm_content=webpage" rel="noopener noreferrer"&gt;iPhone tester&lt;/a&gt; Simulator. Try for free!&lt;/strong&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h3&gt;
  
  
  Execution (PyUnit + Selenium)
&lt;/h3&gt;

&lt;p&gt;As stated earlier, we can use the browser on a local machine (or grid) and cloud Selenium grid.&lt;/p&gt;

&lt;p&gt;Set environment variable &lt;strong&gt;&lt;em&gt;EXEC_PLATFORM&lt;/em&gt;&lt;/strong&gt; to &lt;em&gt;**local *&lt;/em&gt;&lt;em&gt;using Chrome browser (headless mode) with Selenium for web scraping. Invoke the command *make scrap-using-pyunit&lt;/em&gt; to start scraping content from the test website(s).&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.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%2AoToOMCHxE0ebZrSS.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.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%2AoToOMCHxE0ebZrSS.png" width="800" height="475"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;As shown below, the Accept all button does not appear when the scraping is done on a local machine.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.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%2AzQ9mTCoeHvQwMUAR.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.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%2AzQ9mTCoeHvQwMUAR.png" width="800" height="442"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;On the other hand, the Accept all button is seen when the scraping is done using Chrome (headless mode) on cloud Selenium Grid.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.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%2AFx5DKaZFiEbfDEWP.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.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%2AFx5DKaZFiEbfDEWP.png" width="800" height="376"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;As seen below, the content from the LambdaTest YouTube channel and LambdaTest eCommerce playground was scrapped successfully.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.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%2AApRVjeztIIqoHzXP.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.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%2AApRVjeztIIqoHzXP.png" width="800" height="400"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.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%2AJPBOml8HIQZeS6f4.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.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%2AJPBOml8HIQZeS6f4.png" width="800" height="371"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Set environment variable &lt;strong&gt;&lt;em&gt;EXEC_PLATFORM&lt;/em&gt;&lt;/strong&gt; to &lt;em&gt;**cloud *&lt;/em&gt;*for using Chrome browser (headless mode) on cloud Selenium Grid.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.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%2AxyQ0svtzi_an2fzl.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.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%2AxyQ0svtzi_an2fzl.png" width="800" height="400"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Log on to the &lt;a href="https://automation.lambdatest.com/build?utm_source=devto&amp;amp;utm_medium=organic&amp;amp;utm_campaign=nov_22&amp;amp;utm_term=bh&amp;amp;utm_content=webpage" rel="noopener noreferrer"&gt;LambdaTest Automation Dashboard&lt;/a&gt; to view the status of the test execution.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.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%2A7lOjvtsfGRxFRhz_.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.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%2A7lOjvtsfGRxFRhz_.png" width="800" height="326"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;As seen below, the test execution on LambdaTest was successful!&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.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%2A5V1enehABzalkn_7.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.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%2A5V1enehABzalkn_7.png" width="800" height="321"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  Execution (pytest + Selenium)
&lt;/h3&gt;

&lt;p&gt;Like earlier, set &lt;strong&gt;&lt;em&gt;EXEC_PLATFORM&lt;/em&gt;&lt;/strong&gt; to &lt;em&gt;**local *&lt;/em&gt;&lt;em&gt;for using Chrome browser (headless mode) with Selenium for web scraping with Python. Invoke the command *make scrap-using-pytest&lt;/em&gt; to start scraping content from the test website(s).&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.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%2AYeKUTNTa786Bki7L.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.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%2AYeKUTNTa786Bki7L.png" width="800" height="400"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;As seen below, scraping data from the test websites was successful.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.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%2Ar2smPbSTV2J8sBk3.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.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%2Ar2smPbSTV2J8sBk3.png" width="800" height="442"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.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%2ABhCnf8E-769MsiZR.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.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%2ABhCnf8E-769MsiZR.png" width="800" height="400"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Set environment variable &lt;strong&gt;&lt;em&gt;EXEC_PLATFORM&lt;/em&gt;&lt;/strong&gt; to the &lt;em&gt;**cloud *&lt;/em&gt;&lt;em&gt;and invoke *make scrap-using-pytest&lt;/em&gt; for using Chrome browser (headless mode) on cloud Selenium Grid.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.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%2AJ0tpw7VvC5dh_7uP.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.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%2AJ0tpw7VvC5dh_7uP.png" width="800" height="436"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.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%2ADPV6fwisgm04zaGM.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.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%2ADPV6fwisgm04zaGM.png" width="800" height="400"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Shown below is the status on the dashboard, which indicates that web scraping using Selenium and pytest was successful.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.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%2A4tdgNNmzguNlvF5z.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.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%2A4tdgNNmzguNlvF5z.png" width="800" height="318"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.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%2AzFpfKwD9XiwUUin7.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.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%2AzFpfKwD9XiwUUin7.png" width="800" height="326"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Explore Jest tutorial, your go-to guide for mastering &lt;a href="https://www.lambdatest.com/jest?utm_source=devto&amp;amp;utm_medium=organic&amp;amp;utm_campaign=nov_22&amp;amp;utm_term=bh&amp;amp;utm_content=webpage" rel="noopener noreferrer"&gt;Jest&lt;/a&gt; testing. Run Jest automation tests in parallel across multiple browser and OS combinations.&lt;/strong&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2&gt;
  
  
  Web Scraping using Beautiful Soup and Selenium
&lt;/h2&gt;

&lt;p&gt;For a demonstration of web scraping with Python using Beautiful Soup and Selenium, we would be scraping content from the following websites:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.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%2A1xf1FSyaiwV_vlVyz_SxRg.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.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%2A1xf1FSyaiwV_vlVyz_SxRg.png" width="800" height="146"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;We are using the combination of Beautiful Soup and Selenium for a very specific reason.🙂As you can see, both the test websites have dynamic content which is where Selenium has an upper-hand over Beautiful Soup.&lt;/p&gt;

&lt;p&gt;Selenium is used for performing actions (e.g., scrolling, clicking, etc.) on the page (or document) so that the content to be scraped is available for our perusal. On the other hand, Beautiful Soup is used for parsing and navigating the structure of the HTML page.&lt;/p&gt;

&lt;p&gt;The prerequisites and directory structure are already covered in the earlier sections of this blog on web scraping with Python. &lt;em&gt;BeautifulSoup4&lt;/em&gt; (or &lt;em&gt;bs4&lt;/em&gt;) &amp;amp; &lt;em&gt;Selenium (v 4.13.0)&lt;/em&gt; are used for scraping the ScrapingClub website, whereas bs4, along with &lt;em&gt;Requests (v 2.31.0)&lt;/em&gt; help in scraping the eCommerce playground product page(s).&lt;/p&gt;

&lt;h3&gt;
  
  
  Demonstration: Scraping eCommerce Playground
&lt;/h3&gt;

&lt;p&gt;In this test scenario, we would scrap the products from the eCommerce Playground product page. The website uses pagination, and 15 products are displayed on a single page.&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;&lt;p&gt;Navigate to the &lt;a href="https://ecommerce-playground.lambdatest.io/index.php?route=product/category&amp;amp;path=57" rel="noopener noreferrer"&gt;eCommerce Playground product page&lt;/a&gt;.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Send an HTTP GET request to the URL using the Requests library.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Using bs4, scrap product information from all the products of the said category.&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;Similar to the configuration for the PyUnit &amp;amp; pytest frameworks, the locator &amp;amp; URL details are present in locators.py.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.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%2A4XK6Uf_jzE6tPr-7.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.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%2A4XK6Uf_jzE6tPr-7.png" width="800" height="243"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The core scraping logic is implemented in &lt;a href="https://github.com/hjsblogger/web-scraping-with-python/blob/main/tests/beautiful-soup/test_ecommerce_scraping.py" rel="noopener noreferrer"&gt;*test_ecommerce_scraping.py.&lt;/a&gt;* Let’s dive deep into the same!&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;# Beautiful Soup Official Documentation - https://www.crummy.com/software/BeautifulSoup/bs4/doc/

# Import the locators file
import sys
from pprint import pprint
sys.path.append(sys.path[0] + "/../..")

from pageobject.locators import locators
from pageobject.locators import *

from pageobject.helpers import helpers
from pageobject.helpers import *

def scrap_ecommerce(url) -&amp;gt; list:
    response = requests.get(url)

    if response.status_code != 200:
        print(f"Unable to fetch the page. Status code: {response.status_code}")
        return None

    soup = BeautifulSoup(response.text, 'html.parser')

    rows = soup.select('.product-layout.product-grid.no-desc.col-xl-4.col-lg-4.col-md-4.col-sm-6.col-6')

    meta_data_arr = []

    for row in rows:
        link = row.find("a", class_='carousel d-block slide')
        name = row.find("h4", class_='title')
        price = row.find("span", class_='price-new')

        # Create a dictionary of the meta-data of the items on e-commerce store
        meta_data_dict = {
            'product link': link.get_attribute_list('href'),
            'product name': name.get_text(),
            'product price': price.get_text()
        }

        meta_data_arr.append(meta_data_dict)
    return meta_data_arr

# Pagination - 1:5
# Page 1: https://ecommerce-playground.lambdatest.io/index.php?route=product/category&amp;amp;path=57&amp;amp;page=1
# Page 5: https://ecommerce-playground.lambdatest.io/index.php?route=product/category&amp;amp;path=57&amp;amp;page=5
if __name__ == '__main__':
    for iteration in range(1,6):
        test_url = locators.test_bs4_url + "&amp;amp;page=" + str(iteration)
        meta_data_arr = scrap_ecommerce(test_url)
        print('\n')
        print("Product Page = " + test_url)
        print("*********************************************************************************************************\n")
        helpers.print_scrapped_content(meta_data_arr)
&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;Inside the &lt;em&gt;&lt;strong&gt;main&lt;/strong&gt; *construct, we set up the test URL, which constitutes the &lt;a href="https://ecommerce-playground.lambdatest.io/index.php?route=product/category&amp;amp;path=57?utm_source=devto&amp;amp;utm_medium=organic&amp;amp;utm_campaign=nov_22&amp;amp;utm_term=bh&amp;amp;utm_content=webpage" rel="noopener noreferrer"&gt;base URL&lt;/a&gt; appended by the page number (i.e. &amp;amp;*page=&amp;lt; page-number &amp;gt;&lt;/em&gt;).&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.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%2A7k6HAK5J2kr7dKqL.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.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%2A7k6HAK5J2kr7dKqL.png" width="800" height="426"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.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%2AdyLm5y2QvTVL0ErM.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.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%2AdyLm5y2QvTVL0ErM.png" width="800" height="455"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Since there are 5 pages in total for the said product category, the scraping implementation is executed in an interactive loop (from 1..6).&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.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%2ANqCQ--W1MUoVrFdX.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.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%2ANqCQ--W1MUoVrFdX.png" width="800" height="400"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;To get started with scraping, an HTTP GET request is initiated using the &lt;em&gt;get()&lt;/em&gt; method of the &lt;em&gt;requests&lt;/em&gt; library. The method takes the URL under test as the input parameter and returns a response object.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.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_igrQRypmQdLR0J_.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.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_igrQRypmQdLR0J_.png" width="800" height="375"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The status code (i.e., &lt;em&gt;status_code&lt;/em&gt;) attribute of the response object is compared with 200 (i.e., &lt;em&gt;STATUS_OK&lt;/em&gt;). If the status_code is not 200 (e.g., 404, 403, etc.), the test is terminated.&lt;/p&gt;

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

&lt;p&gt;The &lt;em&gt;response.text&lt;/em&gt; attribute contains the HTML content of the page, which is retrieved as a string. As stated earlier, the HTML parser (i.e., &lt;em&gt;html.parser&lt;/em&gt;), the default parser in bs4, is used to parse the HTML content.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;BeautifulSoup(response.text, ‘html.parser’)&lt;/em&gt; returns a &lt;em&gt;BeautifulSoup&lt;/em&gt; object that will be used throughout scraping. The &lt;em&gt;select()&lt;/em&gt; method of Beautiful Soup finds elements using the CSS Selector property — .&lt;em&gt;product-layout.product-grid.no-desc.col-xl-4.col-lg-4.col-md-4.col-sm-6.col-6&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;As expected, 15 elements match the CSS Selector since there are 15 products on the page. The &lt;em&gt;select()&lt;/em&gt; method returns a list that will be further used for iteratively scraping information (i.e., name, cost) of each product.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.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%2AFBM1pqNPwSB8tKJ5.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.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%2AFBM1pqNPwSB8tKJ5.png" width="800" height="400"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.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%2ANIAkIetVFKYyxGSC.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.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%2ANIAkIetVFKYyxGSC.png" width="800" height="400"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Next up, we run a for loop for scraping information of all the 15 products (or elements) under the &lt;em&gt;div&lt;/em&gt; located in the previous step.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.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%2A4lf6O9FQiuK8EHvB.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.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%2A4lf6O9FQiuK8EHvB.png" width="800" height="558"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Test native, hybrid, and web apps on any mobile OS with our free &lt;a href="https://www.lambdatest.com/android-emulator-online?utm_source=devto&amp;amp;utm_medium=organic&amp;amp;utm_campaign=nov_22&amp;amp;utm_term=bh&amp;amp;utm_content=webpage" rel="noopener noreferrer"&gt;Android emulator&lt;/a&gt; online. Sign up to optimize app performance.&lt;/strong&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;The &lt;strong&gt;product link&lt;/strong&gt; is obtained by locating the element using the &lt;em&gt;find()&lt;/em&gt; method of Beautiful Soup. As seen in the Inspect Tools screenshot above, the first argument is the tag that needs to be searched for (i.e., “a” — anchor tag), and the second is the CSS Class attribute.&lt;/p&gt;

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

&lt;p&gt;Link to the product is procured by using the &lt;em&gt;get_attribute_list()&lt;/em&gt; method with the located element. In case you need more information on the &lt;em&gt;find()&lt;/em&gt; method of Beautiful Soup, we recommend checking out the Beautiful Soup Official Documentation.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.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%2ArKRak680c9H8z7CW.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.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%2ArKRak680c9H8z7CW.png" width="800" height="558"&gt;&lt;/a&gt;&lt;/p&gt;

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

&lt;p&gt;On similar lines, the &lt;strong&gt;product name&lt;/strong&gt; is also scraped by using the &lt;em&gt;find()&lt;/em&gt; method in Beautiful Soup. In this case, the first argument is the “&lt;em&gt;h4&lt;/em&gt;” tag, and the second argument is the Class Name locator “&lt;em&gt;title&lt;/em&gt;”.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.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%2Ae9spfdSnjq_XZuLJ.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.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%2Ae9spfdSnjq_XZuLJ.png" width="800" height="543"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Finally, the &lt;a href="https://www.crummy.com/software/BeautifulSoup/bs4/doc/#get-text" rel="noopener noreferrer"&gt;*get_text()&lt;/a&gt;* method of Beautiful Soup, when used with the recently located element, provides the product name of the current product.&lt;/p&gt;

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

&lt;p&gt;The last step is to scrap the &lt;strong&gt;price&lt;/strong&gt; of the respective product. As shown below, the text under the element with Class Name “&lt;em&gt;price-new&lt;/em&gt;” provides the product price.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.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%2AjznWYjvG7we7q8vO.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.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%2AjznWYjvG7we7q8vO.png" width="800" height="537"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Like before, the &lt;em&gt;find()&lt;/em&gt; method of Beautiful Soup is used with “&lt;em&gt;span&lt;/em&gt;” (i.e., tag to be located) as the first argument and Class Name “&lt;em&gt;price-new&lt;/em&gt;” as the second argument. The &lt;em&gt;get_text()&lt;/em&gt; method returns the text inside the element.&lt;/p&gt;

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

&lt;p&gt;Finally, a dictionary (i.e., &lt;em&gt;meta_data_dict&lt;/em&gt;) of the scraped information (i.e., product link, name, and price) is created, and the same is appended to the list (i.e., &lt;em&gt;meta_data_arr&lt;/em&gt;).&lt;/p&gt;

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

&lt;p&gt;The same steps are repeated for all the other product pages (i.e., &lt;a href="https://ecommerce-playground.lambdatest.io/index.php?route=product/category&amp;amp;path=57&amp;amp;page=1" rel="noopener noreferrer"&gt;Product Page 1&lt;/a&gt; → &lt;a href="https://ecommerce-playground.lambdatest.io/index.php?route=product/category&amp;amp;path=57&amp;amp;page=5" rel="noopener noreferrer"&gt;Product Page 5&lt;/a&gt;), and the scraped data is displayed on the screen.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.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%2A397zM9JJdLy_E8_n.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.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%2A397zM9JJdLy_E8_n.png" width="800" height="400"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  Demonstration: Scraping Infinite Scrolling website
&lt;/h3&gt;

&lt;p&gt;In this test scenario, we would be scraping the ScrapingClub website that has content loaded on a dynamic basis. Since the website involves dynamic content, the combination of Selenium &amp;amp; Beautiful Soup is used for web page interactions &amp;amp; scraping.&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;&lt;p&gt;Navigate to Infinite Scroll Page.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Scroll till the end of the page.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Scrap product content using Beautiful Soup.&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;Akin to the PyUnit &amp;amp; pytest frameworks, the locator &amp;amp; URL details are present in locators.py&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.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%2Alr9k17jdMPmZJ1Pa.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.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%2Alr9k17jdMPmZJ1Pa.png" width="800" height="243"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The core scraping logic is implemented in &lt;a href="https://github.com/hjsblogger/web-scraping-with-python/blob/main/tests/beautiful-soup/test_infinite_scraping.py" rel="noopener noreferrer"&gt;*test_infinite_scraping.py.&lt;/a&gt;* Let’s deep dive into the same!&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;# Selenium WebDriver used for infinite scrolling
# Beautiful Soup used for scraping content

# Beautiful Soup Official Documentation - https://www.crummy.com/software/BeautifulSoup/bs4/doc

import sys
# Import the locators file
import os
from pprint import pprint
sys.path.append(sys.path[0] + "/../..")

from pageobject.locators import locators
from pageobject.locators import *

from pageobject.helpers import helpers
from pageobject.helpers import *

exec_platform = os.getenv('EXEC_PLATFORM')

def scrap_inifite_website(url) -&amp;gt; list:
    meta_data_arr = []

    if exec_platform == 'cloud':
        username = environ.get('LT_USERNAME', None)
        access_key = environ.get('LT_ACCESS_KEY', None)

        gridURL = "@hub.lambdatest.com/wd/hub"&amp;gt;https://{}:{}@hub.lambdatest.com/wd/hub".format(username, access_key)

        ch_options = webdriver.ChromeOptions()
        ch_options.browser_version = "latest"
        ch_options.platform_name = "Windows 11"

        lt_options = {}
        lt_options["build"] = "Build: Web Scraping with Selenium &amp;amp; Beautiful Soup"
        lt_options["project"] = "Project: Web Scraping with Selenium &amp;amp; Beautiful Soup"
        lt_options["name"] = "Test: Web Scraping with Selenium &amp;amp; Beautiful Soup"

        lt_options["browserName"] = "Chrome"
        lt_options["browserVersion"] = "latest"
        lt_options["platformName"] = "Windows 11"

        lt_options["console"] = "error"
        lt_options["w3c"] = True
        lt_options["headless"] = True

        ch_options.set_capability('LT:Options', lt_options)

        driver = webdriver.Remote(
            command_executor = gridURL,
            options = ch_options
        )
    elif exec_platform == 'local':
        options = ChromeOptions()
        options.add_argument("--headless=new")
        driver = webdriver.Chrome(options=options)

    driver.get(url)

    # Took some support from https://stackoverflow.com/a/41181494/126105
    start_height = driver.execute_script("return document.documentElement.scrollHeight")

    while True:
        # Scroll to the bottom of the page
        driver.execute_script("window.scrollTo(0, " + str(start_height) + ")")
        # Wait for the content to load
        time.sleep(2)
        scroll_height = driver.execute_script("return document.documentElement.scrollHeight")
        if (scroll_height == start_height):
            # If heights are the same, we reached the end of page
            break
        time.sleep(2)
        start_height = scroll_height

    time.sleep(2)

    # Why features='html.parser' is required
    # The code that caused this warning is on line 44 of the file &amp;lt;file&amp;gt;.
    # To get rid of this warning, pass the additional argument 'features="html.parser"'
    # to the BeautifulSoup constructor.
    soup = BeautifulSoup(driver.page_source, features='html.parser')

    # Instantiated Chrome instance is no longer required
    # since we have already read the source
    driver.quit()

    # Code changes as per 28/07/2023
    # In case if elements are not located, please change the locators accordingly
    rows = soup.find_all('div', class_='w-full rounded border post')
    print("\nTotal items on " + url + " are " + str(len(rows)) + "\n")

    for row in rows:
        dress = row.find('h4')
        link = dress.find('a')
        price = row.find('h5')

        # Create a dictionary of the meta-data of the items on e-commerce store
        meta_data_dict = {
            'dress': dress.text.strip(),
            'link' : link.get_attribute_list('href'),
            'price': price.text.strip()
        }

        meta_data_arr.append(meta_data_dict)
    return meta_data_arr

if __name__ == '__main__':
    meta_data_arr = scrap_inifite_website(locators.test_bs4_infinite_url)
    helpers.print_scrapped_content(meta_data_arr)
&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;Inside the &lt;em&gt;&lt;strong&gt;main&lt;/strong&gt; *construct, we invoke the &lt;a href="https://github.com/hjsblogger/web-scraping-with-python/blob/main/tests/beautiful-soup/test_infinite_scraping.py#L20" rel="noopener noreferrer"&gt;*scrap_inifinite_website()&lt;/a&gt;&lt;/em&gt; method that is primarily responsible for scraping the website under test.&lt;/p&gt;

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

&lt;p&gt;Since we are using Selenium for performing actions on the page, the EXEC_PLATFORM environment variable helps us choose between local grid (or machine) and the cloud Selenium Grid.&lt;/p&gt;

&lt;p&gt;We have already covered Remote WebDriver and &lt;a href="https://www.lambdatest.com/learning-hub/headless-browser-testing?utm_source=devto&amp;amp;utm_medium=organic&amp;amp;utm_campaign=nov_22&amp;amp;utm_term=bh&amp;amp;utm_content=learning_hub" rel="noopener noreferrer"&gt;Headless browser testing&lt;/a&gt; in the earlier sections of the blog.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.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%2AHraxRQiP7LdeoeOZ.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.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%2AHraxRQiP7LdeoeOZ.png" width="800" height="400"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Since the content is loaded dynamically (i.e., on page scroll), scroll till the end of page is performed using the combination of the &lt;em&gt;execute_script()&lt;/em&gt; method with &lt;em&gt;document.documentElement.scrollHeight&lt;/em&gt; as the input argument.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.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%2Ahpnvdj1Ln5tzjKgY.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.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%2Ahpnvdj1Ln5tzjKgY.png" width="800" height="500"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Content &amp;amp; Images loaded on a dynamic basis&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.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%2AMUNNbmZisyq0I0wL.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.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%2AMUNNbmZisyq0I0wL.png" width="800" height="422"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Now that the page load is complete, create a Beautiful Soup object that parses the page source obtained using Selenium. You can check out our detailed blog that deep dives into &lt;a href="https://www.lambdatest.com/blog/how-to-get-page-source-in-selenium-webdriver/?utm_source=devto&amp;amp;utm_medium=organic&amp;amp;utm_campaign=nov_22&amp;amp;utm_term=bh&amp;amp;utm_content=blog" rel="noopener noreferrer"&gt;getting page sources with Selenium WebDriver&lt;/a&gt; for more information on retrieving page sources using Selenium.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.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%2AajypaDcm4AyIR9HS.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.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%2AajypaDcm4AyIR9HS.png" width="800" height="400"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Since we already have the page source, it’s safe to release resources used by the instantiated browser instance.&lt;/p&gt;

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

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Discover 31 &lt;a href="https://www.lambdatest.com/blog/automation-testing-tools/?utm_source=devto&amp;amp;utm_medium=organic&amp;amp;utm_campaign=nov_22&amp;amp;utm_term=bh&amp;amp;utm_content=blog" rel="noopener noreferrer"&gt;best automation testing tools&lt;/a&gt; in 2023 for powerful and efficient testing solutions.&lt;/strong&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Next, the &lt;em&gt;find_all()&lt;/em&gt; method of Beautiful Soup is used with “&lt;em&gt;div&lt;/em&gt;” (i.e., tag to be located) as the first argument and Class Name “&lt;em&gt;w-full rounded border post&lt;/em&gt;” as the second argument. It returns a list of elements that meet the specified conditions.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.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%2AuIotlV06vZ_OWCD0.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.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%2AuIotlV06vZ_OWCD0.png" width="800" height="536"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;As seen from the Inspect Tools screenshot, the length of the list returned by &lt;em&gt;find_all()&lt;/em&gt; method should be 60 (i.e. 60 products are present on the page).&lt;/p&gt;

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

&lt;p&gt;Next up, we use the for loop for iteratively scraping information (i.e. dress name, link, price) of each product.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.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%2Aiot1PemZKrdsEaY0.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.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%2Aiot1PemZKrdsEaY0.png" width="684" height="418"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The &lt;em&gt;find()&lt;/em&gt; method of Beautiful Soup is used to locate the element with input as “&lt;em&gt;h4&lt;/em&gt;” tag.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.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%2Avh1SRH-ID4B_H1sv.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.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%2Avh1SRH-ID4B_H1sv.png" width="800" height="607"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The &lt;em&gt;text()&lt;/em&gt; attribute of the located element provides the &lt;strong&gt;product name&lt;/strong&gt; (e.g., Short Dress). The leading &amp;amp; trailing spaces of the output are done using the &lt;em&gt;strip()&lt;/em&gt; method.&lt;/p&gt;

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

&lt;p&gt;The “&lt;em&gt;href&lt;/em&gt;” attribute of the element located earlier using the “&lt;em&gt;h4&lt;/em&gt;” tag [i.e., &lt;em&gt;dress = row.find(‘h4’)&lt;/em&gt;] gives the &lt;strong&gt;product link&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.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%2A4KDUW7UtUeyNjW3H.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.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%2A4KDUW7UtUeyNjW3H.png" width="800" height="607"&gt;&lt;/a&gt;&lt;/p&gt;

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

&lt;p&gt;On similar lines, the price of the respective product is obtained by locating the element using the &lt;em&gt;find()&lt;/em&gt; method. The method takes the “&lt;em&gt;h5&lt;/em&gt;” tag as the input argument since the &lt;em&gt;text()&lt;/em&gt; attribute of the element contains the &lt;strong&gt;product price&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.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%2AKqza-Gx6KYGDLDVc.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.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%2AKqza-Gx6KYGDLDVc.png" width="800" height="400"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Deep dive to learn about test automation; &lt;a href="https://www.lambdatest.com/learning-hub/automation-testing?utm_source=devto&amp;amp;utm_medium=organic&amp;amp;utm_campaign=nov_22&amp;amp;utm_term=bh&amp;amp;utm_content=learning_hub" rel="noopener noreferrer"&gt;what is automation testing&lt;/a&gt;&lt;/strong&gt;,&lt;strong&gt;its uasage, types and also gain insights on how to get started with automated testing.&lt;/strong&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Like the earlier step, leading &amp;amp; trailing spaces from the output of &lt;em&gt;text()&lt;/em&gt; attribute to get the final price of the respective product.&lt;/p&gt;

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

&lt;p&gt;With this, we have successfully scraped the meta-data of products from the ScrapingClub (Infinite Scrolling) website. Let’s trigger the same on the local as well as LambdaTest Cloud Selenium grid.&lt;/p&gt;

&lt;h3&gt;
  
  
  Execution
&lt;/h3&gt;

&lt;p&gt;As stated earlier, we can instantiate the Chrome browser (in headless mode) on a local machine (or grid) as well as cloud Selenium grid.&lt;/p&gt;

&lt;p&gt;Set environment variable &lt;strong&gt;&lt;em&gt;EXEC_PLATFORM&lt;/em&gt;&lt;/strong&gt; to &lt;strong&gt;&lt;em&gt;local&lt;/em&gt;&lt;/strong&gt; for using cloud Selenium grid. Invoke the command &lt;em&gt;make scrap-using-beautiful-soup&lt;/em&gt; to start scraping content from the test website(s).&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.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%2A_c2_5KLZR87LiBSr.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.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%2A_c2_5KLZR87LiBSr.png" width="800" height="292"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;As seen below, we are able to scrap content from the LambdaTest E-commerce Playground Product Page(s) and ScrapingClub website.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.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%2Aze-kj2X-jqRuLXIu.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.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%2Aze-kj2X-jqRuLXIu.png" width="800" height="415"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.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%2Ai0rPluJra9tXolSN.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.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%2Ai0rPluJra9tXolSN.png" width="800" height="432"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Set environment variable &lt;strong&gt;&lt;em&gt;EXEC_PLATFORM&lt;/em&gt;&lt;/strong&gt; to &lt;strong&gt;&lt;em&gt;cloud&lt;/em&gt;&lt;/strong&gt; for using Cloud Selenium Grid on LambdaTest.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.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%2AiPXF2Xc8-cl00GEF.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.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%2AiPXF2Xc8-cl00GEF.png" width="800" height="426"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.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%2A8uKJsUFUkBf49ntA.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.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%2A8uKJsUFUkBf49ntA.png" width="800" height="421"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.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%2Azs2WJSk0e5d20Cow.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.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%2Azs2WJSk0e5d20Cow.png" width="800" height="400"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.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%2AeW-4wsdpcicLrkMR.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.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%2AeW-4wsdpcicLrkMR.png" width="800" height="400"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Shown below is the status on the dashboard, which indicates that web scraping with Python using Beautiful Soup and Selenium was successful.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.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%2A4_9ZCJvc3wTmBvIo.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.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%2A4_9ZCJvc3wTmBvIo.png" width="800" height="400"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.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%2AjpwMvwz-KgI7fE25.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.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%2AjpwMvwz-KgI7fE25.png" width="800" height="376"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;With this, we have covered web scraping with Python using popular libraries (or tools) like Selenium (with PyUnit &amp;amp; pytest) and Beautiful Soup. The popularly-quoted Spider-Man theme of “&lt;em&gt;With great power, comes great responsibility&lt;/em&gt;” also applies to web scraping!&lt;/p&gt;

&lt;p&gt;This is because web scraping with Python must be performed while taking legal and ethical considerations into account. It is unethical to scrape confidential (or sensitive) information from any website.&lt;/p&gt;

&lt;h2&gt;
  
  
  Web Scraping Done Right!
&lt;/h2&gt;

&lt;p&gt;As stated, web scraping is a super-powerful tool to have in your data collection armory. However, its benefits should be leveraged with utmost caution! Python provides numerous tools (or libraries) for web scraping, making it one of the preferred languages for scraping web content.&lt;/p&gt;

&lt;p&gt;Though the choice of web scraping tool purely depends on the requirements, Selenium can be preferred for scraping dynamic web content. On the other hand, Beautiful Soup is one of the preferred choices for scraping static web content.&lt;/p&gt;

&lt;p&gt;Playwright is another popular framework for scraping web content. You can refer to our detailed 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_22&amp;amp;utm_term=bh&amp;amp;utm_content=blog" rel="noopener noreferrer"&gt;Web Scraping with Playwright and Python&lt;/a&gt; in case you intend to leverage the framework for scraping.&lt;/p&gt;

&lt;p&gt;As a part of the web scraping series, I dabbled into &lt;a href="https://www.lambdatest.com/blog/scraping-dynamic-web-pages/?utm_source=devto&amp;amp;utm_medium=organic&amp;amp;utm_campaign=nov_22&amp;amp;utm_term=bh&amp;amp;utm_content=blog" rel="noopener noreferrer"&gt;Web Scraping with C# using Selenium&lt;/a&gt;. To summarize, web scraping is a great tool that helps make the most of public data, but it should be used with utmost responsibility.🙂&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Happy Scraping !&lt;/em&gt;&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;This blog explains different &lt;a href="https://www.lambdatest.com/blog/types-of-automation-testing/?utm_source=devto&amp;amp;utm_medium=organic&amp;amp;utm_campaign=nov_22&amp;amp;utm_term=bh&amp;amp;utm_content=blog" rel="noopener noreferrer"&gt;types of automation testing&lt;/a&gt; along with their benefits and best practices. Also we have discussed different frameworks and tools at the end.&lt;/strong&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2&gt;
  
  
  Frequently Asked Questions (FAQs)
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Is Python good for web scraping?
&lt;/h3&gt;

&lt;p&gt;Yes, Python is widely recognized as an excellent programming language for web scraping. Python offers several libraries and frameworks, such as BeautifulSoup and Scrapy, that simplify the web scraping process. Its simplicity, readability, and rich ecosystem of packages make it a popular choice for web scraping tasks. Additionally, Python is platform-independent, which means you can use it on various operating systems to collect data from websites efficiently.&lt;/p&gt;

&lt;h3&gt;
  
  
  What is web scraping with Python?
&lt;/h3&gt;

&lt;p&gt;Web scraping with Python refers to automatically extracting data from websites. Python provides various libraries and frameworks, such as BeautifulSoup, Scrapy, and Requests, that enable developers to write scripts to access web pages, parse the HTML content, and extract specific information or data. This data can be used for various purposes, including data analysis, research, content aggregation, or any other application where web data is needed. Web scraping with Python is a versatile and powerful technique for collecting information from the internet.&lt;/p&gt;

&lt;h3&gt;
  
  
  Is it legal to web scrape?
&lt;/h3&gt;

&lt;p&gt;The legality of web scraping depends on various factors, including the website’s terms of service, the nature of the data being collected, and local or international laws. Here are some key points to consider:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;Website’s Terms of Service: Websites often have service or usage policies that specify whether web scraping is allowed or prohibited. It’s essential to review and comply with these terms. Violating a website’s terms of service could lead to legal issues.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Publicly Accessible Data: Web scraping publicly accessible data, such as information without logging in, is typically considered more acceptable. However, this does not grant unlimited rights to use or redistribute the data.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Copyright and Intellectual Property: If the data being scraped is protected by copyright or contains intellectual property, scraping and using that data may infringe on those rights.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Personal Data and Privacy: Scraping and using personal data without consent can raise privacy and legal concerns, particularly under data protection laws like the GDPR in the European Union.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Anti-Competitive Behavior: Web scraping for anti-competitive purposes, like price scraping to undercut competitors unfairly, may be subject to legal action under antitrust laws.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Rate Limiting and Politeness: It’s advisable to implement rate limiting and be polite in your scraping activities. Excessive requests can strain a website’s servers and may be viewed as a denial-of-service attack.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Laws and Jurisdictions: The legal aspects of web scraping can vary by jurisdiction. What’s legal in one country may not be in another.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;To ensure compliance with laws and ethical standards, it’s essential to consult with legal experts if you have concerns about the legality of your web scraping activities, particularly when dealing with sensitive or proprietary data. Additionally, always respect a website’s terms of service and robots.txt file, which can specify rules for web crawlers.&lt;/p&gt;

</description>
      <category>python</category>
      <category>tutorial</category>
      <category>beginners</category>
      <category>programming</category>
    </item>
    <item>
      <title>Auto Healing in Selenium Automation Testing</title>
      <dc:creator>himanshuseth004</dc:creator>
      <pubDate>Fri, 10 Nov 2023 11:00:42 +0000</pubDate>
      <link>https://forem.com/testmuai/auto-healing-in-selenium-automation-testing-1077</link>
      <guid>https://forem.com/testmuai/auto-healing-in-selenium-automation-testing-1077</guid>
      <description>&lt;p&gt;A number of challenges always loom large as the Application Under Test (AUT) undergoes endless cycles of bug fixes and enhancements. As the AUT grows in size and complexity, it will also trickle down on the automated test suite’s stability, reliability, and maintainability. All of this eventually results in intermittent test failures in the CI/CD pipeline, thereby impacting the overall health of the pipeline.&lt;/p&gt;

&lt;p&gt;Over &amp;amp; above, there is a higher probability of witnessing unforeseen errors when the application is deployed across diverse environments (e.g., staging, production, etc.). As per a survey conducted by LambdaTest, &lt;a href="https://www.lambdatest.com/learning-hub/flaky-test?utm_source=devto&amp;amp;utm_medium=organic&amp;amp;utm_campaign=nov_10&amp;amp;utm_term=bh&amp;amp;utm_content=learning_hub" rel="noopener noreferrer"&gt;test flakiness&lt;/a&gt; or test instabilities is one of the major challenges with tests devised using test automation frameworks like Selenium.&lt;/p&gt;

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

&lt;p&gt;&lt;a href="https://www.linkedin.com/feed/update/urn:li:activity:7081963734419615745/" rel="noopener noreferrer"&gt;*Poll Source&lt;/a&gt;*&lt;/p&gt;

&lt;p&gt;Instabilities in the test results normally occur due to internal and external factors, most of which can be controlled by focusing on efficient &amp;amp; scalable test design and execution. Flaky test design, incorrect wait strategies, dependent tests, and inadequate test isolation are some of the ‘controllable’ factors that lead to test flakiness.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://www.lambdatest.com/blog/auto-healing/?utm_source=devto&amp;amp;utm_medium=organic&amp;amp;utm_campaign=nov_10&amp;amp;utm_term=bh&amp;amp;utm_content=blog" rel="noopener noreferrer"&gt;Auto healing&lt;/a&gt; of automated tests helps improve the effectiveness of test suite(s)/test(s) and increases the ROI of test automation. In this blog, we deep-dive into the nitty-gritty of auto healing in Selenium and how to reduce test maintenance costs with the auto healing feature (or Selenium capability) on LambdaTest Continuous Quality Cloud.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Looking for a unique username? Generate usernames with our &lt;a href="https://www.lambdatest.com/free-online-tools/random-username-generator?utm_source=devto&amp;amp;utm_medium=organic&amp;amp;utm_campaign=nov_10&amp;amp;utm_term=bh&amp;amp;utm_content=free_online_tools" rel="noopener noreferrer"&gt;Random Username Generator&lt;/a&gt;! Quick, easy, and fun.&lt;/strong&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2&gt;
  
  
  Flakiness Conundrum in Test Execution
&lt;/h2&gt;

&lt;p&gt;Picture the below scenario that can cause nightmares to engineer:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;The test code is all implemented and working fine, even when they are integrated in the CI/CD pipeline! Voila, CI/CD shows all green. 🙂Hell breaks loose when you realize that the same test suite fails in the next release without a single line of code change.&lt;br&gt;
 Time to don the hat of a debugger to locate and fix issues that seem to never exit in the first place .🙁&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Now, the integral question that needs an immediate answer is, &lt;em&gt;Why did the passing tests fail all of a sudden?&lt;/em&gt; It’s time to dissect the problem. First, upon debugging, you realize the pipeline configuration and network setup are all fine. Time to dig further to debug the underlying issue 🤔&lt;/p&gt;

&lt;p&gt;Well, you finally realize that test flakiness was introduced due to locator changes in the front end (or UI).&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.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%2AVUpPlnAptzZSrsUw.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.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%2AVUpPlnAptzZSrsUw.png" width="613" height="407"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://www.everydayunittesting.com/wp-content/uploads/2022/10/6tt91v1.jpg" rel="noopener noreferrer"&gt;*Overhead costs due to flaky tests&lt;/a&gt;*&lt;/p&gt;

&lt;p&gt;The stability, maintainability, and reliability of the regression test suite(s) will increase multi-fold if the intermittent failures are &lt;em&gt;healed&lt;/em&gt; before they make their way into &lt;a href="https://www.lambdatest.com/learning-hub/continuous-testing?utm_source=devto&amp;amp;utm_medium=organic&amp;amp;utm_campaign=nov_10&amp;amp;utm_term=bh&amp;amp;utm_content=learning_hub" rel="noopener noreferrer"&gt;Continuous Testing&lt;/a&gt; with CI/CD. In the scenario documented earlier, flakiness was due to the changes in the locators in the updated UI.&lt;/p&gt;

&lt;p&gt;Apart from this, intermittent issues (e.g., slow response times, network flakiness, etc.) with external dependencies are some of the ‘external’ factors that can cause flakiness. &lt;strong&gt;&lt;em&gt;Determinism&lt;/em&gt;&lt;/strong&gt; is a fundamental property of any automation test suite, which means that the execution result shouldn’t change if there are no changes in the source code.&lt;/p&gt;

&lt;p&gt;Irrespective of the source of flakiness, the bottom line is that it needs to be identified and fixed, or else it impacts release cycles along with the &lt;a href="https://www.lambdatest.com/blog/how-do-you-calculate-your-roi-on-test-automation-with-selenium/?utm_source=devto&amp;amp;utm_medium=organic&amp;amp;utm_campaign=nov_10&amp;amp;utm_term=bh&amp;amp;utm_content=blog" rel="noopener noreferrer"&gt;ROI of test automation&lt;/a&gt;. In a nutshell, there needs to be a definitive answer to the flakiness conundrum since the impact of flaky tests is not limited to only test execution!&lt;/p&gt;

&lt;h2&gt;
  
  
  Major Causes of Test Flakiness
&lt;/h2&gt;

&lt;p&gt;It turns out that developers (and QA engineers) spend more time maintaining, testing, and securing existing code than developing new code. In a nutshell, close to one-third of the time is spent on ‘Code Maintenance’!&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.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%2AScyg881DmrMWUFqE.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.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%2AScyg881DmrMWUFqE.png" width="620" height="577"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://cdn.thenewstack.io/media/2019/10/5f19d577-tidelift-how-developers-spend-time-graphic.png" rel="noopener noreferrer"&gt;*How Developers Spend Their Time&lt;/a&gt;*&lt;/p&gt;

&lt;p&gt;And flakiness in tests also contributes to the maintenance and longevity aspects of the test suites (or test scenarios). An ACM Survey on Flaky Tests revealed that close to 59 percent of developers have encountered flay tests daily, monthly, or weekly. As aptly mentioned in the Costs &amp;amp; Consequences section of the survey:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;False alarms are the dominant consequence of flaky tests.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Akin to test failures, flaky test failures should not be ignored.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Frequent flaky tests can be highly threatening to the efficiency of the CI.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Now that we know that test flakiness can have a detrimental effect on the CI, release cycles, and developer’s productivity, let’s look at some of the common causes of flaky tests:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Protect your sensitive information with our &lt;a href="https://www.lambdatest.com/free-online-tools/hash-calculator?utm_source=devto&amp;amp;utm_medium=organic&amp;amp;utm_campaign=nov_10&amp;amp;utm_term=bh&amp;amp;utm_content=free_online_tools" rel="noopener noreferrer"&gt;Hash Calculator&lt;/a&gt;. Create secure, one-way hashes in no time and keep your data safe from hackers.&lt;/strong&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2&gt;
  
  
  Frequent Application Changes
&lt;/h2&gt;

&lt;p&gt;You would have faced the “Inconsistent test failures” syndrome when automating UI interactions using frameworks like Selenium. Well, these tests abruptly start failing without any changes in the code under test.&lt;/p&gt;

&lt;p&gt;Usage of unreliable &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_10&amp;amp;utm_term=bh&amp;amp;utm_content=blog" rel="noopener noreferrer"&gt;locators in Selenium&lt;/a&gt; or changes in locators owing to UI changes could result in intermittent test failures. Major overhaul in the UI is not referred to as UI changes. The changes could be minor updates in the UI and/or locators that make the tests unreliable &amp;amp; flaky.&lt;/p&gt;

&lt;p&gt;Consider a &lt;a href="https://github.com/hjsblogger/auto-healing-with-selenium/tree/main/local-project/dist/index.html" rel="noopener noreferrer"&gt;demo login page&lt;/a&gt; that lets users sign up &amp;amp; login using email and social accounts (i.e., LinkedIn, X, and Facebook). Load the page by triggering the &lt;em&gt;php -S localhost:8080&lt;/em&gt; command on the terminal.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.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%2AhURnYs6xnFYqbJ0J.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.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%2AhURnYs6xnFYqbJ0J.png" width="800" height="298"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Let’s consider a simple scenario where UI interactions are automated for the &lt;em&gt;email-address&lt;/em&gt; element:&lt;/p&gt;

&lt;h3&gt;
  
  
  Before the locator changes
&lt;/h3&gt;

&lt;p&gt;&lt;em&gt;By.id(“email”)&lt;/em&gt; is used for locating the &lt;em&gt;email-address&lt;/em&gt; element using the &lt;em&gt;ID&lt;/em&gt; locator.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.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%2ADRg13as91q17mqAA.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.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%2ADRg13as91q17mqAA.png" width="800" height="204"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.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%2AYpQTMwuCvyl9hhfo.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.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%2AYpQTMwuCvyl9hhfo.png" width="800" height="501"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Here is how &lt;a href="https://www.lambdatest.com/blog/findelement-and-findelements-in-selenium/" rel="noopener noreferrer"&gt;*findElement *in Selenium&lt;/a&gt; is used for entering the email address in the newly located element:&lt;/p&gt;

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

&lt;h3&gt;
  
  
  After the locator changes
&lt;/h3&gt;

&lt;p&gt;Consider a hypothetical (yet realistic) scenario where the ID of the &lt;em&gt;email-address&lt;/em&gt; element is changed from “email” to “email-address” during the design and/or code updation process.&lt;/p&gt;

&lt;p&gt;For demonstration purposes, we have changed the ID by invoking the &lt;em&gt;document.getElementById()&lt;/em&gt; method on the Inspect console.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.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%2AItm4Qmwu0MuzRKcA.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.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%2AItm4Qmwu0MuzRKcA.png" width="800" height="208"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.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%2ANRwOdPEl03VMoYK-.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.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%2ANRwOdPEl03VMoYK-.png" width="800" height="614"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;As seen above, the updated ID of the &lt;em&gt;email-address&lt;/em&gt; element is “email-address”. The repercussion of this small change is huge, as the &lt;em&gt;ID&lt;/em&gt; passed to the &lt;em&gt;By&lt;/em&gt; class needs an update for the test execution to succeed:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Locating element using old ID&lt;/strong&gt; 👇&lt;/p&gt;

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

&lt;p&gt;&lt;strong&gt;Locating element using new ID&lt;/strong&gt;&lt;/p&gt;

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

&lt;p&gt;In case the test is executed with the old ID, it would result in a &lt;em&gt;NoSuchElementException&lt;/em&gt;, which eventually leads to the failure of the entire regression test suite. You can check out our comprehensive 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_10&amp;amp;utm_term=bh&amp;amp;utm_content=blog" rel="noopener noreferrer"&gt;Selenium Exceptions&lt;/a&gt; that deep dives into all the common exceptions you might encounter during automated testing.&lt;/p&gt;

&lt;p&gt;The example being highlighted above just scratches the surface when it comes to test flakiness! It can have a snowball effect on the entire test suite(s), test releases, as well as CI. Auto healing of locators can be instrumental in overcoming the brittleness caused by static locators. We will look at auto healing in Selenium in the further sections of the blog.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Make your data more secure with our easy-to-use &lt;a href="https://www.lambdatest.com/free-online-tools/crc32b-hash-calculator?utm_source=devto&amp;amp;utm_medium=organic&amp;amp;utm_campaign=nov_10&amp;amp;utm_term=bh&amp;amp;utm_content=free_online_tools" rel="noopener noreferrer"&gt;CRC32B Hash Calculator&lt;/a&gt;. Create secure hashes easily and keep your information safe from prying eyes. Get started now!&lt;/strong&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2&gt;
  
  
  Flaky Locator Strategy
&lt;/h2&gt;

&lt;p&gt;There is an option to choose from a range of locators, like ID, Name, CSS Selector, XPath, etc., for locating elements in the DOM. Though you have options, choosing the best-suited locators for the automation tests is recommended. This helps improve the reliability, maintainability, and stability of the test suites.&lt;/p&gt;

&lt;p&gt;Over &amp;amp; above, you also need to take care of the challenges posed by dynamic website content and &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_10&amp;amp;utm_term=bh&amp;amp;utm_content=blog" rel="noopener noreferrer"&gt;lazy loading&lt;/a&gt; of images. For instance, the &lt;a href="https://ecommerce-playground.lambdatest.io/?utm_source=devto&amp;amp;utm_medium=organic&amp;amp;utm_campaign=nov_10&amp;amp;utm_term=bh&amp;amp;utm_content=webpage" rel="noopener noreferrer"&gt;LambdaTest eCommerce playground&lt;/a&gt; has a significant number of images that are loaded using the lazy loading technique.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.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%2AwKLEWA_oFOIBID7t.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.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%2AwKLEWA_oFOIBID7t.png" width="800" height="538"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Hence, it is recommended to choose dynamic locators that can accommodate the dynamic nature of the website content. You can check out the &lt;a href="https://www.lambdatest.com/blog/locators-in-selenium-webdriver-with-examples/#BestPractices?utm_source=devto&amp;amp;utm_medium=organic&amp;amp;utm_campaign=nov_10&amp;amp;utm_term=bh&amp;amp;utm_content=blog" rel="noopener noreferrer"&gt;Selenium locators strategy&lt;/a&gt; blog that deep-dives into the best practices for using locators with Selenium WebDriver.&lt;/p&gt;

&lt;h2&gt;
  
  
  Synchronization Issues
&lt;/h2&gt;

&lt;p&gt;Several external factors (e.g., network throttling, dependency on external services, etc.) can contribute to test flakiness. To counter such problems, you need to invest in improving the robustness and reliability of the tests.&lt;/p&gt;

&lt;p&gt;Handling asynchronous calls in Selenium is also important since AJAX requests, animations, dynamic elements, async calls to other layers, etc., are common in today’s applications. As stated in the ACM Survey on Flaky tests, asynchronous waits are a major contributor to flakiness in front-end testing.&lt;/p&gt;

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

&lt;p&gt;Close to 37 percent of commits under the asynchronous waits category were attributed to incorrect handling of waits for asynchronous calls. For instance, using &lt;a href="https://www.lambdatest.com/blog/sleep-java-method/?utm_source=devto&amp;amp;utm_medium=organic&amp;amp;utm_campaign=nov_10&amp;amp;utm_term=bh&amp;amp;utm_content=blog" rel="noopener noreferrer"&gt;*Thread.sleep() *in Selenium&lt;/a&gt; is a bad practice since it is a blocking call that will eventually elongate the overall test execution time.&lt;/p&gt;

&lt;p&gt;Using Explicit Waits in conjunction with &lt;a href="https://www.lambdatest.com/blog/expected-conditions-in-selenium-examples/?utm_source=devto&amp;amp;utm_medium=organic&amp;amp;utm_campaign=nov_10&amp;amp;utm_term=bh&amp;amp;utm_content=blog" rel="noopener noreferrer"&gt;Expected Conditions in Selenium&lt;/a&gt; can be leveraged such that the test waits for a certain duration until the specified condition is met. Fluent Wait is another option for handling synchronization issues, particularly when the tests invoke asynchronous calls and intend to wait for it to finish before evaluating assertions.&lt;/p&gt;

&lt;p&gt;Covering all the causes is beyond the scope of this blog! Apart from the causes mentioned above, here are some of the common causes of test flakiness:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;Flaky test infrastructure&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Inconsistency across third-party libraries&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Flaky test scripts&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Data dependencies&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;You can refer to &lt;a href="https://www.lambdatest.com/blog/flaky-selenium-test-suite/#cause-of-flaky?utm_source=devto&amp;amp;utm_medium=organic&amp;amp;utm_campaign=nov_10&amp;amp;utm_term=bh&amp;amp;utm_content=blog" rel="noopener noreferrer"&gt;common causes of test flakiness&lt;/a&gt; that delves into every pointer in great detail. For the remaining part of the blog, we will focus on how auto healing in Selenium can be leveraged to enhance the robustness of test suites by handling unexpected errors arising due to frequent UI changes.&lt;/p&gt;

&lt;p&gt;Note: Feature and approach are used interchangeably throughout the remaining course of this blog.&lt;/p&gt;

&lt;h2&gt;
  
  
  What is Auto Healing in Selenium?
&lt;/h2&gt;

&lt;p&gt;Auto healing automation is also referred to as self healing automation. Auto healing of Selenium locators (or Selenium) is a dynamic approach in test automation that helps overcome the brittleness associated with traditional static locators.&lt;/p&gt;

&lt;p&gt;Traditional Selenium tests without the auto healing feature would fail in case there is an underlying change in the web locators. Auto healing brings much-needed intelligence to the automated tests, thereby enabling them to dynamically adapt to the changes in the document (i.e., application/website).&lt;/p&gt;

&lt;p&gt;This is driven by state-of-the-art AI (Artificial Intelligence) and ML (Machine Learning) algorithms.&lt;/p&gt;

&lt;p&gt;This further helps in the automatic recovery of the tests from certain failures that are encountered during the process of test execution. The automated test recovery catalyzed by the auto healing approach also improves the overall reliability of the CI pipeline.&lt;/p&gt;

&lt;p&gt;A loose yet relevant analogy:&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Akin to applying bandages on a wound, auto (or self) healing automatically heals the tests when they encounter a wound (i.e., error).&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;In the latter sections of the blog, we will focus on the nuances of auto healing in Selenium and how automation engineers can leverage the Auto Heal feature (on LambdaTest) to improve the resilience and robustness of the tests.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Looking for a quick way to generate hashes? Try our &lt;a href="https://www.lambdatest.com/free-online-tools/md4-hash-calculator?utm_source=devto&amp;amp;utm_medium=organic&amp;amp;utm_campaign=nov_10&amp;amp;utm_term=bh&amp;amp;utm_content=free_online_tools" rel="noopener noreferrer"&gt;MD4 Hash Calculator&lt;/a&gt; to get strong hash values for your data and protect your information from cyber threats.&lt;/strong&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2&gt;
  
  
  How does Auto Healing in Selenium Work?
&lt;/h2&gt;

&lt;p&gt;Though there are a few AI-powered open-source libraries for auto healing, we would be limiting our discussion to auto healing feature on LambdaTest. Let’s have a bird’s-eye view at the internal working of the said functionality (auto healing in Selenium):&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;Auto healing in Selenium on LambdaTest intelligently adapts locators by combining attributes and the test’s context for ensuring the test’s stability and resilience&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;The document (i.e., web page) is continuously scanned during runtime for any changes in the DOM&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Once the element is successfully located, the DOM path of the element is fetched and saved for future reference&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;In case the element is referenced using the same locator (but not present on the page), the auto healing algorithm generates new locators for the elements using the earlier baseline&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;In the example shown below, there is a minor change in the structure of the DOM where the image (i.e. &lt;em&gt;img&lt;/em&gt;) is under a different &lt;em&gt;div&lt;/em&gt;. Existing baseline (on the left) is updated with the new tags ahead of the Image with id &lt;em&gt;‘test-image’&lt;/em&gt;. From the next run onwards, the snapshot on the right will be used as the baseline by the auto healing algorithm.&lt;/p&gt;

&lt;p&gt;Automation test suite impacted with such minor changes will be auto healed by the auto healing algorithm.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.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%2A8h8rMrUK3r8dg1Dy.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.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%2A8h8rMrUK3r8dg1Dy.png" width="800" height="479"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Unlike the earlier example, here the DOM structure still remains the same but the ID of &lt;em&gt;img&lt;/em&gt; is changed from &lt;em&gt;test-image&lt;/em&gt; to &lt;em&gt;test-image1&lt;/em&gt;. The said locator will be auto healed during the process of test execution.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.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%2AfnT76QI2tiwgJrsk.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.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%2AfnT76QI2tiwgJrsk.png" width="800" height="479"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;In the scenario highlighted in the earlier section, there was no change in the DOM tree (or page layout), except that the &lt;em&gt;ID&lt;/em&gt; locator of the email element was changed from &lt;em&gt;email to email-address&lt;/em&gt;.&lt;/p&gt;

&lt;p&gt;*&lt;em&gt;Locating element using old ID *&lt;/em&gt;👇&lt;/p&gt;

&lt;p&gt;Here, the element can be located using the &lt;em&gt;ID as email&lt;/em&gt;.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.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%2AznNrEd04SEalId-a.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.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%2AznNrEd04SEalId-a.png" width="800" height="501"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Snapshot from Inspect Tools below:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.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%2Azi0auesrROhhgWlg.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.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%2Azi0auesrROhhgWlg.png" width="800" height="400"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Need to create MD5 hashes quickly? Our &lt;a href="https://www.lambdatest.com/free-online-tools/md5-hash-calculator?utm_source=devto&amp;amp;utm_medium=organic&amp;amp;utm_campaign=nov_10&amp;amp;utm_term=bh&amp;amp;utm_content=free_online_tools" rel="noopener noreferrer"&gt;MD5 hash calculator&lt;/a&gt; create reliable, one-way hashes quickly and keep your information secure. Try it out now!&lt;/strong&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;&lt;strong&gt;Locating &amp;amp; Healing element using updated ID&lt;/strong&gt; 👇&lt;br&gt;
Here, only the &lt;em&gt;ID&lt;/em&gt; of the email element is changed to &lt;em&gt;email-address&lt;/em&gt; whereas&lt;br&gt;
the layout of the DOM tree remains unchanged. Once the test scenarios are executed, the locator (i.e. &lt;em&gt;ID&lt;/em&gt;) will be healed from &lt;em&gt;email-address to email&lt;/em&gt;.&lt;/p&gt;

&lt;p&gt;The healing is possible, since the snapshot of the earlier test execution is available for AI-powered DOM comparison. Hence, the respective locator will be healed and the execution will result in a pass!&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.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%2A9hHDk8ImBbZb_4IO.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.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%2A9hHDk8ImBbZb_4IO.png" width="800" height="614"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Snapshot from Inspect Tools below:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.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%2ABaxicFHwFcgjry5z.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.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%2ABaxicFHwFcgjry5z.png" width="800" height="352"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Both the execution cycles will pass since the locators are healed during the process of test execution! To summarize, auto healing in Selenium automatically recovers the tests from failures encountered due to changes in locators. This results in reduced test flakiness and improved test reliability &amp;amp; stability!&lt;/p&gt;

&lt;p&gt;Now, let’s walk the talk with a demonstration of auto healing in Selenium on the LambdaTest cloud grid!&lt;/p&gt;

&lt;h2&gt;
  
  
  Demonstration: Auto Healing in Selenium
&lt;/h2&gt;

&lt;p&gt;Before we head on to the demonstration of auto healing in Selenium, here is an important caveat that requires attention:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Auto healing can reduce test flakiness, but the overall increase in the test execution time largely depends on the overall layout of the DOM tree.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Now that we have covered the essentials of auto healing in Selenium, let’s get our hands dirty with a few examples. We will be using Selenium Java for the implementation of the test scenarios. Without further ado, let’s get started!&lt;/p&gt;

&lt;p&gt;For demonstration, we would initially execute the test scenarios below by setting the &lt;em&gt;autoHeal&lt;/em&gt; capability to &lt;em&gt;true&lt;/em&gt;. The rest of the capabilities can be set using the &lt;a href="https://www.lambdatest.com/capabilities-generator/?utm_source=devto&amp;amp;utm_medium=organic&amp;amp;utm_campaign=nov_10&amp;amp;utm_term=bh&amp;amp;utm_content=webpage" rel="noopener noreferrer"&gt;LambdaTest Capabilities Generator&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.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%2A4zII4MMK5caBmQXU.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.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%2A4zII4MMK5caBmQXU.png" width="800" height="388"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Test Scenarios
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;Test Scenario — 1&lt;/strong&gt;&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Navigate to the &lt;a href="https://ecommerce-playground.lambdatest.io/index.php?route=product/category&amp;amp;path=57?utm_source=devto&amp;amp;utm_medium=organic&amp;amp;utm_campaign=nov_10&amp;amp;utm_term=bh&amp;amp;utm_content=webpage" rel="noopener noreferrer"&gt;eCommerce Playground Products Page&lt;/a&gt;.&lt;/li&gt;
&lt;li&gt;Scroll until the product “MacBook Air” is located.&lt;/li&gt;
&lt;li&gt;Click the “Buy Now” button.&lt;/li&gt;
&lt;li&gt;Assert if the “Check out page” is not shown.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;&lt;strong&gt;Test Scenario — 2&lt;/strong&gt;&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Navigate to the &lt;a href="https://ecommerce-playground.lambdatest.io/index.php?route=account/register?utm_source=devto&amp;amp;utm_medium=organic&amp;amp;utm_campaign=nov_10&amp;amp;utm_term=bh&amp;amp;utm_content=webpage" rel="noopener noreferrer"&gt;eCommerce Playground Registration Page&lt;/a&gt;.&lt;/li&gt;
&lt;li&gt;Enter the required details on the page.&lt;/li&gt;
&lt;li&gt;Click the “Continue” button.&lt;/li&gt;
&lt;li&gt;Assert if the “User registration is not successful”.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;&lt;strong&gt;Test Scenario — 3&lt;/strong&gt;&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Navigate to the Development Page.&lt;/li&gt;
&lt;li&gt;Click on the “SIGN UP” button.&lt;/li&gt;
&lt;li&gt;Enter the required details on the page.&lt;/li&gt;
&lt;li&gt;Navigate to the Development Page within iFrame&lt;/li&gt;
&lt;li&gt;Switch to the iFrame.&lt;/li&gt;
&lt;li&gt;Enter the required registration details and click “Sign Up”.&lt;/li&gt;
&lt;li&gt;Assert if the “Signup is not successful”.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;We would have two test execution cycles:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;&lt;p&gt;Execution with unchanged locators&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Execution with locators that have changed during development&lt;/p&gt;&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;The second execution cycle should result in the healing of the locators, i.e., even though the earlier locators (e.g., Name, ID, etc.) on the page are changed, they would still be healed by the auto (or self) healing feature on LambdaTest.&lt;/p&gt;

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

&lt;p&gt;Shown below is the overall directory structure:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.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%2AG5B14EWKKbd2dGus.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.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%2AG5B14EWKKbd2dGus.png" width="680" height="1144"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The &lt;strong&gt;local-project&lt;/strong&gt; folder consists of the sign-up page, which will be used for localhost testing using the &lt;a href="https://www.lambdatest.com/local-page-testing?utm_source=devto&amp;amp;utm_medium=organic&amp;amp;utm_campaign=nov_10&amp;amp;utm_term=bh&amp;amp;utm_content=webpage" rel="noopener noreferrer"&gt;LambdaTest tunnel&lt;/a&gt; and Auto Heal feature on LambdaTest. The page is inspired by the &lt;a href="https://codepen.io/FlorinPop17/pen/vPKWjd" rel="noopener noreferrer"&gt;Signup Form&lt;/a&gt; by &lt;a href="https://www.florin-pop.com/" rel="noopener noreferrer"&gt;Florin Pop&lt;/a&gt;; thanks for it. 🙂&lt;/p&gt;

&lt;p&gt;The &lt;strong&gt;setup&lt;/strong&gt; package (in &lt;em&gt;src/main/java&lt;/em&gt; folder) consists of a single file named &lt;em&gt;DriverManager.java&lt;/em&gt;. It contains wrapper functions for instantiating Remote WebDriver, setting up implicit timeouts, and more.&lt;/p&gt;

&lt;p&gt;Lastly, the &lt;strong&gt;test/java&lt;/strong&gt; folder consists of test files (&lt;em&gt;OrgTest.java&lt;/em&gt; and &lt;em&gt;AutoHealingTest.java&lt;/em&gt;) that contain almost the same test methods with a tinge of difference. &lt;em&gt;AutoHealingTest.java&lt;/em&gt; contains test methods where some locators are intentionally changed using &lt;a href="https://www.lambdatest.com/blog/how-to-use-javascriptexecutor-in-selenium-webdriver/?utm_source=devto&amp;amp;utm_medium=organic&amp;amp;utm_campaign=nov_10&amp;amp;utm_term=bh&amp;amp;utm_content=blog" rel="noopener noreferrer"&gt;JavaScript Executor in Selenium WebDriver&lt;/a&gt;. This is where auto healing will be done by the auto hеaling feature on LambdaTest. We will dive more deeper in the &lt;em&gt;Implementation&lt;/em&gt; section of this blog on auto healing in Selenium.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Looking for a reliable&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=nov_10&amp;amp;utm_term=bh&amp;amp;utm_content=free_online_tools" rel="noopener noreferrer"&gt; SHA1 hash calculator&lt;/a&gt;? Our tool generates secure, one-way hashes that you can trust. Create secure hashes quickly with just a few clicks.&lt;/strong&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2&gt;
  
  
  Implementation [POM, Wrapper Functions]
&lt;/h2&gt;

&lt;p&gt;Before we kick start the implementation of auto healing in Selenium, let’s add the required project dependencies in the project POM file.&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;&amp;lt;?xml version="1.0" encoding="UTF-8"?&amp;gt;
&amp;lt;project xmlns="http://maven.apache.org/POM/4.0.0"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"&amp;gt;
    &amp;lt;modelVersion&amp;gt;4.0.0&amp;lt;/modelVersion&amp;gt;
    &amp;lt;groupId&amp;gt;org.example&amp;lt;/groupId&amp;gt;
    &amp;lt;artifactId&amp;gt;AutoHealingTest&amp;lt;/artifactId&amp;gt;
    &amp;lt;version&amp;gt;1.0-SNAPSHOT&amp;lt;/version&amp;gt;

    &amp;lt;properties&amp;gt;
        &amp;lt;maven.compiler.source&amp;gt;1.8&amp;lt;/maven.compiler.source&amp;gt;
        &amp;lt;maven.compiler.target&amp;gt;1.8&amp;lt;/maven.compiler.target&amp;gt;
    &amp;lt;/properties&amp;gt;

    &amp;lt;dependencies&amp;gt;
        &amp;lt;dependency&amp;gt;
            &amp;lt;groupId&amp;gt;org.testng&amp;lt;/groupId&amp;gt;
            &amp;lt;artifactId&amp;gt;testng&amp;lt;/artifactId&amp;gt;
            &amp;lt;version&amp;gt;7.6.1&amp;lt;/version&amp;gt;
            &amp;lt;scope&amp;gt;test&amp;lt;/scope&amp;gt;
        &amp;lt;/dependency&amp;gt;
        &amp;lt;dependency&amp;gt;
            &amp;lt;groupId&amp;gt;org.slf4j&amp;lt;/groupId&amp;gt;
            &amp;lt;artifactId&amp;gt;slf4j-nop&amp;lt;/artifactId&amp;gt;
            &amp;lt;version&amp;gt;1.7.28&amp;lt;/version&amp;gt;
            &amp;lt;scope&amp;gt;test&amp;lt;/scope&amp;gt;
        &amp;lt;/dependency&amp;gt;
        &amp;lt;dependency&amp;gt;
            &amp;lt;groupId&amp;gt;org.seleniumhq.selenium&amp;lt;/groupId&amp;gt;
            &amp;lt;artifactId&amp;gt;selenium-java&amp;lt;/artifactId&amp;gt;
            &amp;lt;version&amp;gt;4.7.0&amp;lt;/version&amp;gt;
        &amp;lt;/dependency&amp;gt;
        &amp;lt;dependency&amp;gt;
            &amp;lt;groupId&amp;gt;org.seleniumhq.selenium&amp;lt;/groupId&amp;gt;
            &amp;lt;artifactId&amp;gt;selenium-chrome-driver&amp;lt;/artifactId&amp;gt;
            &amp;lt;version&amp;gt;4.7.0&amp;lt;/version&amp;gt;
        &amp;lt;/dependency&amp;gt;
        &amp;lt;dependency&amp;gt;
            &amp;lt;groupId&amp;gt;junit&amp;lt;/groupId&amp;gt;
            &amp;lt;artifactId&amp;gt;junit&amp;lt;/artifactId&amp;gt;
            &amp;lt;version&amp;gt;4.12&amp;lt;/version&amp;gt;
            &amp;lt;scope&amp;gt;test&amp;lt;/scope&amp;gt;
        &amp;lt;/dependency&amp;gt;
        &amp;lt;dependency&amp;gt;
            &amp;lt;groupId&amp;gt;org.apache.httpcomponents&amp;lt;/groupId&amp;gt;
            &amp;lt;artifactId&amp;gt;httpclient&amp;lt;/artifactId&amp;gt;
            &amp;lt;version&amp;gt;4.5.13&amp;lt;/version&amp;gt;
        &amp;lt;/dependency&amp;gt;
        &amp;lt;!-- https://mvnrepository.com/artifact/com.github.sdrss/reportng --&amp;gt;
        &amp;lt;dependency&amp;gt;
            &amp;lt;groupId&amp;gt;org.uncommons&amp;lt;/groupId&amp;gt;
            &amp;lt;artifactId&amp;gt;reportng&amp;lt;/artifactId&amp;gt;
            &amp;lt;version&amp;gt;1.1.4&amp;lt;/version&amp;gt;
        &amp;lt;/dependency&amp;gt;
        &amp;lt;dependency&amp;gt;
            &amp;lt;groupId&amp;gt;com.google.inject&amp;lt;/groupId&amp;gt;
            &amp;lt;artifactId&amp;gt;guice&amp;lt;/artifactId&amp;gt;
            &amp;lt;version&amp;gt;4.2.2&amp;lt;/version&amp;gt;
        &amp;lt;/dependency&amp;gt;
        &amp;lt;dependency&amp;gt;
            &amp;lt;groupId&amp;gt;org.apache.maven.plugins&amp;lt;/groupId&amp;gt;
            &amp;lt;artifactId&amp;gt;maven-surefire-plugin&amp;lt;/artifactId&amp;gt;
            &amp;lt;version&amp;gt;3.0.0-M5&amp;lt;/version&amp;gt;
            &amp;lt;type&amp;gt;maven-plugin&amp;lt;/type&amp;gt;
        &amp;lt;/dependency&amp;gt;
        &amp;lt;!-- https://mvnrepository.com/artifact/io.github.bonigarcia/webdrivermanager --&amp;gt;
        &amp;lt;dependency&amp;gt;
            &amp;lt;groupId&amp;gt;io.github.bonigarcia&amp;lt;/groupId&amp;gt;
            &amp;lt;artifactId&amp;gt;webdrivermanager&amp;lt;/artifactId&amp;gt;
            &amp;lt;version&amp;gt;5.3.0&amp;lt;/version&amp;gt;
        &amp;lt;/dependency&amp;gt;
        &amp;lt;!-- https://mvnrepository.com/artifact/org.seleniumhq.selenium/selenium-manager --&amp;gt;
        &amp;lt;dependency&amp;gt;
            &amp;lt;groupId&amp;gt;org.seleniumhq.selenium&amp;lt;/groupId&amp;gt;
            &amp;lt;artifactId&amp;gt;selenium-manager&amp;lt;/artifactId&amp;gt;
            &amp;lt;version&amp;gt;4.7.0&amp;lt;/version&amp;gt;
        &amp;lt;/dependency&amp;gt;
    &amp;lt;/dependencies&amp;gt;

    &amp;lt;build&amp;gt;
        &amp;lt;defaultGoal&amp;gt;install&amp;lt;/defaultGoal&amp;gt;
        &amp;lt;plugins&amp;gt;
            &amp;lt;plugin&amp;gt;
                &amp;lt;artifactId&amp;gt;maven-compiler-plugin&amp;lt;/artifactId&amp;gt;
                &amp;lt;version&amp;gt;3.7.0&amp;lt;/version&amp;gt;
                &amp;lt;configuration&amp;gt;
                    &amp;lt;source&amp;gt;1.8&amp;lt;/source&amp;gt;
                    &amp;lt;target&amp;gt;1.8&amp;lt;/target&amp;gt;
                &amp;lt;/configuration&amp;gt;
            &amp;lt;/plugin&amp;gt;
            &amp;lt;plugin&amp;gt;
                &amp;lt;groupId&amp;gt;org.apache.maven.plugins&amp;lt;/groupId&amp;gt;
                &amp;lt;artifactId&amp;gt;maven-surefire-plugin&amp;lt;/artifactId&amp;gt;
                &amp;lt;version&amp;gt;3.0.0-M5&amp;lt;/version&amp;gt;
                &amp;lt;configuration&amp;gt;
                    &amp;lt;properties&amp;gt; &amp;lt;property&amp;gt;
                        &amp;lt;name&amp;gt;usedefaultlisteners&amp;lt;/name&amp;gt;
                        &amp;lt;value&amp;gt;false&amp;lt;/value&amp;gt;
                    &amp;lt;/property&amp;gt;
                        &amp;lt;property&amp;gt;
                            &amp;lt;name&amp;gt;listener&amp;lt;/name&amp;gt;
                            &amp;lt;value&amp;gt;org.uncommons.reportng.HTMLReporter,
                                org.uncommons.reportng.JUnitXMLReporter
                            &amp;lt;/value&amp;gt;
                        &amp;lt;/property&amp;gt; &amp;lt;/properties&amp;gt;
                    &amp;lt;!-- Trigger file from command-line --&amp;gt;
                    &amp;lt;!--&amp;lt;suiteXmlFiles&amp;gt;
                        &amp;lt;suiteXmlFile&amp;gt;testng_autohealing.xml&amp;lt;/suiteXmlFile&amp;gt;
                    &amp;lt;/suiteXmlFiles&amp;gt;
                    --&amp;gt;

                    &amp;lt;suiteXmlFiles&amp;gt;
                        &amp;lt;!-- pass testng.xml files as argument from command line --&amp;gt;
                        &amp;lt;suiteXmlFile&amp;gt;${suiteXml}&amp;lt;/suiteXmlFile&amp;gt;
                    &amp;lt;/suiteXmlFiles&amp;gt;
                &amp;lt;/configuration&amp;gt;
            &amp;lt;/plugin&amp;gt;
            &amp;lt;plugin&amp;gt;
                &amp;lt;groupId&amp;gt;org.apache.maven.plugins&amp;lt;/groupId&amp;gt;
                &amp;lt;artifactId&amp;gt;maven-surefire-report-plugin&amp;lt;/artifactId&amp;gt;
                &amp;lt;version&amp;gt;3.0.0-M5&amp;lt;/version&amp;gt;
            &amp;lt;/plugin&amp;gt;
        &amp;lt;/plugins&amp;gt;
    &amp;lt;/build&amp;gt;
&amp;lt;/project&amp;gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;With the configuration details all set up, let’s look into the wrapper functions and test methods that we looked into earlier in the blog.&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;/* Source Code inspiration: Selenium4POC: https://bit.ly/ManagerSelenium */

package setup;

import org.openqa.selenium.JavascriptExecutor;
import org.openqa.selenium.WebDriver;
import org.openqa.selenium.chrome.ChromeDriver;
import org.openqa.selenium.chrome.ChromeOptions;
import org.openqa.selenium.firefox.FirefoxDriver;
import org.openqa.selenium.firefox.FirefoxOptions;
import org.openqa.selenium.remote.RemoteWebDriver;

import java.net.MalformedURLException;
import java.net.URL;
import java.time.Duration;
import java.util.HashMap;

import static java.text.MessageFormat.format;

public class DriverManager
{
    private static final ThreadLocal&amp;lt;WebDriver&amp;gt; driverLocal = new ThreadLocal&amp;lt;&amp;gt; ();
    /* Get UserName and Access Key from https://accounts.lambdatest.com/security */
    /* Use getEnv if env variables are exported using export or set */
    private static final String LT_USERNAME= System.getenv("LT_USERNAME");
    private static final String LT_ACCESS_KEY = System.getenv("LT_ACCESS_KEY");
    private static final String GRID_URL = "@hub.lambdatest.com/wd/hub";
    public static String status = "passed";

    public static void quitDriver() {
        if (null != getDriver()) {
            ((JavascriptExecutor) getDriver()).executeScript("lambda-status=" + status);
            getDriver().quit();
        }
    }

    public static void createDriver(final String browser) {
        if (browser.equalsIgnoreCase("chrome")) {
            setupChromeDriver();
        } else if (browser.equalsIgnoreCase("firefox")) {
            setupFirefoxDriver();
        } else if (browser.equalsIgnoreCase("remote-chrome")) {
            setupRemoteChromeDriver();
        } else {
            System.out.println("Browser driver is not available!");
        }
        setupBrowserTimeouts();
    }

    public static WebDriver getDriver () {
        return driverLocal.get();
    }

    private static void setupChromeDriver () {
        final ChromeOptions options = new ChromeOptions ();
        options.addArguments ("--no-sandbox");
        options.addArguments ("--disable-dev-shm-usage");
        options.addArguments ("--window-size=1050,600");
        options.addArguments("--safebrowsing-disable-download-protection");
        driverLocal.set(new ChromeDriver(options));
    }

    private static void setupFirefoxDriver () {

        final FirefoxOptions options = new FirefoxOptions ();
        options.addArguments ("--no-sandbox");
        options.addArguments ("--disable-dev-shm-usage");
        options.addArguments("--window-size=1050,600");
        driverLocal.set(new FirefoxDriver(options));
    }

    private static void setupRemoteChromeDriver ()
    {
        final ChromeOptions browserOptions = new ChromeOptions();
        final HashMap&amp;lt;String, Object&amp;gt; ltOptions = new HashMap&amp;lt;String, Object&amp;gt;();

        browserOptions.setPlatformName ("Windows 11");
        ltOptions.put ("username", LT_USERNAME);
        ltOptions.put ("accessKey", LT_ACCESS_KEY);
        ltOptions.put ("resolution", "2560x1440");
        ltOptions.put ("build", "[Build] Demo: Auto-healing using Selenium on LambdaTest");
        ltOptions.put ("name", "Demo: Auto-healing using Selenium on LambdaTest");
        ltOptions.put ("project", "[Project] Auto-Healing Functionality");
        ltOptions.put ("plugin", "java-testNG");
        ltOptions.put("ACCEPT_INSECURE_CERTS", false);
        ltOptions.put("ACCEPT_SSL_CERTS", false);
        ltOptions.put("tunnel", true);
        ltOptions.put ("w3c", true);
        ltOptions.put("autoHeal", true);

        browserOptions.setCapability ("LT:Options", ltOptions);

        try
        {
            driverLocal.set(new RemoteWebDriver(
                    new URL(format("https://{0}:{1}{2}", LT_USERNAME, LT_ACCESS_KEY, GRID_URL)),
                        browserOptions));
        }
        catch (final MalformedURLException e)
        {
            throw new Error(e);
        }
    }

    private static void setupBrowserTimeouts () {
        WebDriver driver = driverLocal.get();
        driver.manage ()
              .timeouts ()
              .implicitlyWait (Duration.ofSeconds (30));
    }
}
&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 source code for this file is inspired by the Selenium4POC repo. To ensure that the WebDriver instance is thread-safe, the &lt;em&gt;ThreadLocal&lt;/em&gt; class is used for creating and managing thread-local variables. Also, thread-local variables for WebDriver instances are created since all the test scenarios are executed in parallel on LambdaTest Selenium Grid.&lt;/p&gt;

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

&lt;p&gt;The &lt;em&gt;getDriver()&lt;/em&gt; method returns the current thread’s value of this thread-local.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.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%2APgHuo1vJUH1jWFcJ.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.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%2APgHuo1vJUH1jWFcJ.png" width="800" height="377"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The &lt;em&gt;setupRemoteChromeDriver()&lt;/em&gt; method lets you set the options (i.e., &lt;em&gt;browserName, browserVersion, platformName, etc&lt;/em&gt;.). Since we are also doing localhost testing, the &lt;em&gt;tunnel&lt;/em&gt; option (or capability) is set to &lt;em&gt;true&lt;/em&gt;.&lt;/p&gt;

&lt;p&gt;The &lt;em&gt;autoHeal&lt;/em&gt; option (or capability) is set to &lt;em&gt;true&lt;/em&gt; so that tests are auto healed and automatically recover from certain failures encountered during test execution.&lt;/p&gt;

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

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Say goodbye to the guesswork, get an accurate count of lines for your code or text with our Line Counter tool. Get accurate &lt;a href="https://www.lambdatest.com/free-online-tools/lines-count?utm_source=devto&amp;amp;utm_medium=organic&amp;amp;utm_campaign=nov_10&amp;amp;utm_term=bh&amp;amp;utm_content=free_online_tools" rel="noopener noreferrer"&gt;lines count&lt;/a&gt; today!&lt;/strong&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Since the test execution is done on the LambdaTest cloud grid, the &lt;a href="https://www.lambdatest.com/blog/selenium-remotewebdriver/?utm_source=devto&amp;amp;utm_medium=organic&amp;amp;utm_campaign=nov_10&amp;amp;utm_term=bh&amp;amp;utm_content=blog" rel="noopener noreferrer"&gt;RemoteWebDriver class in Selenium&lt;/a&gt; is used for instantiating respective browsers on the cloud. &lt;em&gt;BrowserOptions, LambdaTest user-name &amp;amp; access-key&lt;/em&gt;, and *Grid URL *are also passed as parameters to the instance of the *RemoteWebDriver *class. You can find the LambdaTest credentials from the &lt;a href="https://accounts.lambdatest.com/security?utm_source=devto&amp;amp;utm_medium=organic&amp;amp;utm_campaign=nov_10&amp;amp;utm_term=bh&amp;amp;utm_content=webpage" rel="noopener noreferrer"&gt;LambdaTest Accounts Page&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.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%2AvBPonzWYZZIW_qIt.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.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%2AvBPonzWYZZIW_qIt.png" width="800" height="271"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.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%2A2q5wMnBUe5-GZrjv.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.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%2A2q5wMnBUe5-GZrjv.png" width="800" height="400"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;After the driver is set, an implicit wait of 30 seconds is set using the &lt;em&gt;setupBrowserTimeouts()&lt;/em&gt; method.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fcdn-images-1.medium.com%2Fmax%2F3120%2F0%2A8_tdZQ0S-m1gKR2q.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fcdn-images-1.medium.com%2Fmax%2F3120%2F0%2A8_tdZQ0S-m1gKR2q.png" width="800" height="235"&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 org.testng.annotations.AfterClass;
import org.testng.annotations.BeforeClass;
import org.testng.annotations.Parameters;
import static setup.DriverManager.createDriver;
import static setup.DriverManager.quitDriver;

public class BaseTest
{
    @BeforeClass(alwaysRun = true)
    @Parameters({"browser"})
    public void setupTest (final String browser)
    {
        createDriver(browser);
    }

    @AfterClass(alwaysRun = true)
    public void tearDown ()
    {
        quitDriver ();
    }
}
&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;em&gt;setupTest()&lt;/em&gt; is a parameterized method that takes &lt;em&gt;browser&lt;/em&gt; as the input parameter, which is in-turn passed from the TestNG xml file (&lt;em&gt;i.e. xml/testng_autohealing.xml or xml/testng_org.xml&lt;/em&gt;). The other parameter (&lt;em&gt;i.e., testurl&lt;/em&gt;) is used directly inside the parameterized test method(s).&lt;/p&gt;

&lt;p&gt;The &lt;em&gt;alwaysRun&lt;/em&gt; attribute used along with the &lt;em&gt;@BeforeClass&lt;/em&gt; annotation indicates that the annotated method [&lt;em&gt;i.e., setupTest()&lt;/em&gt;] must be executed even if test methods inside the class fail or throw exceptions.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.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%2AamyIwbZetQdrF8ym.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.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%2AamyIwbZetQdrF8ym.png" width="800" height="327"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;On successful execution, you should have the Chrome browser instantiated on the LambdaTest cloud grid.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.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%2Av6TU-TqluYXYb1LF.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.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%2Av6TU-TqluYXYb1LF.png" width="800" height="400"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;On similar lines, the &lt;em&gt;tearDown()&lt;/em&gt; method implemented under the &lt;em&gt;@AfterClass&lt;/em&gt; annotation is invoked once all the test methods in a test class have run. You can refer to the &lt;a href="https://www.lambdatest.com/blog/complete-guide-on-testng-annotations-for-selenium-webdriver/?utm_source=devto&amp;amp;utm_medium=organic&amp;amp;utm_campaign=nov_10&amp;amp;utm_term=bh&amp;amp;utm_content=blog" rel="noopener noreferrer"&gt;TestNG Annotations tutorial&lt;/a&gt; for a quick refresher about these annotations!&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.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%2Aiz7emzGCX5UM1UzJ.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.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%2Aiz7emzGCX5UM1UzJ.png" width="734" height="502"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.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%2AzdnqrWq8j4gwpo6S.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.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%2AzdnqrWq8j4gwpo6S.png" width="734" height="502"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Want to decode a messy URL? Unleash the full potential of your website with our online &lt;a href="https://www.lambdatest.com/free-online-tools/url-parse?utm_source=devto&amp;amp;utm_medium=organic&amp;amp;utm_campaign=nov_10&amp;amp;utm_term=bh&amp;amp;utm_content=free_online_tools" rel="noopener noreferrer"&gt;URL Parse&lt;/a&gt; tool. Optimize your website’s URLs to improve your site’s visibility,&lt;/strong&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2&gt;
  
  
  Implementation [Test Methods]
&lt;/h2&gt;

&lt;p&gt;For simplification, we have separated the test methods into two files (located in the *test/java *folder):&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;OrgTest.java:&lt;/strong&gt; Contains implementation with the original locators in place. Hence, you would observe no auto healing when executing tests in the file.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;AutoHealingTest.java:&lt;/strong&gt; Contains implementation with the new locators in place. Hence, you would observe the locators being auto-healed when executing test methods in the file.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Since the blog focuses more on auto healing in Selenium, we won’t be delving deeper into Selenium locator-specific implementation. Let’s get started…&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;import org.openqa.selenium.By;
import org.openqa.selenium.JavascriptExecutor;
import org.openqa.selenium.WebDriver;
import org.openqa.selenium.WebElement;
import org.openqa.selenium.support.ui.ExpectedConditions;
import org.openqa.selenium.support.ui.WebDriverWait;
import org.testng.Assert;
import org.testng.annotations.BeforeClass;
import org.testng.annotations.Parameters;
import org.testng.annotations.Test;
import org.openqa.selenium.interactions.Actions;

import java.time.Duration;
import static setup.DriverManager.getDriver;
import static setup.DriverManager.status;

public class OrgTest extends BaseTest {
    //WebDriver driver = null;
    private WebDriverWait webDriverWait;
    private Actions actionChains;
    JavascriptExecutor jsExecutor;
    int ELEM_TIMEOUT_DUR = 10;

    @BeforeClass
    @Parameters({"testurl"})
    public void navigateToWebsite(final String testURL) throws InterruptedException
    {
        WebDriver driver = getDriver();

        webDriverWait = new WebDriverWait(driver,
                Duration.ofSeconds(ELEM_TIMEOUT_DUR));
        jsExecutor = (JavascriptExecutor) driver;
        actionChains = new Actions(driver);
        driver.get(testURL);

        driver.manage().window().maximize();
        /* Recommend using explicit wait */
        /* Blocking wait used only for testing */
        Thread.sleep(2000);
    }

    @Test(description = "Test 1: Auto Healing on LambdaTest - Ecommerce Registration Page",
            enabled=true)
    public void testAutoHealing_ecommerce_cart() throws InterruptedException
    {
        WebDriver driver = getDriver();
        try
        {
            /* Avoid scrolling, directly scroll into the view */
            /*
            long start_height = (long) jsExecutor.executeScript("return document.documentElement.scrollHeight;");
            jsExecutor.executeScript("window.scrollTo(0, " + start_height + ")");
            Thread.sleep(1000);
             */

            /* New locator works, healed logic is not working fine. This needs to be commented later */
            WebElement elemMacBook = driver.findElement(By.id(
                    "mz-product-grid-image-44-212408"));

            Thread.sleep(1000);

            ((JavascriptExecutor) driver).executeScript("arguments[0].scrollIntoView(true);", elemMacBook);
            Thread.sleep(500);

            actionChains.moveToElement(elemMacBook).perform();

            try
            {
                Thread.sleep(2000);
            } catch (InterruptedException e)
            {
                e.printStackTrace();
            }

            WebElement elemQuickView = driver.findElement(By.xpath(
                    "//button[@class='btn btn-quick-view quick-view-44']"));
            webDriverWait.until(ExpectedConditions.elementToBeClickable(elemQuickView));

            Thread.sleep(1000);
            elemQuickView.click();

            WebElement elemBuyNowButton;
            elemBuyNowButton = driver.findElement(By.id(
                    "entry_212965"));
            webDriverWait.until(ExpectedConditions.elementToBeClickable(elemBuyNowButton));

            Thread.sleep(2000);

            elemBuyNowButton = driver.findElement(By.id("entry_212965"));
            elemBuyNowButton.click();

            WebElement elemContinueButton = driver.findElement(By.cssSelector(
                    "#button-save"));
            webDriverWait.until(ExpectedConditions.elementToBeClickable(elemContinueButton));

            /* We can do some checks later, raise assert if failure */
            String curURL = driver.getCurrentUrl();
            boolean statusURL = curURL.contains("checkout");
            Assert.assertTrue(statusURL,
                    "Checkout page is displayed");
        }
        catch (Exception e)
        {
            System.out.println(e.getMessage());
            status = "failed";
        }
        Thread.sleep(2000);
    }

    @Test(description = "Test 2: Auto-Healing on E-Commerce Playground Registration Page",
          enabled=true)
    public void testAutoHealing_ecomm_registration() throws InterruptedException
    {
        WebDriver driver = getDriver();

        try
        {
            WebElement elemFirstName = driver.findElement(By.id("input-firstname"));
            elemFirstName.sendKeys("Testing");

            WebElement elemLastName = driver.findElement(By.id("input-lastname"));
            elemLastName.sendKeys("Testing12345");

            By newEmailAddr = By.id("input-email");
            WebElement tempElement = driver.findElement(newEmailAddr);
            tempElement.sendKeys("testingemail4@gmail.com");

            /* Not a good practice, should be avoided. Used only for testing */
            Thread.sleep(1000);

            /* If auto-healing is applied, below mail will be entered in the said location */
            WebElement elemEmail = driver.findElement(By.id("input-email"));
            elemEmail.clear();
            Thread.sleep(1000);
            elemEmail.sendKeys("testingemail5@gmail.com");

            Thread.sleep(1000);

            WebElement elemTel = driver.findElement(By.xpath("//input[@id='input-telephone']"));
            elemTel.sendKeys("12345678");


            WebElement elemPass = driver.findElement(By.name("password"));
            elemPass.sendKeys("password");

            WebElement elemPassConf = driver.findElement(By.name("confirm"));
            elemPassConf.sendKeys("password");

            By newNewsletter = By.id("input-newsletter-yes");
            WebElement tempElementNewsletter = driver.findElement(newNewsletter);
            jsExecutor.executeScript("arguments[0].checked = true;",
                    tempElementNewsletter);

            Thread.sleep(3000);

            WebElement elemNewsletter = driver.findElement(By.id("input-newsletter-yes"));
            jsExecutor.executeScript("arguments[0].checked = false;", elemNewsletter);

            WebElement elemNewsletterNo = driver.findElement(By.id("input-newsletter-no"));
            jsExecutor.executeScript("arguments[0].checked = true;", elemNewsletterNo);

            WebElement elemAgree = driver.findElement(By.cssSelector("[for='input-agree']"));
            elemAgree.click();

            Thread.sleep(2000);

            WebElement elemSubmit = driver.findElement
                        (By.xpath("//input[@class='btn btn-primary']"));
            elemSubmit.click();

            webDriverWait.until(d -&amp;gt; ((JavascriptExecutor) driver).
                    executeScript("return document.readyState").toString().equals
                    ("complete"));

            String curURL = driver.getCurrentUrl();
            boolean statusURL = curURL.contains("contains");
            Assert.assertFalse(statusURL,
                    "Success URL is not displayed");
        }
        catch (Exception e)
        {
            System.out.println(e.getMessage());
            status = "failed";
        }
        Thread.sleep(2000);
    }

    @Test(description = "Test 3: [Local Page] Auto Healing on LambdaTest", enabled=true)
    public void testAutoHealing_local_page() throws InterruptedException
    {
        WebDriver driver = getDriver();

        try
        {
            WebElement elemSignUp = driver.findElement(By.id("signUp"));
            elemSignUp.click();

            WebElement elemFirstName = driver.findElement(By.name("first-name"));
            webDriverWait.until(ExpectedConditions.visibilityOf(elemFirstName));

            /* Clear the existing text and enter the details */
            elemFirstName.clear();
            elemFirstName.sendKeys("Himanshu Blogger");

            Thread.sleep(2000);

            /* Scenario 2: The iFrame contains the page where the ID's are changed */
            driver.get("http://localhost:8080/iframe.html");

            webDriverWait.until(d -&amp;gt; ((JavascriptExecutor) driver).
                    executeScript("return document.readyState").toString().equals
                            ("complete"));

            driver.switchTo().frame("iframe_1");
            Thread.sleep(2000);

            /* Healing 3 : ID of the button was changed earlier */
            WebElement elemSignUpButton = driver.findElement(By.id("signUp"));
            elemSignUpButton.click();

            /* Healing 4 : Name of the button was changed in the base HTML file */
            WebElement elemModFirstName = driver.findElement(By.name("first-name"));
            elemModFirstName.sendKeys("Himanshu Sheth");

            WebElement elemEmail = driver.findElement(By.name("email"));
            elemEmail.sendKeys("himanshu.blogger@gmail.com");
            Thread.sleep(500);

            WebElement elemNewPwd = driver.findElement(By.className("password"));
            elemNewPwd.sendKeys("Password");
            Thread.sleep(500);

            /* Click Sign-up button */
            elemSignUpButton = driver.findElement(By.cssSelector(".sign-up-container button"));
            elemSignUpButton.click();

            Thread.sleep(3000);

            String curURL = driver.getPageSource();
            boolean statusURL = curURL.contains("SignUp iFrame");
            Assert.assertFalse(statusURL,
                    "Sign Up was not successful");
        }
        catch (Exception e)
        {
            System.out.println(e.getMessage());
            status = "failed";
        }
        Thread.sleep(2000);
    }
}
&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 &lt;em&gt;OrgTest&lt;/em&gt; class extends &lt;em&gt;BaseTest&lt;/em&gt; that contains utility functions for creating WebDriver instance and destroying the same via the &lt;a href="https://www.lambdatest.com/blog/selenium-close-tab/?utm_source=devto&amp;amp;utm_medium=organic&amp;amp;utm_campaign=nov_10&amp;amp;utm_term=bh&amp;amp;utm_content=blog" rel="noopener noreferrer"&gt;*quit() *method in Selenium&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;To start with, we create a reference to the &lt;em&gt;JavascriptExecutor&lt;/em&gt; interface. This will be further used for executing &lt;a href="https://www.lambdatest.com/blog/how-to-use-javascriptexecutor-in-selenium-webdriver/?utm_source=devto&amp;amp;utm_medium=organic&amp;amp;utm_campaign=nov_10&amp;amp;utm_term=bh&amp;amp;utm_content=blog" rel="noopener noreferrer"&gt;JavaScript within Selenium&lt;/a&gt; script. Further, an object of the &lt;a href="https://www.lambdatest.com/blog/what-is-actions-class-in-selenium/?utm_source=devto&amp;amp;utm_medium=organic&amp;amp;utm_campaign=nov_10&amp;amp;utm_term=bh&amp;amp;utm_content=blog" rel="noopener noreferrer"&gt;&lt;em&gt;Actions&lt;/em&gt; class&lt;/a&gt; is created so that we can automate actions like mouse clicks, button clicks, and more.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.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%2AQjZfKrqldt8CxFLM.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.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%2AQjZfKrqldt8CxFLM.png" width="800" height="357"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Here, we will look into the important aspects of the test methods.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Test Method (Test Scenario — 1): testAutoHealing_ecommerce_cart&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;After navigating to the &lt;a href="https://ecommerce-playground.lambdatest.io/index.php?route=product/category&amp;amp;path=57?utm_source=devto&amp;amp;utm_medium=organic&amp;amp;utm_campaign=nov_10&amp;amp;utm_term=bh&amp;amp;utm_content=webpage" rel="noopener noreferrer"&gt;eCommerce Playground Product Page&lt;/a&gt;, the desired product (i.e., MacBook Air) is located using the&lt;a href="https://www.lambdatest.com/blog/locators-in-selenium-webdriver-with-examples/#idlocator?utm_source=devto&amp;amp;utm_medium=organic&amp;amp;utm_campaign=nov_10&amp;amp;utm_term=bh&amp;amp;utm_content=blog" rel="noopener noreferrer"&gt; &lt;em&gt;ID&lt;/em&gt; locator in Selenium&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.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%2AzjNCOHryEg-DV-Ih.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.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%2AzjNCOHryEg-DV-Ih.png" width="800" height="499"&gt;&lt;/a&gt;&lt;/p&gt;

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

&lt;p&gt;Once the element is identified (or located), we use the JS method &lt;em&gt;scrollIntoView()&lt;/em&gt; to scroll into the visible area of the located element. &lt;em&gt;scrollIntoView()&lt;/em&gt; is invoked using &lt;em&gt;executeScript()&lt;/em&gt; method of the JavascriptExecutor interface.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.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%2AWyAhMpSbFNWNTOXs.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.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%2AWyAhMpSbFNWNTOXs.png" width="800" height="163"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Once into the view, locate the &lt;em&gt;Quick View&lt;/em&gt; button using the XPath locator. We use the &lt;em&gt;elementToBeClickable&lt;/em&gt; &lt;a href="https://www.lambdatest.com/blog/expected-conditions-in-selenium-examples/?utm_source=devto&amp;amp;utm_medium=organic&amp;amp;utm_campaign=nov_10&amp;amp;utm_term=bh&amp;amp;utm_content=blog" rel="noopener noreferrer"&gt;ExpectedCondition in Selenium&lt;/a&gt; to wait until the element is not clickable. Once clickable, the element is clicked by invoking the Click method.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.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%2Au5MAl88Of7Dl-cg1.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.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%2Au5MAl88Of7Dl-cg1.png" width="800" height="377"&gt;&lt;/a&gt;&lt;/p&gt;

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

&lt;p&gt;The &lt;em&gt;Buy Now&lt;/em&gt; button is located using the ID locator, and a click is performed to navigate to the next page.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.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%2AXKJ_CRtkcBOE95Yi.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.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%2AXKJ_CRtkcBOE95Yi.png" width="800" height="525"&gt;&lt;/a&gt;&lt;/p&gt;

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

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Unescape your JSON strings with just a few clicks using our easy-to-use online &lt;a href="https://www.lambdatest.com/free-online-tools/json-unescape?utm_source=devto&amp;amp;utm_medium=organic&amp;amp;utm_campaign=nov_10&amp;amp;utm_term=bh&amp;amp;utm_content=free_online_tools" rel="noopener noreferrer"&gt;JSON Unescape&lt;/a&gt; tool. Get accurate results in no time. Try it out now.&lt;/strong&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Finally, locate the Continue button (i.e., &lt;em&gt;ID = button-save&lt;/em&gt;) on the page using the ID locator. An assert is raised in case the URL does not contain checkout.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.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%2Ahi0awn8v0qoKeKIO.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.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%2Ahi0awn8v0qoKeKIO.png" width="800" height="627"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.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%2AUYQsPLVMc5DTTu-w.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.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%2AUYQsPLVMc5DTTu-w.png" width="800" height="400"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Test Method (Test Scenario — 2): testAutoHealing_ecomm_registration&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;After navigating the &lt;a href="https://ecommerce-playground.lambdatest.io/index.php?route=account/register?utm_source=devto&amp;amp;utm_medium=organic&amp;amp;utm_campaign=nov_10&amp;amp;utm_term=bh&amp;amp;utm_content=webpage" rel="noopener noreferrer"&gt;eCommerce Playground Registration page&lt;/a&gt;, the respective elements are located using ID, XPath, and other locators. As shown below, the first name element is located using the ID locator, and &lt;a href="https://www.lambdatest.com/blog/how-to-use-selenium-sendkeys?utm_source=devto&amp;amp;utm_medium=organic&amp;amp;utm_campaign=nov_10&amp;amp;utm_term=bh&amp;amp;utm_content=blog" rel="noopener noreferrer"&gt;&lt;em&gt;sendKeys&lt;/em&gt; in Selenium&lt;/a&gt; is used for entering details in the text box.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.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%2AZ17Snjl55qj_gIQW.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.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%2AZ17Snjl55qj_gIQW.png" width="800" height="628"&gt;&lt;/a&gt;&lt;/p&gt;

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

&lt;p&gt;&lt;em&gt;Subscription to newsletter&lt;/em&gt; is marked as NO by locating the respective Radio button. Since there are multiple elements of &lt;em&gt;input-newsletter&lt;/em&gt;-no in the DOM, &lt;em&gt;arguments[0].checked&lt;/em&gt; is used for toggling the state of the first argument (i.e., reference to the Radio button).&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.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%2Atvtg0vlStcxdiuon.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.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%2Atvtg0vlStcxdiuon.png" width="800" height="642"&gt;&lt;/a&gt;&lt;/p&gt;

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

&lt;p&gt;Now that all the required entries are filled, the Submit button is clicked. The &lt;em&gt;Document.readyState&lt;/em&gt; property is checked for completion (i.e., &lt;em&gt;complete&lt;/em&gt;), after which a check is performed to verify if the required page has opened.&lt;/p&gt;

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

&lt;p&gt;&lt;strong&gt;Test Method (Test Scenario — 3): testAutoHealing_local_page&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;This test is performed on a &lt;a href="https://github.com/hjsblogger/auto-healing-with-selenium/tree/main/local-project" rel="noopener noreferrer"&gt;document (or page)&lt;/a&gt; under development. Once you have downloaded its source code, navigate to the &lt;em&gt;dist&lt;/em&gt; folder and trigger php -S localhost:8080 command on the terminal.&lt;/p&gt;

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

&lt;p&gt;Navigate to localhost:8080 from the web browser.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.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%2A2tD4TEgyRr5eySJ2.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.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%2A2tD4TEgyRr5eySJ2.png" width="800" height="531"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;a href="https://www.lambdatest.com/free-online-tools/json-validator?utm_source=devto&amp;amp;utm_medium=organic&amp;amp;utm_campaign=nov_10&amp;amp;utm_term=bh&amp;amp;utm_content=free_online_tools" rel="noopener noreferrer"&gt;**JSON Validator&lt;/a&gt; is a free and easy-to-use tool to validate JSON data, which makes it easier to fix the issue by providing error line and character position.**&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Since this is the case of localhost testing, we need to start the &lt;em&gt;tunnel&lt;/em&gt;. To do the same, download &lt;a href="https://www.lambdatest.com/support/docs/underpass-tunnel-application/?utm_source=devto&amp;amp;utm_medium=organic&amp;amp;utm_campaign=nov_10&amp;amp;utm_term=bh&amp;amp;utm_content=support_doc" rel="noopener noreferrer"&gt;Underpass from LambdaTest&lt;/a&gt; to establish an SSH tunnel between the OS &amp;amp; LambdaTest cloud server(s).&lt;/p&gt;

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

&lt;p&gt;With this, we are all geared to test the local pages on LambdaTest! First, the SIGN UP button on the page is located using the &lt;em&gt;ID&lt;/em&gt; locator. Once located, the button is clicked using the &lt;em&gt;Click()&lt;/em&gt; method.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.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%2AYxrd75XYs01tiSXn.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.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%2AYxrd75XYs01tiSXn.png" width="800" height="620"&gt;&lt;/a&gt;&lt;/p&gt;

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

&lt;p&gt;Required details like First Name, Email, and Password are entered using the same approach we have discussed. As a part of the next test, we navigate to &lt;a href="http://localhost:8080/iframe.html" rel="noopener noreferrer"&gt;http://localhost:8080/iframe.html&lt;/a&gt;, where the iFrame is a container of the earlier page (i.e., index.html). The intent of creating this page is to verify if auto healing of locators is possible within iFrames.&lt;/p&gt;

&lt;p&gt;To get started, we first switch to the iFrame by invoking the &lt;em&gt;switchTo()&lt;/em&gt; method of Selenium WebDriver. If you are new to iFrames, check the tutorial on &lt;a href="https://www.lambdatest.com/blog/handling-frames-and-iframes-selenium-c-sharp/?utm_source=devto&amp;amp;utm_medium=organic&amp;amp;utm_campaign=nov_10&amp;amp;utm_term=bh&amp;amp;utm_content=blog" rel="noopener noreferrer"&gt;handling iFrames with Selenium&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.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%2A-wqjPaONBh1CD4On.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.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%2A-wqjPaONBh1CD4On.png" width="800" height="646"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Once we switch to the iFrame, the combination of &lt;em&gt;findElement()&lt;/em&gt; and &lt;em&gt;sendKeys()&lt;/em&gt; methods is used for automating interactions with the elements in the DOM.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.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%2AiGzZp2qGA2j5dCWN.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.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%2AiGzZp2qGA2j5dCWN.png" width="800" height="353"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;With this, we are all set to execute the tests on the LambdaTest platform! It is essential to note that we have set the &lt;em&gt;autoHeal&lt;/em&gt; capability to &lt;em&gt;true&lt;/em&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Execution [No Auto Healing]
&lt;/h2&gt;

&lt;p&gt;First, export the environment variables &lt;em&gt;LT_USERNAME&lt;/em&gt; and &lt;em&gt;LT_ACCESS_KEY&lt;/em&gt; by adding them in the &lt;a href="https://github.com/hjsblogger/auto-healing-with-selenium/blob/main/Makefile" rel="noopener noreferrer"&gt;Makefile&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.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%2A9FfvPSaI-U_VoQ0q.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.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%2A9FfvPSaI-U_VoQ0q.png" width="800" height="267"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;With this, we are all set to trigger the tests via the Makefile. Run &lt;em&gt;make clean&lt;/em&gt; to clean any output/target directories.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.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%2AQk0X-PPEBvFdjMHy.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.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%2AQk0X-PPEBvFdjMHy.png" width="800" height="222"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Run &lt;em&gt;make set-env&lt;/em&gt; to export the desired environment variables (i.e., &lt;em&gt;LT_USERNAME&lt;/em&gt; and &lt;em&gt;LT_ACCESS_KEY&lt;/em&gt;).&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.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%2AQK-bhWBqxjdSPEOs.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.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%2AQK-bhWBqxjdSPEOs.png" width="754" height="68"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;For simplification, we have two separate TestNG XML (i.e., &lt;a href="https://github.com/hjsblogger/auto-healing-with-selenium/blob/main/xml/testng_org.xml" rel="noopener noreferrer"&gt;testng_org.xml&lt;/a&gt; &amp;amp; &lt;a href="https://github.com/hjsblogger/auto-healing-with-selenium/blob/main/xml/testng_autohealing.xml" rel="noopener noreferrer"&gt;testng_autohealing.xml&lt;/a&gt;) files to demonstrate auto healing in Selenium.&lt;/p&gt;

&lt;p&gt;Run &lt;em&gt;make org-test&lt;/em&gt; to execute tests with locators that have not been modified during development. Hence, we will not observe any auto healing of locators in this test execution cycle.&lt;/p&gt;

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

&lt;p&gt;As seen below, the test execution is triggered with all three tests running in parallel on LambdaTest Selenium Grid.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.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%2AaQ1qwlC83ATRxFk3.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.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%2AaQ1qwlC83ATRxFk3.png" width="800" height="400"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Log on to the &lt;a href="https://automation.lambdatest.com/build?utm_source=devto&amp;amp;utm_medium=organic&amp;amp;utm_campaign=nov_10&amp;amp;utm_term=bh&amp;amp;utm_content=webpage" rel="noopener noreferrer"&gt;LambdaTest Automation Dashboard&lt;/a&gt; to check the status of the test execution.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.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%2ATlu_MT2F9DFMcw2q.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.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%2ATlu_MT2F9DFMcw2q.png" width="800" height="322"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;As seen below, the test execution was successful, and no auto healing was done since there was no change in the locators.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.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%2AvbQyzHgauvM_QenX.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.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%2AvbQyzHgauvM_QenX.png" width="800" height="444"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.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%2Aw3R1A4jjOkFSoFef.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.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%2Aw3R1A4jjOkFSoFef.png" width="800" height="355"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Things are getting exciting.🙂Now, let’s change a few locators using the &lt;em&gt;executeScript()&lt;/em&gt; method in Selenium that helps execute JavaScript in the context of the currently selected frame (or window). Let’s set the ball rolling!&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;&lt;a href="https://www.lambdatest.com/free-online-tools/html-validator?utm_source=devto&amp;amp;utm_medium=organic&amp;amp;utm_campaign=nov_10&amp;amp;utm_term=bh&amp;amp;utm_content=free_online_tools" rel="noopener noreferrer"&gt;HTML Validator&lt;/a&gt; is an online tool that checks HTML syntax and quality. Improve your website’s performance and functionality with ease.&lt;/strong&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2&gt;
  
  
  Implementation Changes [for Auto Healing demonstration]
&lt;/h2&gt;

&lt;p&gt;As mentioned earlier in the above sections, we have kept the auto-healing tests in a separate file so that it becomes easy to differentiate between them! Here are the important code touch-points:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;&lt;a href="https://github.com/hjsblogger/auto-healing-with-selenium/blob/main/src/test/java/AutoHealingTest.java" rel="noopener noreferrer"&gt;Auto Healing tests&lt;/a&gt;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;a href="https://github.com/hjsblogger/auto-healing-with-selenium/blob/main/xml/testng_autohealing.xml" rel="noopener noreferrer"&gt;Auto Healing TestNG XML&lt;/a&gt;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;a href="https://github.com/hjsblogger/auto-healing-with-selenium/blob/main/Makefile#L14" rel="noopener noreferrer"&gt;Auto Healing (Makefile)&lt;/a&gt;&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Let’s change locators in the &lt;strong&gt;first test scenario&lt;/strong&gt; by replacing the ID &lt;em&gt;entry_212965&lt;/em&gt; to &lt;em&gt;buy_later&lt;/em&gt; on the &lt;a href="https://ecommerce-playground.lambdatest.io/index.php?route=product/category&amp;amp;path=57?utm_source=devto&amp;amp;utm_medium=organic&amp;amp;utm_campaign=nov_10&amp;amp;utm_term=bh&amp;amp;utm_content=webpage" rel="noopener noreferrer"&gt;eCommerce Playground Product Page&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.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%2AIultYRDg-Uz9WSx_.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.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%2AIultYRDg-Uz9WSx_.png" width="800" height="635"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;For demonstration, we execute the command on the DevTools console. As seen below, the ID of the said element is changed to &lt;em&gt;buy_later&lt;/em&gt;.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.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%2AbT_Z7_tVIiNMwFjI.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.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%2AbT_Z7_tVIiNMwFjI.png" width="800" height="538"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.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%2A8M39k_Ev1ZlI0Jd6.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.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%2A8M39k_Ev1ZlI0Jd6.png" width="800" height="635"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;To mimic the same change via code, we first locate the element using the &lt;em&gt;ID&lt;/em&gt; locator. Once the element is located, its ID is changed using the &lt;em&gt;getElementById()&lt;/em&gt; method of the Document interface.&lt;/p&gt;

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

&lt;p&gt;Now, here comes the magic of the auto healing logic. 🤔Instead of using the new locator (i.e. &lt;em&gt;buy_later&lt;/em&gt;), we still use the old locator (i.e. &lt;em&gt;entry_212965&lt;/em&gt;) in the test implementation.&lt;/p&gt;

&lt;p&gt;As the &lt;em&gt;AutoHeal&lt;/em&gt; capability on LambdaTest is set to &lt;em&gt;true&lt;/em&gt;, the respective locator will be auto healing by the auto healing algorithm. In a nutshell, test execution will still be successful!&lt;/p&gt;

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

&lt;p&gt;Let’s change locators in the &lt;strong&gt;second test scenario&lt;/strong&gt; by modifying the NAME locator from &lt;em&gt;password&lt;/em&gt; to &lt;em&gt;password-new&lt;/em&gt; on the &lt;a href="https://ecommerce-playground.lambdatest.io/index.php?route=account/register?utm_source=devto&amp;amp;utm_medium=organic&amp;amp;utm_campaign=nov_10&amp;amp;utm_term=bh&amp;amp;utm_content=webpage" rel="noopener noreferrer"&gt;eCommerce Playground Registration Page&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;As seen below, there is one element with the Name locator as a &lt;em&gt;password&lt;/em&gt;.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.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%2AQ8w32QZUja8MIqYq.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.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%2AQ8w32QZUja8MIqYq.png" width="800" height="429"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;For demonstrating auto healing in Selenium, we manually changed the Name locator for the said element from &lt;em&gt;password&lt;/em&gt; to &lt;em&gt;password-new&lt;/em&gt;.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.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%2A07Ak-qDJMm7eXB8c.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.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%2A07Ak-qDJMm7eXB8c.png" width="800" height="502"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.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%2A_rIyANkG7mw79Pov.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.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%2A_rIyANkG7mw79Pov.png" width="800" height="400"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Now that we know that the locator change is working, we use the &lt;em&gt;getElementsByName()&lt;/em&gt; method of the Document interface. Since the method returns an array, we change the Name locator of the first element of the array.&lt;/p&gt;

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

&lt;p&gt;Even though the value of the locator has changed, the earlier locator value (i.e., &lt;em&gt;password&lt;/em&gt;) is used for locating the said element. This is where the locator will be auto healed by the algorithm, and test execution still results in a success.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.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%2AF3-ycV_ydx1m31EK.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.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%2AF3-ycV_ydx1m31EK.png" width="800" height="233"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The same experiments are repeated in &lt;strong&gt;the third test scenario&lt;/strong&gt; where the ID of the Sign Up button is changed from &lt;em&gt;signUp&lt;/em&gt; to &lt;em&gt;signUp-Button&lt;/em&gt;. Also, the earlier ID(i.e. &lt;em&gt;signUp&lt;/em&gt;) is used to locate the button using its &lt;em&gt;ID&lt;/em&gt; locator.&lt;/p&gt;

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

&lt;h2&gt;
  
  
  Execution [With Auto Healing]
&lt;/h2&gt;

&lt;p&gt;The test code with modified locators is present in &lt;a href="https://github.com/hjsblogger/auto-healing-with-selenium/blob/main/src/test/java/AutoHealingTest.java" rel="noopener noreferrer"&gt;AutoHealingTest.java&lt;/a&gt;. Run the command &lt;em&gt;make auto-heal&lt;/em&gt; to execute the tests that have locators healed by the auto healing algorithm.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.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%2A3gIpFpdlXc0t3E7X.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.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%2A3gIpFpdlXc0t3E7X.png" width="800" height="400"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.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%2A1bRsdF_Dta1Fv4rk.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.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%2A1bRsdF_Dta1Fv4rk.png" width="800" height="460"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;As seen below, the test execution was successful. However, a small &lt;em&gt;bandage&lt;/em&gt; icon (for indicating healed tests) is next to the respective tests. One more noticeable change is the increase in the test execution time which can be largely attributed to the locators being healed by the algorithm!&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.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%2ATwAUf6xKYYH7kRFf.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.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%2ATwAUf6xKYYH7kRFf.png" width="800" height="400"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.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%2AMDtMaXcLhRssSwM7.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.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%2AMDtMaXcLhRssSwM7.png" width="800" height="400"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;In order to check the details of the healed locator, navigate to the respective test in the dashboard and click on the &lt;em&gt;Bandage&lt;/em&gt; icon. You should be able to view details of the locator used for healing the test.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.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%2AwOmjn6W1dVuJ-zA8.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.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%2AwOmjn6W1dVuJ-zA8.png" width="800" height="504"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;In a nutshell, auto healing should be used with caution, as there could be performance impact; the gravity of which largely depends on DOM size. Hence, it is recommended to patch the test code with the healed locator (on a case-to-case basis) to further reduce flakiness and increase stability of the tests!&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Don’t waste time debugging your YAML files. Use our free &lt;a href="https://www.lambdatest.com/free-online-tools/yaml-validator?utm_source=devto&amp;amp;utm_medium=organic&amp;amp;utm_campaign=nov_10&amp;amp;utm_term=bh&amp;amp;utm_content=free_online_tools" rel="noopener noreferrer"&gt;YAML validator&lt;/a&gt; tool to validate your YAML code quickly and identify syntax errors and fix them.&lt;/strong&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2&gt;
  
  
  Limitations of Auto Healing Code
&lt;/h2&gt;

&lt;p&gt;Now that we have looked at the major upsides of auto (or self) healing, the integral question is: &lt;em&gt;Can auto healing address all issues related to flakiness?&lt;/em&gt; Though auto healing on LambdaTest is designed to handle a wide range of issues, there are some limitations!&lt;/p&gt;

&lt;p&gt;Or should we say that the learnings of auto healed tests must be utilized in making the tests less flaky (i.e., more stable &amp;amp; reliable). Without further ado, here are some of the limitations of the auto healing functionality:&lt;/p&gt;

&lt;h2&gt;
  
  
  Non-recoverable Errors
&lt;/h2&gt;

&lt;p&gt;Auto healing cannot recover from certain failure types (e.g., WebDriver initialization errors, system-level failures, etc.)&lt;/p&gt;

&lt;h2&gt;
  
  
  Test Accuracy
&lt;/h2&gt;

&lt;p&gt;Though auto healing does an excellent job of reducing test flakiness, there is a possibility that it might be masking underlying issues in the test scripts.&lt;/p&gt;

&lt;p&gt;As mentioned earlier, it is imperative to review the logs, find the root cause of failure, and fix the test script (depending on the cause of flakiness).&lt;/p&gt;

&lt;h2&gt;
  
  
  Performance Impact
&lt;/h2&gt;

&lt;p&gt;As seen throughout test execution, auto healing can have a slight impact on test execution time.&lt;/p&gt;

&lt;p&gt;This impact can be largely attributed to the additional checks and recovery mechanisms used to recover the tests from potential failures!&lt;/p&gt;

&lt;p&gt;Apart from this, &lt;strong&gt;false positives&lt;/strong&gt; and &lt;strong&gt;false negatives&lt;/strong&gt; are some of the other limitations of auto healing tests. Hence, a lot depends on the algorithm built for detecting the cause of failure and healing the cause with the healed locator!&lt;/p&gt;

&lt;h2&gt;
  
  
  It’s Time to Heal!
&lt;/h2&gt;

&lt;p&gt;In general, auto healing functionality is built to improve the robustness and reliability of the tests. This, in turn, aids in reducing the flakiness factor in the tests!&lt;/p&gt;

&lt;p&gt;Having said that, it just temporarily heals the &lt;em&gt;wound&lt;/em&gt;, which in our case is the test (or test suite). The learnings of auto healed tests must be used for a &lt;em&gt;near-permanent&lt;/em&gt; removal of flakiness aspects of the tests. At the end of it, auto healed tests cannot replace good test design or error-handling principles.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Think of auto healing as a remedy that needs a permanent fix such that healing is no longer necessary…&lt;/em&gt;&lt;br&gt;
__&lt;/p&gt;

</description>
      <category>selenium</category>
      <category>webdev</category>
      <category>opensource</category>
      <category>testing</category>
    </item>
    <item>
      <title>Generating XML And HTML Report In PyUnit For Test Automation</title>
      <dc:creator>himanshuseth004</dc:creator>
      <pubDate>Mon, 06 Nov 2023 14:29:17 +0000</pubDate>
      <link>https://forem.com/testmuai/lwdksn-3c26</link>
      <guid>https://forem.com/testmuai/lwdksn-3c26</guid>
      <description>&lt;p&gt;Irrespective of the test framework being used, one aspect that would be of interest to the various stakeholders in the project e.g. developers, testers, project managers, etc. would be the output of the test results. Keeping a track of the progression of test suites/test cases and their corresponding results can be a daunting task. The task can become more complex in the later stages of the project since the product would have undergone various levels of testing.&lt;/p&gt;

&lt;p&gt;Automation Reports is an ideal way through which you can track progress, improve the readability of the test output thereby minimizing the amount of time required in the maintenance of the test data (&amp;amp; results). We have already covered importance &amp;amp; advantages of test reports refer to our blog on &lt;a href="https://www.lambdatest.com/blog/pytest-report-generation-for-selenium-automation-scripts/?utm_source=devto&amp;amp;utm_medium=organic&amp;amp;utm_campaign=nov_06&amp;amp;utm_term=bh&amp;amp;utm_content=blog" rel="noopener noreferrer"&gt;report generation in pytest&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;In this article, I will be talking about generating HTML reports in PyUnit (also known as unittest), a popular test &lt;a href="https://www.lambdatest.com/blog/top-python-frameworks-in-2020-for-selenium-test-automation/?utm_source=devto&amp;amp;utm_medium=organic&amp;amp;utm_campaign=nov_06&amp;amp;utm_term=bh&amp;amp;utm_content=blog" rel="noopener noreferrer"&gt;Python testing framework&lt;/a&gt; that is widely used to execute unit testing of a Selenium Python test suite.&lt;/p&gt;

&lt;p&gt;If you’re new to Selenium and wondering what it is then we recommend checking out our guide — &lt;a href="https://www.lambdatest.com/selenium?utm_source=devto&amp;amp;utm_medium=organic&amp;amp;utm_campaign=nov_06&amp;amp;utm_term=bh&amp;amp;utm_content=webpage" rel="noopener noreferrer"&gt;What is Selenium?&lt;/a&gt;&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Looking for an easy way to convert Gray code to Decimal? Try our free online &lt;a href="https://www.lambdatest.com/free-online-tools/gray-to-decimal?utm_source=devto&amp;amp;utm_medium=organic&amp;amp;utm_campaign=nov_06&amp;amp;utm_term=bh&amp;amp;utm_content=free_online_tools" rel="noopener noreferrer"&gt;Gray to Decimal&lt;/a&gt; Converter tool to convert Gray code to Decimal in just a few seconds!&lt;/strong&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2&gt;
  
  
  PyUnit HTML Report Generation Using HTMLTestRunner
&lt;/h2&gt;

&lt;p&gt;To generate PyUnit HTML reports that have in-depth information about the tests in the HTML format, execution results, etc.; you can make use of HtmlTestRunner module in Python.&lt;/p&gt;

&lt;p&gt;There are different ways in which reports can be generated in the HTML format; however, HtmlTestRunner is widely used by the developer community. To install HtmlTestRunner module, you need to execute the following command in your terminal:&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;pip install html-testRunner
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;The entire purpose of HtmlTest runner is to save the output of your automation test execution in html file to make it much easier to interpret.&lt;/p&gt;

&lt;p&gt;Below is a snapshot of the HtmlTestRunner module installation.&lt;/p&gt;

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

&lt;p&gt;We make use of the Eclipse IDE for development purpose and the same can be downloaded from &lt;a href="https://www.eclipse.org/downloads/" rel="noopener noreferrer"&gt;here&lt;/a&gt;. You also have the option of using the Community version of the PyCharm IDE which can be downloaded from &lt;a href="https://www.jetbrains.com/pycharm/download/?utm_source=product&amp;amp;utm_medium=link&amp;amp;utm_campaign=PC&amp;amp;utm_content=2018.3#section=windows" rel="noopener noreferrer"&gt;here&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Now that you have installed HtmlTestRunner, let’s have a look at the test suite used for generating PyUnit HTML reports. The testsuite contains two test cases.&lt;/p&gt;

&lt;p&gt;a. Google Search where the search term is ‘LambdaTest’.&lt;/p&gt;

&lt;p&gt;b. Wikipedia Search where the search term is ‘Steve Jobs’.&lt;/p&gt;

&lt;h2&gt;
  
  
  Test Case A — Google Search where the search term is ‘LambdaTest’
&lt;/h2&gt;

&lt;p&gt;The filename is GoogleTest.py and the compiled Python file (GoogleTest) would be imported in the file where the TestSuite is created. For more information on the setUp() and tearDown() methods, please have a look at the earlier articles that have covered PyUnit/unittest in more detail.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Filename — GoogleTest.py (Compiled Python File — GoogleTest)&lt;/strong&gt;&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;import unittest
from selenium import webdriver
import time
from time import sleep

class GoogleSeachTest(unittest.TestCase):
    def setUp(self):
        self.driver = webdriver.Firefox()

    def test_GoogleSearch(self):
        driver_firefox = self.driver
        driver_firefox.maximize_window()
        driver_firefox.get('http://www.google.com')

        # Perform search operation
        elem = driver_firefox.find_element_by_name("q")
        elem.send_keys("Lambdatest")
        elem.submit()

        sleep(10)

    def tearDown(self):
        # Close the browser.
        self.driver.close()

if __name__ == '__main__':
    unittest.main()
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;
&lt;h2&gt;
  
  
  Test Case B — Wikipedia Search where the search term is ‘Steve Jobs’
&lt;/h2&gt;

&lt;p&gt;The filename is WikiTest.py and the compiled Python file (WikiTest) would be imported in the file where the TestSuite is created.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Filename — WikiTest.py (Compiled Python File — WikiTest)&lt;/strong&gt;&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;import unittest
from selenium import webdriver
import time
from time import sleep

class WikipediaSeachTest(unittest.TestCase):
    def setUp(self):
        self.driver = webdriver.Firefox()

    def test_WikipediaSearch(self):
        driver_firefox = self.driver
        driver_firefox.maximize_window()


        # Perform search operation
        driver_firefox.get('http://en.wikipedia.org')

        driver_firefox.find_element_by_id('searchInput').clear()
        driver_firefox.find_element_by_id('searchInput').send_keys('Steve Jobs')

        sleep(10)
        driver_firefox.find_element_by_id('searchButton').click()

        sleep(10)

    def tearDown(self):
        # Close the browser.
        self.driver.close()

if __name__ == '__main__':
    unittest.main()

Compiling Test Case A &amp;amp; B For Generating PyUnit HTML Reports
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;For compiling the files, you can make use of the command:&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;python GoogleTest.py
python WikiTest.py
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;We make use of the compiled Python code (GoogleTest and WikiTest) to create a testsuite that has these two test cases. We make use of the HtmlTestRunner module to create PyUnit HTML report that contains details about the tests along with the execution results.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Filename — test_html_runner_search.py&lt;/strong&gt;&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;import unittest
import GoogleTest
import WikiTest
import os

# Import the HTMLTestRunner Module
import HtmlTestRunner

# Get the Present Working Directory since that is the place where the report
# would be stored

current_directory = os.getcwd()

class HTML_TestRunner_TestSuite(unittest.TestCase):
    def test_GoogleWiki_Search(self):

        # Create a TestSuite comprising the two test cases
        consolidated_test = unittest.TestSuite()

        # Add the test cases to the Test Suite
        consolidated_test.addTests([
            unittest.defaultTestLoader.loadTestsFromTestCase(GoogleTest.GoogleSeachTest),
            unittest.defaultTestLoader.loadTestsFromTestCase(WikiTest.WikipediaSeachTest)
        ])

        output_file = open(current_directory + "\HTML_Test_Runner_ReportTest.html", "w")

        html_runner = HtmlTestRunner.HTMLTestRunner(
            stream=output_file,
            report_title='HTML Reporting using PyUnit',
            descriptions='HTML Reporting using PyUnit &amp;amp; HTMLTestRunner'
        )

        html_runner.run(consolidated_test)

if __name__ == '__main__':
    unittest.main()
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;As seen in the implementation above, we make use of the HTMLTestRunner method which is implemented in the HtmlTestRunner module (HtmlTestRunner.HTMLTestRunner). For the purpose of testing, we have passed three arguments to the HTMLTestRunner method:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.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%2At1tqH8vSyKpivynF4sWMfQ.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.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%2At1tqH8vSyKpivynF4sWMfQ.png" width="800" height="400"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Need to decode Base64 strings but don’t know how? Our &lt;a href="https://www.lambdatest.com/free-online-tools/base64-decode?utm_source=devto&amp;amp;utm_medium=organic&amp;amp;utm_campaign=nov_06&amp;amp;utm_term=bh&amp;amp;utm_content=free_online_tools" rel="noopener noreferrer"&gt;Base64 decode&lt;/a&gt; tool is the easiest way to decode Base64 strings. Simply paste your code and get decoded text in seconds.&lt;/strong&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;For more details about the HTMLTestRunner method, you can refer to runner.py that can be found in the location where HtmlTestRunner module is installed (&amp;lt; Installation Path &amp;gt;\…\venv\Lib\site-packages\runner.py).&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;class HTMLTestRunner(TextTestRunner):
    """" A test runner class that output the results. """

    time_format = "%Y-%m-%d_%H-%M-%S"

    def __init__(self, output="./reports/", verbosity=2, stream=sys.stderr,
                 descriptions=True, failfast=False, buffer=False,
                 report_title=None, report_name=None, template=None, resultclass=None,
                 add_timestamp=True, open_in_browser=False,
                 combine_reports=False, template_args=None):
        self.verbosity = verbosity
        self.output = output
        self.encoding = UTF8

        TextTestRunner.__init__(self, stream, descriptions, verbosity,
                                failfast=failfast, buffer=buffer)
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;Below is the execution snapshot of test_html_runner_search.py&lt;/p&gt;

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

&lt;p&gt;The report location &amp;amp; report name is passed as an argument to the HTMLTestRunner() method. Below is the content for the PyUnit HTML report (HTML_Test_Runner_ReportTest.html). The report contains information about the two tests that were executed as a part of the testsuite, their execution results and time taken for completion.&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Running tests... ----------------------------------------------------------------------
test_GoogleSearch (GoogleTest.GoogleSeachTest) ... OK (18.393839)s
test_WikipediaSearch (WikiTest.WikipediaSeachTest) ... OK (32.298229)s
Ran 2 tests in 0:00:50 OK Generating HTML reports... reports\TestResults_GoogleTest.GoogleSeachTest_2019-05-31_17-48-20.html reports\TestResults_WikiTest.WikipediaSeachTest_2019-05-31_17-48-20.html
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;Apart from the consolidated report shown above, there would be individual reports that would be located in the &amp;lt; output-folder &amp;gt;\reports\ folder&lt;/p&gt;

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

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;TestResults_GoogleTest.GoogleSeachTest_2019–05–31_xxxx.html
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;&lt;a href="https://media2.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%2ATxHTYpBQ62nAIAev.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.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%2ATxHTYpBQ62nAIAev.png" width="800" height="206"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;TestResults_WikiTest.WikipediaSeachTest_2019–05–31_xxxx.html
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;&lt;a href="https://media2.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%2AaOzpeRtKLH0JIkJ5.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.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%2AaOzpeRtKLH0JIkJ5.png" width="800" height="208"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  PyUnit XML Report Generation Using XMLTestRunner
&lt;/h2&gt;

&lt;p&gt;Apart from html-testrunner, you can also make use of xmlrunner in case you want to generate a PyUnit XML report. In order to use xmlrunner, you need to install the module using the following command.&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;pip install xmlrunner
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;Once the module is installed, you need to perform minimal changes in the HTML report-based implementation. We will be using the similar test cases as we used for PyUnit HTML reports, the code snippet is below:&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;import unittest
import GoogleTest
import WikiTest
import os

# Import the xmlrunner Module
import xmlrunner

# Get the Present Working Directory since that is the place where the report
# would be stored

current_directory = os.getcwd()

        ……………………………………


        ……………………………………
        output_file = open(current_directory + "\XML_Test_Runner_ReportTest", "w")

        testRunner = xmlrunner.XMLTestRunner(output=output_file)
        testRunner.run(consolidated_test)


        ……………………………………


        ……………………………………
if __name__ == '__main__':
    unittest.main()
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;
&lt;h2&gt;
  
  
  Scope Of PyUnit HTML Reports/ XML Reports For Automated Cross Browser Testing
&lt;/h2&gt;

&lt;p&gt;Now that we know how to generate a PyUnit HTML report, or PyUnit XML report, we need to understand the scope of implementation based on the automated cross browser testing needs of your project. How practical is this approach and is it going to hamper your productivity as you perform &lt;a href="https://www.lambdatest.com/selenium-automation?utm_source=devto&amp;amp;utm_medium=organic&amp;amp;utm_campaign=nov_06&amp;amp;utm_term=bh&amp;amp;utm_content=webpage" rel="noopener noreferrer"&gt;Selenium?&lt;/a&gt; Let’s find out.&lt;/p&gt;
&lt;h2&gt;
  
  
  Not So Scalable Approach
&lt;/h2&gt;

&lt;p&gt;HtmlTestRunner in conjunction with Selenium &amp;amp; PyUnit (unittest) can be used to a good extent to test the features of a web product. However, the approach may not be scalable if the testing is performed on a project/product at a large scale.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;*&lt;em&gt;Check out the &lt;a href="https://www.lambdatest.com/free-online-tools/text-to-html-entities-convertor?utm_source=devto&amp;amp;utm_medium=organic&amp;amp;utm_campaign=nov_06&amp;amp;utm_term=bh&amp;amp;utm_content=free_online_tools" rel="noopener noreferrer"&gt;Text to HTML Entities Converter&lt;/a&gt; allows it to save and share HTML Entities and convert Text to HTML Entities Numbers, which display reserved letters in HTML. *&lt;/em&gt;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h2&gt;
  
  
  Maintenance Is Going To Be A Hassle
&lt;/h2&gt;

&lt;p&gt;Maintenance of the test reports would be a strenuous task and its complexity will multiply with the growing scale of the project.&lt;/p&gt;
&lt;h2&gt;
  
  
  What If You Are Using An Automated Cross Browser Testing Cloud For Selenium Script Execution?
&lt;/h2&gt;

&lt;p&gt;With the increasing number of web browsers, operating systems, devices; performing testing on these different combinations has become essential to ensure premium end-user experience. You can improve your existing infrastructure to perform automated cross browser testing but the approach may neither be scalable, not economical. So a good idea would be to empower yourself with automated cross browser testing on cloud. You can increase the throughput from your test team by making use of parallel test execution in Selenium on a cloud-based testing infrastructure.&lt;/p&gt;

&lt;p&gt;LambdaTest is a &lt;a href="https://www.lambdatest.com/feature?utm_source=devto&amp;amp;utm_medium=organic&amp;amp;utm_campaign=nov_06&amp;amp;utm_term=bh&amp;amp;utm_content=webpage" rel="noopener noreferrer"&gt;browser compatibility testing&lt;/a&gt; tool on cloud that supports 2,000+ real browsers &amp;amp; browser versions for both mobile, and desktop. LambdaTest offers a Selenium Grid with zero-downtime, so you don’t have to worry about maintenance. All you would need is a LambdaTest account, internet connectivity, and your desired capabilities to invoke a test automation script on Selenium Grid offered by LambdaTest. You would also get the benefits of &lt;a href="https://www.lambdatest.com/integrations?utm_source=devto&amp;amp;utm_medium=organic&amp;amp;utm_campaign=nov_06&amp;amp;utm_term=bh&amp;amp;utm_content=webpage" rel="noopener noreferrer"&gt;LambdaTest integrations&lt;/a&gt; to third party tools for CI/CD, project management, codeless automation and more.&lt;/p&gt;

&lt;p&gt;Automation testing with Selenium Grid offered by LambdaTest also allows you to extract your test reports from our cloud servers to your preferred storage, without logging into LambdaTest.&lt;/p&gt;

&lt;p&gt;Meaning, you can execute your tests are written using PyUnit &amp;amp; Selenium with reports generated using popular modules like HtmlTestRunner or xmlrunner on our Selenium Grid.&lt;/p&gt;

&lt;p&gt;Sharing &amp;amp; maintaining test reports becomes easy with LambdaTest Selenium API, and you also get the convenience to perform browser compatibility testing on your locally hosted web pages, at scale.&lt;/p&gt;
&lt;h2&gt;
  
  
  Run The Existing Test Suite On LambdaTest Selenium Grid
&lt;/h2&gt;

&lt;p&gt;The first task is to port the existing implementation (WikiTest.py and GoogleTest.py) to LambdaTest Selenium Grid. The only change that is done in the implementation is porting from local webdriver to remote webdriver; the capabilities are generated using the &lt;a href="https://www.lambdatest.com/capabilities-generator/?utm_source=devto&amp;amp;utm_medium=organic&amp;amp;utm_campaign=nov_06&amp;amp;utm_term=bh&amp;amp;utm_content=webpage" rel="noopener noreferrer"&gt;Lambdatest Capability Generator.&lt;/a&gt; Shown below is the code ported to LambdaTest Selenium Grid.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Filename — WikiTest.py (Ported to Lambdatest Grid, Compiled Python File — WikiTest)&lt;/strong&gt;&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;import unittest
from selenium import webdriver
import time
from time import sleep
import warnings
import urllib3

#Set capabilities for testing on Firefox
ff_caps = {
    "build" : "Testing reporting on Lambdatest using PyUnit (Wikipedia-1)",
    "name" : "Testing reporting on Lambdatest using PyUnit (Wikipedia-1)",
    "platform" : "Windows 10",
    "browserName" : "Firefox",
    "version" : "64.0",
}

# Obtain details from https://accounts.lambdatest.com/profile
user_name = "your-user-name"
app_key = "app-key-generated-during-account-creation"

class WikipediaSeachTest(unittest.TestCase):
    def setUp(self):
        global remote_url

        urllib3.disable_warnings(urllib3.exceptions.InsecureRequestWarning)
        remote_url = "https://" + user_name + ":" + app_key + "@hub.lambdatest.com/wd/hub"

    def test_GoogleSearch(self):
        driver_firefox = webdriver.Remote(command_executor=remote_url, desired_capabilities=ff_caps)
        self.driver = driver_firefox
        driver_firefox.maximize_window()

        # Perform search operation
        driver_firefox.get('http://en.wikipedia.org')

        driver_firefox.find_element_by_id('searchInput').clear()
        driver_firefox.find_element_by_id('searchInput').send_keys('Steve Jobs')
        driver_firefox.find_element_by_id('searchButton').click()

    def tearDown(self):
        # Close the browser.
        self.driver.close()
        self.driver.quit()

if __name__ == '__main__':
    unittest.main()
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;&lt;strong&gt;Filename — GoogleTest.py (Ported to Lambdatest Grid, Compiled Python File — GoogleTest)&lt;/strong&gt;&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;import unittest
from selenium import webdriver
import time
from time import sleep
import warnings
import urllib3

#Set capabilities for testing on Firefox
ff_caps = {
    "build" : "Testing reporting on Lambdatest using PyUnit (Google)",
    "name" : "Testing reporting on Lambdatest using PyUnit (Google)",
    "platform" : "Windows 10",
    "browserName" : "Firefox",
    "version" : "64.0",
}

# Obtain details from https://accounts.lambdatest.com/profile
user_name = "your-user-name"
app_key = "app-key-generated-during-account-creation"

class GoogleSeachTest(unittest.TestCase):
    def setUp(self):
        global remote_url

        urllib3.disable_warnings(urllib3.exceptions.InsecureRequestWarning)
        remote_url = "https://" + user_name + ":" + app_key + "@hub.lambdatest.com/wd/hub"

    def test_GoogleSearch(self):
        driver_firefox = webdriver.Remote(command_executor=remote_url, desired_capabilities=ff_caps)
        self.driver = driver_firefox
        driver_firefox.maximize_window()
        driver_firefox.get('http://www.google.com')

        # Perform search operation
        elem = driver_firefox.find_element_by_name("q")
        elem.send_keys("Lambdatest")
        elem.submit()

    def tearDown(self):
        # Close the browser.
        self.driver.close()
        self.driver.quit()

if __name__ == '__main__':
    unittest.main()
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;The implementation of test_html_runner_search.py remains the same since it only contains the testsuite for execution (i.e. it is only a placeholder). Below is the screenshot of the Execution Status of these tests on the LambdaTest cloud.&lt;/p&gt;

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

&lt;blockquote&gt;
&lt;p&gt;&lt;a href="https://www.lambdatest.com/free-online-tools/binary-to-gray?utm_source=devto&amp;amp;utm_medium=organic&amp;amp;utm_campaign=nov_06&amp;amp;utm_term=bh&amp;amp;utm_content=free_online_tools" rel="noopener noreferrer"&gt;**Binary to Gray&lt;/a&gt; Code Converter is a free, easy-to-use online tool that converts a binary number into its equivalent Gray code representation in just one click.**&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2&gt;
  
  
  Using LambdaTest Selenium API For Extracting PyUnit Test Reports
&lt;/h2&gt;

&lt;p&gt;LambdaTest has provided APIs through which developers &amp;amp; testers can manage test builds, track test status, fetch logs for tests performed over a period of time, modify build related information, etc. The output of the API is in JSON (JavaScript Object Notation) format, it contains detailed information about the test environment (browser, operating system, device, etc.) along with the execution results.&lt;/p&gt;

&lt;p&gt;In order to make use of the LambdaTest APIs, you need to login to &lt;a href="https://www.lambdatest.com/support/docs/api-doc/" rel="noopener noreferrer"&gt;https://www.lambdatest.com/support/docs/api-doc/&lt;/a&gt; using your user-name and access-token. You can get user-name &amp;amp; access-token by visiting your &lt;a href="https://accounts.lambdatest.com/profile?utm_source=devto&amp;amp;utm_medium=organic&amp;amp;utm_campaign=nov_06&amp;amp;utm_term=bh&amp;amp;utm_content=webpage" rel="noopener noreferrer"&gt;profile section&lt;/a&gt;.&lt;/p&gt;

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

&lt;p&gt;You should authorize the LambdaTest API using these credentials in order to trigger GET, POST requests using the APIs. The &lt;a href="https://automation.lambdatest.com/timeline?utm_source=devto&amp;amp;utm_medium=organic&amp;amp;utm_campaign=nov_06&amp;amp;utm_term=bh&amp;amp;utm_content=webpage" rel="noopener noreferrer"&gt;test automation timeline&lt;/a&gt; will have history of the tests that you have performed so far.&lt;/p&gt;

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

&lt;p&gt;Every test execution will be associated with a unique testID, buildid, and sessionId. The URL for test execution has the format &lt;a href="https://automation.lambdatest.com/logs/?testID={test-id}&amp;amp;build={build-id}." rel="noopener noreferrer"&gt;https://automation.lambdatest.com/logs/?testID={test-id}&amp;amp;build={build-id}.&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The sessionId details can be found by visiting the Command tab for that particular testID &amp;amp; buildID, sample is shown below:&lt;/p&gt;

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

&lt;h2&gt;
  
  
  LambdaTest Build &amp;amp; Session API Demonstration
&lt;/h2&gt;

&lt;p&gt;API that can be used for detailed reporting:&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;https://api.lambdatest.com/automation/api/v1/
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;You need to append the necessary end-point to the end of the API and issue a GET/POST to get the results.&lt;/p&gt;

&lt;p&gt;Endpoints can be &lt;strong&gt;builds&lt;/strong&gt;, &lt;strong&gt;sessions&lt;/strong&gt;, &lt;strong&gt;tunnels&lt;/strong&gt;, &lt;strong&gt;platforms&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;For example, you can make use of &lt;strong&gt;/tunnels&lt;/strong&gt; i.e.&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;https://api.lambdatest.com/automation/api/v1/tunnels
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;to fetch the running tunnels used for testing locally hosted web pages through your account.&lt;/p&gt;

&lt;p&gt;When you initiate a test request, a session-id is assigned to that test session. Using LambdaTest Selenium API, you can get detailed information at the test session level using the&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;https://api.lambdatest.com/automation/api/v1/sessions
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;a href="https://www.lambdatest.com/free-online-tools/binary-to-octal?utm_source=devto&amp;amp;utm_medium=organic&amp;amp;utm_campaign=nov_06&amp;amp;utm_term=bh&amp;amp;utm_content=free_online_tools" rel="noopener noreferrer"&gt;**Binary to Octal&lt;/a&gt; Converter is a free, easy-to-use online tool that converts binary number to octal format. Enter a binary number and convert it to an octal number.**&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2&gt;
  
  
  Extracting Build Details Using LambdaTest Selenium API
&lt;/h2&gt;

&lt;p&gt;For demonstration, we extract details of the tests that have status — completed, error, and timeout and we limit the search for first 20 tests. To get started, you need to authorize those APIs to fetch build, session, tunnel &amp;amp; other related information from your account. Login to &lt;a href="https://www.lambdatest.com/support/docs/api-doc/" rel="noopener noreferrer"&gt;https://www.lambdatest.com/support/docs/api-doc/&lt;/a&gt; with your user-name &amp;amp; session-key.&lt;/p&gt;

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

&lt;p&gt;Since you require information about the builds that match the execution state (completed, error, timeout), you have to fill those requirements in the Builds section in &lt;a href="https://www.lambdatest.com/support/docs/api-doc/#/Build/builds" rel="noopener noreferrer"&gt;https://www.lambdatest.com/support/docs/api-doc/#/Build/builds&lt;/a&gt; Below is the screenshot of the command in execution. You also get the CURL API which can be used in your code for fetching the same information programmatically.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.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%2A6Z7CFAeZHQHnk7CL.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.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%2A6Z7CFAeZHQHnk7CL.png" width="800" height="576"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The response code can be 200 (OK), 400 (Session Invalid), and 401 (Authentication Failed). Since we are using Python, we convert the CURL response into Python code using the converter located in &lt;a href="https://curl.trillworks.com/" rel="noopener noreferrer"&gt;https://curl.trillworks.com/&lt;/a&gt;. You can use other tools or websites that can offer similar functionality (for free), for our demonstration we are making use of the above-mentioned website.&lt;/p&gt;

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

&lt;p&gt;We extract details of the first 20 builds that matches status-completed, timeout, error. Once we have extracted the details, we update the build headline for the buildID = 12024. We make use of:&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;https://api.lambdatest.com/automation/api/v1/builds/{Build_ID}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;In our example, the build ID would be 12024. Below is the code which demonstrates the LambdaTest API integration:&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;# Requirements

# Fetch first 20 builds which have status as completed, error, timeout
# Once the details are out, change the Build Title of the build_id 12024

# Refer https://www.lambdatest.com/support/docs/api-doc/#/Build/builds for more information
import requests
import json

# Equivalent Python code from https://curl.trillworks.com/
headers = {
    'accept': 'application/json',
    'Authorization': 'Basic aGltYW5zaHUuc2hldGhAZ21haWGI0T2x1OVI4bHdCc1hXVFNhSU9lYlhuNHg5',
}

params = (
    ('limit', '20'),
    ('status', 'completed,error,timeout'),
)

# Updated build information for build 12024
headers_updated_build = {
    'accept': 'application/json',
    'Authorization': 'Basic aGltYW5zaHUuc2hldGhAZ21haWGI0T2x1OVI4bHdCc1hXVFNhSU9lYlhuNHg5',
    'Content-Type': 'application/json',
}

data_updated_build = '{"name":"Updated build details for PyUnit test from prompt"}'

response = requests.get('https://api.lambdatest.com/automation/api/v1/builds', headers=headers, params=params)

print(response)

json_arr = response.json()

# Print the build_id matching our requirements and Change build title of build_id 12024

for loop_var in range(20):
    build_id = ((json_arr['data'][loop_var])['build_id'])
    test_status = ((json_arr['data'][loop_var])['status_ind'])

    if build_id == 12024:
        response_updated_build = requests.patch('https://api.lambdatest.com/automation/api/v1/builds/12024', headers=headers_updated_build, data=data_updated_build)
        print(response_updated_build)

    print ((build_id), (test_status))
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;As seen in the screenshot, the Build headline for BuildID = 12024 is updated.&lt;/p&gt;

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

&lt;p&gt;We execute the code using the normal Python command, the response of the execution is 200 (OK).&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.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%2AC9RpN83gd5zLlhX9.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.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%2AC9RpN83gd5zLlhX9.png" width="699" height="399"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Extracting Tunnel Information Of Locally Hosted Web Pages
&lt;/h2&gt;

&lt;p&gt;One of the primary advantages of moving the web testing and automated cross browser testing to LambdaTest Selenium Grid is the flexibility to test locally hosted &amp;amp; privately hosted pages using the LambdaTest Tunnel. LambdaTest Tunnel establishes an SSH(Secure Shell) connection between your local machine &amp;amp; LambdaTest cloud servers. To configure the LambdaTest Tunnel for different operating systems, please visit&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;&lt;a href="https://www.lambdatest.com/support/docs/local-testing-for-windows/?utm_source=devto&amp;amp;utm_medium=organic&amp;amp;utm_campaign=nov_06&amp;amp;utm_term=bh&amp;amp;utm_content=support_doc" rel="noopener noreferrer"&gt;LambdaTest Tunnel For Windows&lt;/a&gt;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;a href="https://www.lambdatest.com/support/docs/local-testing-for-macos/?utm_source=devto&amp;amp;utm_medium=organic&amp;amp;utm_campaign=nov_06&amp;amp;utm_term=bh&amp;amp;utm_content=support_doc" rel="noopener noreferrer"&gt;LambdaTest Tunnel For Mac&lt;/a&gt;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;a href="https://www.lambdatest.com/support/docs/local-testing-for-linux/?utm_source=devto&amp;amp;utm_medium=organic&amp;amp;utm_campaign=nov_06&amp;amp;utm_term=bh&amp;amp;utm_content=support_doc" rel="noopener noreferrer"&gt;LambdaTest Tunnel For Linux&lt;/a&gt;&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;In order to make use of the Tunnel APIs, you need to visit: &lt;a href="https://www.lambdatest.com/support/docs/api-doc/#/tunnel/get_tunnels" rel="noopener noreferrer"&gt;https://www.lambdatest.com/support/docs/api-doc/#/tunnel/get_tunnels&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The Tunnel API does not accept any parameters and successful execution fetches details about the different tunnels running in your account. In the implementation shown below, we can see that there were two tunnels that were in use by the user and once the tunnel usage was complete, the tunnel instance was removed &amp;amp; the session was closed.&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;import requests
import json

headers = {
    'accept': 'application/json',
    'Authorization': 'Basic aGltYW5zaHUuc2hldGhAZ21haWwuY2lYlhuNHg5',
}

response = requests.get('https://api.lambdatest.com/automation/api/v1/tunnels', headers=headers)

print(response)
json_arr = response.json()
print(json_arr)
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;&lt;a href="https://media2.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%2A-xjh1wBdHjLjPtM3.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.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%2A-xjh1wBdHjLjPtM3.png" width="800" height="131"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;You can also stop an already running tunnel instance using the corresponding LambdaTest API&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;https://api.lambdatest.com/automation/api/v1/tunnels/{tunnel-ID}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;For a complete list of operations that can be performed on Builds, Sessions, Tunnels, and Platforms; please visit the &lt;a href="https://www.lambdatest.com/support/docs/api-doc/?utm_source=devto&amp;amp;utm_medium=organic&amp;amp;utm_campaign=nov_06&amp;amp;utm_term=bh&amp;amp;utm_content=support_doc" rel="noopener noreferrer"&gt;LambdaTest API Page&lt;/a&gt;.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Convert &lt;a href="https://www.lambdatest.com/free-online-tools/octal-to-decimal?utm_source=devto&amp;amp;utm_medium=organic&amp;amp;utm_campaign=nov_06&amp;amp;utm_term=bh&amp;amp;utm_content=free_online_tools" rel="noopener noreferrer"&gt;octal to decimal &lt;/a&gt;quickly and easily with our free online converter tool. Try it now for free!&lt;/strong&gt;&lt;/p&gt;
&lt;/blockquote&gt;

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

&lt;p&gt;Testing is an integral part of any product/project and its effectiveness can be enhanced by making use of powerful reporting tools. Reports are used to keep track of the testing activities (results, test scenarios, etc.) and using the right tool can improve the overall testing process.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://www.lambdatest.com/?utm_source=devto&amp;amp;utm_medium=organic&amp;amp;utm_campaign=nov_06&amp;amp;utm_term=bh&amp;amp;utm_content=webpage" rel="noopener noreferrer"&gt;Cross browser testing&lt;/a&gt; should be performed for web products since it helps in providing consistent behavior and performance across different combinations of web browsers, operating systems, and devices. Choosing the right cross browser testing platform that has API support and powerful report generation features can reduce the overall effort spent on test related activities of your product.&lt;/p&gt;

&lt;p&gt;LambdaTest serves the purpose of helping you perform &lt;a href="https://www.lambdatest.com/?utm_source=devto&amp;amp;utm_medium=organic&amp;amp;utm_campaign=nov_06&amp;amp;utm_term=bh&amp;amp;utm_content=webpage" rel="noopener noreferrer"&gt;automated cross browser testing&lt;/a&gt; on cloud with a maintenance-free Selenium Grid offering 3000+ real browsers, along with integrations to numerous CI/CD tools, and RESTful Selenium API for extracting HTML reports in PyUnit and every other test automation framework that offers compatibility with Selenium. Go sign up for free, if you haven’t already!&lt;/p&gt;

</description>
      <category>python</category>
      <category>programming</category>
      <category>webdev</category>
      <category>tutorial</category>
    </item>
    <item>
      <title>How To Use Robot Framework For Parallel Test Execution</title>
      <dc:creator>himanshuseth004</dc:creator>
      <pubDate>Mon, 26 Jun 2023 11:58:06 +0000</pubDate>
      <link>https://forem.com/testmuai/how-to-use-robot-framework-for-parallel-test-execution-4h51</link>
      <guid>https://forem.com/testmuai/how-to-use-robot-framework-for-parallel-test-execution-4h51</guid>
      <description>&lt;h2&gt;
  
  
  How To Use Robot Framework For Parallel Test Execution
&lt;/h2&gt;

&lt;p&gt;Over the years, there have been monumental changes in the processes used for developing, testing, and delivering software. Development &amp;amp; release cycles have become considerably short, with Continuous Integration and Continuous Delivery (CI/CD) catalyzing these changes.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://www.lambdatest.com/learning-hub/selenium-interview-questions?utm_source=devto&amp;amp;utm_medium=organic&amp;amp;utm_campaign=jun_26&amp;amp;utm_term=bh&amp;amp;utm_content=learning_hub" rel="noopener noreferrer"&gt;Continuous testing&lt;/a&gt; is one of the guiding pillars of the continuous integration process, as it aids in accelerating feedback and improving product quality. There is a constant race against time, but acceleration shouldn’t be achieved at the cost of quality!&lt;/p&gt;

&lt;p&gt;This is where automated testing at scale can prove beneficial in unearthing bugs and increasing the overall &lt;a href="https://www.lambdatest.com/blog/code-coverage-vs-test-coverage/?utm_source=devto&amp;amp;utm_medium=organic&amp;amp;utm_campaign=jun_26&amp;amp;utm_term=bh&amp;amp;utm_content=blog" rel="noopener noreferrer"&gt;test coverage&lt;/a&gt;. All of this can be achieved by accelerating the CI/CD pipelines with parallel tests (or parallel test execution). On a lighter note, &lt;a href="https://www.lambdatest.com/blog/what-is-parallel-testing-and-why-to-adopt-it/?utm_source=devto&amp;amp;utm_medium=organic&amp;amp;utm_campaign=jun_26&amp;amp;utm_term=bh&amp;amp;utm_content=blog" rel="noopener noreferrer"&gt;parallel testing&lt;/a&gt; is one of the most trusted &amp;amp; reliable friends of CI/CD 🙂&lt;/p&gt;

&lt;p&gt;The majority of the &lt;a href="https://www.lambdatest.com/blog/automation-testing-frameworks/?utm_source=devto&amp;amp;utm_medium=organic&amp;amp;utm_campaign=jun_26&amp;amp;utm_term=bh&amp;amp;utm_content=blog" rel="noopener noreferrer"&gt;test automation frameworks&lt;/a&gt; provide the flexibility to run tests in parallel at different levels — &lt;em&gt;test, test suite, method, class,&lt;/em&gt; and more. Though the level of parallelism depends on the nuances of the underlying framework, you need to choose the parallelism approach based on your execution requirements.&lt;/p&gt;

&lt;p&gt;When it comes to &lt;a href="https://www.lambdatest.com/python-automation-testing?utm_source=devto&amp;amp;utm_medium=organic&amp;amp;utm_campaign=jun_26&amp;amp;utm_term=bh&amp;amp;utm_content=web_page" rel="noopener noreferrer"&gt;automated testing with Python&lt;/a&gt;, there is flexibility to choose from several frameworks — PyUnit, Pytest (or pytest), Behave, and Robot, amongst others.&lt;/p&gt;

&lt;p&gt;Frameworks like pytest support parallel test execution out of the box, whereas parallel testing in Robot, PyUnit, etc., is enabled using supporting tools and libraries. In this blog on using the Robot framework for parallel test execution, I will cover how to run automation tests in parallel using the Robot framework on the local grid, as well as the &lt;a href="https://www.lambdatest.com/selenium-grid-online?utm_source=devto&amp;amp;utm_medium=organic&amp;amp;utm_campaign=jun_26&amp;amp;utm_term=bh&amp;amp;utm_content=web_page" rel="noopener noreferrer"&gt;cloud Selenium grid&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Let’s get started by quickly revisiting the concepts of the Robot framework for parallel test execution.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;*&lt;em&gt;Discover essential tips and &lt;a href="https://www.lambdatest.com/learning-hub/agile-interview-questions?utm_source=devto&amp;amp;utm_medium=organic&amp;amp;utm_campaign=jun_26&amp;amp;utm_term=bh&amp;amp;utm_content=learning_hub" rel="noopener noreferrer"&gt;agile interview question&lt;/a&gt; in our comprehensive tutorial. Prepare effectively and ace your next agile interview. *&lt;/em&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2&gt;
  
  
  What is Robot Framework?
&lt;/h2&gt;

&lt;p&gt;For starters, Robot is a popular Python-based extensible keyword-driven open-source framework. It is primarily used for test automation, Acceptance Test Driven Development (ATDD), &lt;a href="https://www.lambdatest.com/blog/behaviour-driven-development-by-selenium-testing-with-gherkin/?utm_source=devto&amp;amp;utm_medium=organic&amp;amp;utm_campaign=jun_26&amp;amp;utm_term=bh&amp;amp;utm_content=blog" rel="noopener noreferrer"&gt;Behavior Driven Development &lt;/a&gt;(BDD), and Robotic Process Automation (RPA ).&lt;/p&gt;

&lt;p&gt;The Robot framework uses an easy-to-read syntax that utilizes human-readable keywords. The framework-agnostic keywords define the actions that must be performed in a particular test.&lt;/p&gt;

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

&lt;p&gt;Keywords in the Robot framework are divided into three categories:&lt;/p&gt;

&lt;h2&gt;
  
  
  High-level keywords
&lt;/h2&gt;

&lt;p&gt;High-level keywords provide an abstract representation of the underlying business functionality. They improve the readability and maintainability of the test cases since they can encapsulate low-level keywords.&lt;/p&gt;

&lt;p&gt;They should resonate with the actions being performed in the said use case. Consider a banking application (or website) where users need to login before doing any financial transaction. &lt;em&gt;Login, Transfer Amount&lt;/em&gt;, etc., are some of the high-level keywords in line with the business case.&lt;/p&gt;

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

&lt;h2&gt;
  
  
  Low-level keywords
&lt;/h2&gt;

&lt;p&gt;Low-level keywords represent the operations performed as a part of the test scenario. These keywords, defined in libraries or resource files, can be either custom-built libraries or built-in keywords provided by the Robot framework.&lt;/p&gt;

&lt;p&gt;Keywords that are a part of the &lt;em&gt;BuiltIn&lt;/em&gt; library and &lt;em&gt;SeleniumLibrary&lt;/em&gt; in Robot can be leveraged in automating test scenarios. &lt;em&gt;Should Contain, Should Be Equal, Skip If&lt;/em&gt;, etc., are some of the keywords that are a part of the &lt;em&gt;BuiltIn&lt;/em&gt; library. &lt;em&gt;Select From List By Value, Input Text, Click Button, Click Element, Wait Until Element Is Visible,&lt;/em&gt; etc., are some of the prominent keywords that are a part of the &lt;em&gt;SeleniumLibrary&lt;/em&gt;.&lt;/p&gt;

&lt;p&gt;The &lt;em&gt;SeleniumLibrary&lt;/em&gt; in Robot uses the &lt;a href="https://www.lambdatest.com/learning-hub/webdriver?utm_source=devto&amp;amp;utm_medium=organic&amp;amp;utm_campaign=jun_26&amp;amp;utm_term=bh&amp;amp;utm_content=learning_hub" rel="noopener noreferrer"&gt;Selenium WebDriver&lt;/a&gt;, which further helps automate interactions with the web browser.&lt;/p&gt;

&lt;p&gt;Here is the sample usage of low-level keywords for creating high-level keywords.&lt;/p&gt;

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

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Here’s 295+ &lt;a href="https://www.lambdatest.com/learning-hub/selenium-interview-questions?utm_source=devto&amp;amp;utm_medium=organic&amp;amp;utm_campaign=jun_26&amp;amp;utm_term=bh&amp;amp;utm_content=learning_hub" rel="noopener noreferrer"&gt;Selenium Interview Questions &lt;/a&gt;with Answers that will help you boost your confidence in an Interview.&lt;/strong&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2&gt;
  
  
  Technical keywords
&lt;/h2&gt;

&lt;p&gt;Technical keywords are built-in keywords that help with &lt;a href="https://www.lambdatest.com/learning-hub/test-management?utm_source=devto&amp;amp;utm_medium=organic&amp;amp;utm_campaign=jun_26&amp;amp;utm_term=bh&amp;amp;utm_content=learning_hub" rel="noopener noreferrer"&gt;test management&lt;/a&gt; and &lt;a href="https://www.lambdatest.com/learning-hub/test-execution?utm_source=devto&amp;amp;utm_medium=organic&amp;amp;utm_campaign=jun_26&amp;amp;utm_term=bh&amp;amp;utm_content=learning_hub" rel="noopener noreferrer"&gt;test execution&lt;/a&gt;. They help with logging, controlling execution flow, and error handling.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Capture Page Screenshot, Capture Element Screenshot, Pass Execution If, Log, Log To Console,&lt;/em&gt; etc., are some of the technical keywords in Robot. For instance, the keyword to Capture Element Screenshot can be leveraged for capturing a screenshot of a certain WebElement and comparing it across different releases.&lt;/p&gt;

&lt;p&gt;In the below-mentioned example, we capture the screenshots of the entire webpage and the Button element. The screenshots are placed in a customized directory instead of &lt;em&gt;${OUTPUT_DIR}&lt;/em&gt; (or present working directory).&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.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%2AZ-JdHr311X-cjJUz.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.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%2AZ-JdHr311X-cjJUz.png"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Along with &lt;em&gt;BuiltIn&lt;/em&gt; and &lt;em&gt;SeleniumLibrary&lt;/em&gt;, we have also included the &lt;em&gt;OperatingSystem&lt;/em&gt; library for using Directory-related keywords (i.e., &lt;em&gt;Create Directory&lt;/em&gt; and &lt;em&gt;​​Directory Should Exist&lt;/em&gt;).&lt;/p&gt;

&lt;h2&gt;
  
  
  Robot Framework Architecture
&lt;/h2&gt;

&lt;p&gt;Robot is an open-source, extensible, and language-independent framework. All of this is possible using the test libraries written in that particular programming language.&lt;/p&gt;

&lt;p&gt;The &lt;em&gt;Remote Library Interface&lt;/em&gt; allows the test libraries to be run as external processes. It uses the XML-RPC protocol for communication; hence test libraries can be implemented for programming languages (e.g., Java, C#, JavaScript, etc.) that support the protocol. For example, the &lt;em&gt;jrobotremoteserver&lt;/em&gt; is a remote server for the Robot framework implemented using Java.&lt;/p&gt;

&lt;p&gt;Here are some of the major building blocks of the Robot Framework:&lt;/p&gt;

&lt;h2&gt;
  
  
  Robot Framework
&lt;/h2&gt;

&lt;p&gt;Referred to as the framework’s core which is implemented in Python. It is primarily responsible for test execution and test case management.&lt;/p&gt;

&lt;p&gt;The core has no information regarding the TUT (Target Under Test), as the entire interaction is handled by the test libraries. It is also responsible for features related to logging and reporting.&lt;/p&gt;

&lt;h2&gt;
  
  
  Test Data
&lt;/h2&gt;

&lt;p&gt;As the name indicates, test data serves as the input to the test cases. The test data is written in a tabular format to enhance the maintainability of the tests.&lt;/p&gt;

&lt;p&gt;Once the Robot framework receives the input data, it starts executing the test scenarios and generates detailed reports in HTML format. Robot also offers integration with popular reporting tools &amp;amp; dashboards like Allure, Report Portal, and Grafana.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.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%2AvBnygrkl-cI8QxhO.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.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%2AvBnygrkl-cI8QxhO.png"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://www.lambdatest.com/blog/wp-content/uploads/2020/09/Robot-Framework-Architecture.jpg?utm_source=devto&amp;amp;utm_medium=organic&amp;amp;utm_campaign=jun_26&amp;amp;utm_term=bh&amp;amp;utm_content=blog" rel="noopener noreferrer"&gt;High-level Architecture — Robot Framework&lt;/a&gt;&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Get ready to ace your JUnit interviews in 2023 with our comprehensive list of 90 &lt;a href="https://www.lambdatest.com/learning-hub/Junit-interview-questions?utm_source=devto&amp;amp;utm_medium=organic&amp;amp;utm_campaign=jun_26&amp;amp;utm_term=bh&amp;amp;utm_content=learning_hub" rel="noopener noreferrer"&gt;junit interviews questions&lt;/a&gt; and answers that will help you sharpen your testing skills.&lt;/strong&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2&gt;
  
  
  Test Libraries
&lt;/h2&gt;

&lt;p&gt;As seen in the architecture diagram, Test Libraries act as the bridge (or interface) between the Robot Framework Core and SUT. Test libraries in Robot contain the keywords, thereby improving its maintainability &amp;amp; reusability.&lt;/p&gt;

&lt;p&gt;Apart from &lt;em&gt;Robot Standard Libraries, SeleniumLibrary&lt;/em&gt; in Robot can be extensively used when performing automated testing using the framework. Do check out this exhaustive list of Robot libraries that comprises mid-level (framework) Libraries, low-level (driver) Libraries, and Remote Libraries.&lt;/p&gt;

&lt;h2&gt;
  
  
  System Under Test (SUT)
&lt;/h2&gt;

&lt;p&gt;This is the target on which the automated tests are performed. It could be either a website or a web application.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://www.lambdatest.com/learning-hub/test-case?utm_source=devto&amp;amp;utm_medium=organic&amp;amp;utm_campaign=jun_26&amp;amp;utm_term=bh&amp;amp;utm_content=learning_hub" rel="noopener noreferrer"&gt;Test Cases&lt;/a&gt; in Robot consist of Test Steps that, in turn, use the Test Libraries for realizing interactions with the SUT. Like any other test automation framework, test cases can be organized in test suites for improved maintenance.&lt;/p&gt;

&lt;h2&gt;
  
  
  Benefits of Parallel Testing in Robot Framework
&lt;/h2&gt;

&lt;p&gt;The Robot framework does offer out-of-the-box support for parallel test execution. However, it recommends the usage of Pabot — a parallel test executor for tests written using the Robot framework.&lt;/p&gt;

&lt;p&gt;Before we deep dive into using the Robot framework for parallel test execution with Pabot, let’s quickly revisit the salient benefits of parallel test execution.&lt;/p&gt;

&lt;h2&gt;
  
  
  Accelerated Test Execution
&lt;/h2&gt;

&lt;p&gt;By default, Robot tests run in a sequential manner. The serial execution applies to tests as well as test suites. Even if a single test takes a few seconds to execute, the complete test execution time might go up to a few minutes (for a significantly last test suite).&lt;/p&gt;

&lt;p&gt;Running tests serially might yield results in the short run, but the approach falters when test suites consisting of numerous tests have to be executed in a CI/CD pipeline. This is where parallel testing in Robot can be highly instrumental, as it helps accelerate the test execution by leveraging parallelism at the test and/or test-suite level.&lt;/p&gt;

&lt;h2&gt;
  
  
  Increased Test Coverage
&lt;/h2&gt;

&lt;p&gt;Parallel test execution in Robot helps improve product quality, as tests are run across different input combinations. The said combination can consist of generic input values (e.g., input URL, email address), browser names, browser versions, platform names, etc., depending on the scenario under test.&lt;/p&gt;

&lt;p&gt;Below is an example of a Robot script where tests will be executed in parallel on Chrome and Safari browsers. This logic can be further scaled to run tests on different input combinations.&lt;/p&gt;

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

&lt;p&gt;All of this eventually helps increase the overall test coverage and identify issues at a significantly faster pace.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;This exhaustive list of &lt;a href="https://www.lambdatest.com/learning-hub/testng-interview-questions?utm_source=devto&amp;amp;utm_medium=organic&amp;amp;utm_campaign=jun_26&amp;amp;utm_term=bh&amp;amp;utm_content=learning_hub" rel="noopener noreferrer"&gt;TestNG interview questions&lt;/a&gt; will aid you in strengthening your position as a candidate for the TestNG interview.&lt;/strong&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2&gt;
  
  
  Faster Feedback Cycles
&lt;/h2&gt;

&lt;p&gt;As seen so far, there are umpteen benefits of &lt;a href="https://www.lambdatest.com/blog/speed-up-testing-cycle/?utm_source=devto&amp;amp;utm_medium=organic&amp;amp;utm_campaign=jun_26&amp;amp;utm_term=bh&amp;amp;utm_content=blog" rel="noopener noreferrer"&gt;speeding up test cycles &lt;/a&gt;using parallel test execution. Another major benefit of parallel testing is shortened feedback loops for issue discovery, bug fixes, and testing the fixes for potential side effects.&lt;/p&gt;

&lt;p&gt;The same guiding principles also apply to the Robot framework, as continuous testing with Robot can be realized by running parallel tests in a CI pipeline. Jenkins, GitLab, TeamCity, Azure DevOps, and GitHub Actions are some of the &lt;a href="https://www.lambdatest.com/blog/best-ci-cd-tools/?utm_source=devto&amp;amp;utm_medium=organic&amp;amp;utm_campaign=jun_26&amp;amp;utm_term=bh&amp;amp;utm_content=blog" rel="noopener noreferrer"&gt;top CI/CD tools &lt;/a&gt;supported by the Robot framework.&lt;/p&gt;

&lt;p&gt;The combination of parallel testing &amp;amp; CI/CD aids in improving the effectiveness of the pipeline, shortening feedback cycles, and improving product quality at every step of product development.&lt;/p&gt;

&lt;h2&gt;
  
  
  Achieve More with Fail-Fast Approach
&lt;/h2&gt;

&lt;p&gt;Consider a scenario where you have 100’s of tests triggered in a CI/CD pipeline but only a selected few (e.g., 10 tests) fail all the time. What if these 10 &lt;em&gt;independent&lt;/em&gt; tests are run in parallel in the next execution cycle? This approach will be instrumental in ensuring that tests are completed quickly or fail-fast, thereby helping save time as well as crucial $!&lt;/p&gt;

&lt;p&gt;It is essential to note that tests when executed in parallel, might exhibit flakiness in case tests are interdependent or reliant on any sort of ordering. Apart from following &lt;a href="https://www.lambdatest.com/blog/selenium-best-practices-for-web-testing/?utm_source=devto&amp;amp;utm_medium=organic&amp;amp;utm_campaign=jun_26&amp;amp;utm_term=bh&amp;amp;utm_content=blog" rel="noopener noreferrer"&gt;Selenium’s best practices&lt;/a&gt; for test case design, you could also look at reducing the number of parallel processes.&lt;/p&gt;

&lt;p&gt;Robot framework provides the flexibility to re-run only failed tests, post which the results can be merged into a single results file using &lt;em&gt;rebot&lt;/em&gt;.&lt;/p&gt;

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

&lt;p&gt;You can refer to the &lt;a href="https://docs.robotframework.org/docs/flaky_tests" rel="noopener noreferrer"&gt;Robot official documentation&lt;/a&gt; that deep dives into flaky-test management and re-execution of failed tests.&lt;/p&gt;

&lt;p&gt;Apart from the benefits mentioned above, I would recommend revisiting this blog that deep-dives into all the major &lt;a href="https://www.lambdatest.com/blog/what-is-parallel-testing-and-why-to-adopt-it/?utm_source=devto&amp;amp;utm_medium=organic&amp;amp;utm_campaign=jun_26&amp;amp;utm_term=bh&amp;amp;utm_content=blog" rel="noopener noreferrer"&gt;benefits of parallel testing&lt;/a&gt;. Now that the platform is all set let’s look at how to leverage Pabot (Parallel Robot Framework) for realizing parallel test execution.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Here’s a list of 70 &lt;a href="https://www.lambdatest.com/learning-hub/cucumber-interview-questions?utm_source=devto&amp;amp;utm_medium=organic&amp;amp;utm_campaign=jun_26&amp;amp;utm_term=bh&amp;amp;utm_content=learning_hub" rel="noopener noreferrer"&gt;Cucumber Interview Questions &lt;/a&gt;and Answers that will help you boost your confidence in an Interview.&lt;/strong&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2&gt;
  
  
  Introduction to Pabot (Parallel Robot Framework)
&lt;/h2&gt;

&lt;p&gt;The Robot framework executes individual tests and tests within multiple test suites in a sequential manner. In case you want a quick recap of Robot, look at this detailed &lt;a href="https://www.lambdatest.com/blog/robot-framework-tutorial/?utm_source=devto&amp;amp;utm_medium=organic&amp;amp;utm_campaign=jun_26&amp;amp;utm_term=bh&amp;amp;utm_content=blog" rel="noopener noreferrer"&gt;Robot framework tutorial&lt;/a&gt;, where we have covered all the essential aspects of Robot, including test execution on local and cloud Selenium grid(s).&lt;/p&gt;

&lt;p&gt;As mentioned earlier, Pabot is a parallel test runner for the Robot framework. Pabot lets you run Robot tests in parallel at test-level as well as test-suite level.&lt;/p&gt;

&lt;p&gt;By default, Pabot does parallelization at the test suite level. Each process has its own memory space and runs a single test suite. In this case, individual tests within the test suite run sequentially, whereas all the test suites (&lt;em&gt;e.g., test1.robot, test2.robot&lt;/em&gt;, etc.) run in parallel.&lt;/p&gt;

&lt;p&gt;Resource file(s) normally houses reusable components like high-level keywords, variables, libraries, and other settings normally used across tests and test suites.&lt;/p&gt;

&lt;p&gt;Below is an example Resource file (&lt;em&gt;e.g., common.robot&lt;/em&gt;):&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.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%2A2s2tLeBr8gDvaGGj.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.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%2A2s2tLeBr8gDvaGGj.png"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Sample Resource File&lt;/em&gt;&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Get ready for your BDD interview with 100+ &lt;a href="https://www.lambdatest.com/learning-hub/bdd-interview-questions?utm_source=devto&amp;amp;utm_medium=organic&amp;amp;utm_campaign=jun_26&amp;amp;utm_term=bh&amp;amp;utm_content=learning_hub" rel="noopener noreferrer"&gt;BDD interview questions.&lt;/a&gt; Boost your confidence and impress your interviewer with this comprehensive guide covering principles &amp;amp; techniques.&lt;/strong&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2&gt;
  
  
  Installation
&lt;/h2&gt;

&lt;p&gt;Like any other Python package, Pabot can also be installed globally or inside a virtual environment (&lt;em&gt;venv&lt;/em&gt;). At the time of writing this blog on using Robot framework for parallel test execution, the latest version of Pabot is 2.15.0.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Global installation of Pabot&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Invoke the following command on the terminal to install Pabot:&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;pip3 install -U robotframework-pabot
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;&lt;a href="https://media2.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%2AaCR5P34I9QGE_BbQ.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.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%2AaCR5P34I9QGE_BbQ.png"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Though &lt;em&gt;pip3&lt;/em&gt; is the recommended package manager for Python 3.x, you can still use &lt;em&gt;pip&lt;/em&gt; with Python 3.x. In that case, the installation command is &lt;em&gt;pip install -U robotframework-pabot&lt;/em&gt;.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Pabot installation in Virtual Environment (virtualvenv)&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Virtual Environment (virtualvenv) provides much-need isolation of builds, thereby providing better management of Python packages for different projects.&lt;/p&gt;

&lt;p&gt;Follow the below-mentioned steps for installing Pabot in venv:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Run the command &lt;em&gt;pip3 install virtualenv&lt;/em&gt; on the terminal if venv is not installed on your machine.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;&lt;a href="https://media2.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%2AGidW_dubw0OSwtxW.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.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%2AGidW_dubw0OSwtxW.png"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Create a virtual environment in the project folder by invoking the &lt;em&gt;virtualenv venv&lt;/em&gt; command on the terminal. It is important to note that the environment name (i.e., &lt;em&gt;venv&lt;/em&gt; in our case) is arbitrary.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;&lt;a href="https://media2.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%2A_bZBFELWcx8GGaoF.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.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%2A_bZBFELWcx8GGaoF.png"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Activate the environment (i.e., &lt;em&gt;venv&lt;/em&gt;) by invoking the source &lt;em&gt;venv/bin/activate&lt;/em&gt; command on the terminal.&lt;/li&gt;
&lt;/ol&gt;

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

&lt;ol&gt;
&lt;li&gt;Invoke the command &lt;em&gt;pip3 install robotframework-pabot&lt;/em&gt; to install the latest version of Pabot.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;&lt;a href="https://media2.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%2AOS7u2Cx6IYlbF7Sk.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.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%2AOS7u2Cx6IYlbF7Sk.png"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Navigate your Laravel interview with ease! Our guide covers over 190 &lt;a href="https://www.lambdatest.com/learning-hub/laravel-interview-questions?utm_source=devto&amp;amp;utm_medium=organic&amp;amp;utm_campaign=jun_26&amp;amp;utm_term=bh&amp;amp;utm_content=learning_hub" rel="noopener noreferrer"&gt;Laravel interview questions&lt;/a&gt; to help you land your ideal tech job.&lt;/strong&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2&gt;
  
  
  Usage
&lt;/h2&gt;

&lt;p&gt;Now that the Pabot installation is successful, let’s look at the commands to achieve parallelism at test and test-suite levels. By default, Pabot splits the execution at the suite level. Every process runs a single suite, you can also configure the number of parallel executors using the &lt;em&gt;–processes&lt;/em&gt; option.&lt;/p&gt;

&lt;p&gt;If not specified, Pabot enables two CPU cores that run one process each. Specifying all in &lt;em&gt;–processes&lt;/em&gt; creates as many processes as the number of executable test suites or tests. However, this option must be used with caution, as too much parallelism might affect the performance and/or cause invalid test failures.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Parallel Execution with Pabot at suite-level (default)&lt;/strong&gt;&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;pabot [path to tests]
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;For instance, the command shown below starts 2 processes and runs test suites located in &lt;em&gt;Tests/CloudGrid&lt;/em&gt; folder in parallel.&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;pabot — verbose Tests/CloudGrid
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;As seen below, two processes (with PID 91894 and 91895) running in parallel are executing different test suites.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.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%2AWnwJVxDXEBrWJiPL.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.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%2AWnwJVxDXEBrWJiPL.png"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Parallel Execution with Pabot at test level&lt;/strong&gt;&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;pabot — verbose — testlevelsplit [path to tests]
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;For instance, the command shown below starts 2 processes and runs individual tests located in test suites in the &lt;em&gt;Tests/CloudGrid&lt;/em&gt; folder in parallel.&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;pabot — verbose — testlevelsplit Tests/CloudGrid
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;As seen below, two processes (with PID 93731 and 93732) running in parallel are executing two separate tests — App.Example 1 &amp;amp; App.Example 2&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.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%2ACeMZOOK1nA_FWkOE.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.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%2ACeMZOOK1nA_FWkOE.png"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Customize the number of parallel processes&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;As mentioned earlier, the &lt;em&gt;–processes&lt;/em&gt; option lets you achieve the same. Simply specify the number of processes (in integer format) that you want to start in parallel.&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;pabot — verbose — processes &amp;lt;num_of_processes&amp;gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;In the below command, we have specified the number of parallel processes as 4 with parallelism at the test level. However, only 3 parallel processes (PID — 95319, 95320, and 95320) are kick-started since maximum tests in a particular test-suite is 3.&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;pabot — verbose — testlevelsplit — processes 4 &amp;lt;path_to_tests&amp;gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;&lt;a href="https://media2.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%2AJq9gMGl0tdq6R6Y4.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.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%2AJq9gMGl0tdq6R6Y4.png"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Run &lt;em&gt;pabot –help&lt;/em&gt; on the terminal in case you want to have a look at other options supported by the runner.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.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%2A87In1r3qrwADWGHd.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.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%2A87In1r3qrwADWGHd.png"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Ace your &lt;a href="https://www.lambdatest.com/learning-hub/unit-testing-interview-questions?utm_source=devto&amp;amp;utm_medium=organic&amp;amp;utm_campaign=jun_26&amp;amp;utm_term=bh&amp;amp;utm_content=learning_hub" rel="noopener noreferrer"&gt;Unit testing interview questions&lt;/a&gt; with our comprehensive interview questions and solutions for unit testing, covering topics from syntax to advanced techniques.&lt;/strong&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;In a nutshell, Pabot can be used for parallel test execution to optimize resource utilization by leveraging the available memory and CPU cores in the machine.&lt;/p&gt;

&lt;p&gt;Now that we have covered all the essentials of Pabot, let’s look at using Robot framework for parallel test execution on Local Grid as well as LambdaTest cloud Grid.&lt;/p&gt;

&lt;h2&gt;
  
  
  Demo: Parallel Testing in Selenium Robot using Pabot
&lt;/h2&gt;

&lt;p&gt;The primary pre-requisite for the demonstration is Python 🙂, you can refer to &lt;a href="https://www.lambdatest.com/blog/getting-started-with-selenium-python/?utm_source=devto&amp;amp;utm_medium=organic&amp;amp;utm_campaign=jun_26&amp;amp;utm_term=bh&amp;amp;utm_content=blog" rel="noopener noreferrer"&gt;getting started with Selenium Python&lt;/a&gt; in case you need a quick recap of Python for Selenium automated testing. For demonstrating parallel testing with Robot using Pabot, I would be running the same test scenarios across the local grid and LambdaTest cloud grid.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;I am using a macOS (Ventura 13.4) machine with the latest versions of Chrome and Safari installed. Some of the steps might vary in case you are using some other platform (e.g., Windows).&lt;/em&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  &lt;strong&gt;Test Suite 1&lt;/strong&gt;
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;Test Scenario — 1&lt;/strong&gt;&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;&lt;p&gt;Navigate to &lt;a href="https://lambdatest.github.io/sample-todo-app/" rel="noopener noreferrer"&gt;https://lambdatest.github.io/sample-todo-app/&lt;/a&gt;.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Select the first two checkboxes.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Send ‘Yey Let’s add it to list’ to the textbox with id = sampletodotext.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Click the Add Button and verify whether the text has been added or not.&lt;/p&gt;&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;&lt;strong&gt;Test Scenario — 2&lt;/strong&gt;&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;&lt;p&gt;Navigate to &lt;a href="https://lambdatest.github.io/sample-todo-app/" rel="noopener noreferrer"&gt;https://lambdatest.github.io/sample-todo-app/&lt;/a&gt;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Select the first, second, and fifth checkboxes.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Send ‘Yey Let’s add it to list’ to the textbox with id = sampletodotext.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Click the Add Button and verify whether the text has been added or not.&lt;/p&gt;&lt;/li&gt;
&lt;/ol&gt;

&lt;h3&gt;
  
  
  &lt;strong&gt;Test Suite 2&lt;/strong&gt;
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;Test Scenario — 1&lt;/strong&gt;&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;&lt;p&gt;Navigate to &lt;a href="https://www.lambdatest.com/selenium-playground/" 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;Click on the element ‘Input Form Submit’.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Enter all the required details in the newly opened form.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Submit the items.&lt;/p&gt;&lt;/li&gt;
&lt;/ol&gt;

&lt;h2&gt;
  
  
  Project Prerequisites
&lt;/h2&gt;

&lt;p&gt;Before I deep dive into the implementation, let’s do a quick walk-through of the setup used for implementing the scenarios. I am using Visual Studio (VS) Code IDE and Poetry Package Manager (instead of pip) as it simplifies project management to a huge extent.&lt;/p&gt;

&lt;p&gt;Follow the below-mentioned steps for creating a Robot project in VS Code:&lt;/p&gt;

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

&lt;p&gt;After adding the project in VS Code, please run the command to create a virtual environment after navigating to the parent project folder.&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;virtualenv venv

source venv
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;&lt;a href="https://media2.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%2AGpZLIMK7-2ZFtrpY.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.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%2AGpZLIMK7-2ZFtrpY.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;Install Poetry by invoking the following command on the terminal:&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;pip3 install poetry
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;&lt;a href="https://media2.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%2AWgfOJLB_xcY1wj95.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.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%2AWgfOJLB_xcY1wj95.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;Dependencies, plugins, and build configurations are a part of &lt;em&gt;pyproject.toml.&lt;/em&gt; In our case, we have already added the respective packages and dependencies in &lt;em&gt;project.toml&lt;/em&gt;.&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;[tool.poetry]
name = "robot-parallel-tests"
version = "0.1.0"
description = ""
authors = ["Himanshu Jagdish Sheth &amp;lt;himanshu.sheth@gmail.com&amp;gt;"]
readme = "README.md"


[tool.poetry.dependencies]
python = "^3.11"
selenium = "4.9.0"
flake8 = "^6.0.0"
autopep8 = "^2.0.2"


[build-system]
requires = ["poetry-core"]
build-backend = "poetry.core.masonry.api"
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;If &lt;em&gt;pyproject.toml&lt;/em&gt; is not present for your project, run the command &lt;em&gt;poetry init&lt;/em&gt; on the terminal. Post that, select the following packages using Poetry (via pyproject.toml):&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;Python (v 3.11.0)&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Selenium (v 4.9.0)&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Flake8&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Autopep8&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Select the relevant package name and version from the list (e.g., selenium, autopep8, etc.).&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.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%2AJFcG48t6gDxDG2JK.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.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%2AJFcG48t6gDxDG2JK.png"&gt;&lt;/a&gt;&lt;/p&gt;

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

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

&lt;p&gt;Once all the packages are selected for &lt;em&gt;pyproject.toml&lt;/em&gt;, press &lt;em&gt;Enter&lt;/em&gt; to complete the generation of the file.&lt;/p&gt;

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

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;This questionnaire intends to provide a list of some of the most frequently asked &lt;a href="https://www.lambdatest.com/learning-hub/jenkins-interview-questions?utm_source=devto&amp;amp;utm_medium=organic&amp;amp;utm_campaign=jun_26&amp;amp;utm_term=bh&amp;amp;utm_content=learning_hub" rel="noopener noreferrer"&gt;Jenkins interview questions &lt;/a&gt;to assist job seekers in preparing for interviews.&lt;/strong&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;&lt;strong&gt;Step 4&lt;/strong&gt;&lt;br&gt;
Invoke the command &lt;em&gt;poetry install&lt;/em&gt; for setting up the project configurations. On completion, this will generate the files &lt;em&gt;pyproject.toml&lt;/em&gt; and p*oetry.lock*.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.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%2AzT6pYv4GcyP-eVrI.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.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%2AzT6pYv4GcyP-eVrI.png"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Step 5&lt;/strong&gt;&lt;br&gt;
Install the project dependencies (i.e., &lt;em&gt;selenium, robotframework, and robotframework-seleniumlibrary&lt;/em&gt;).&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;selenium==4.9.0
robotframework
robotframework-seleniumlibrary
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;Invoke the command &lt;em&gt;pip3 install -r requirements.txt&lt;/em&gt; to install the project dependencies (or packages).&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.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%2A-dy_jB8S19hntEbj.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.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%2A-dy_jB8S19hntEbj.png"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Project Setup
&lt;/h2&gt;

&lt;p&gt;Shown below is the project setup that will be used in the Robot framework for parallel test execution on the local grid and LambdaTest cloud grid.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.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%2A9LL9k8qujNCee6TN.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.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%2A9LL9k8qujNCee6TN.png"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Here is a gist of the overall folder structure:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Resources/PageObject&lt;/strong&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;This folder contains three subfolders — &lt;em&gt;Common, KeyDefs,&lt;/em&gt; and Locators that contain implementation related to test execution status and high-level keywords used in the tests.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.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%2AbFTzRWnbz4Z9e0Vs.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.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%2AbFTzRWnbz4Z9e0Vs.png"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Tests&lt;/strong&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;As the name indicates, Tests contain implementation of the actual tests using the Robot framework for parallel test execution with the Pabot runner.&lt;/p&gt;

&lt;p&gt;Since the demo will be performed on the local grid and the cloud-based LambdaTest grid, we have segregated the test implementation into separate subfolders — LocalGrid and CloudGrid&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.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%2A3Zj1f8MCJ-RytfoL.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.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%2A3Zj1f8MCJ-RytfoL.png"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Apart from the folders mentioned above, we have &lt;em&gt;pyproject.toml&lt;/em&gt; &amp;amp; &lt;em&gt;requirements.txt&lt;/em&gt; that contain build system-related settings &amp;amp; project dependencies, respectively.&lt;/p&gt;

&lt;p&gt;For better continuity, I will cover aspects related to execution on the cloud grid in a separate section.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Here’s Top 30+ &lt;a href="https://www.lambdatest.com/learning-hub/cicd-interview-questions?utm_source=devto&amp;amp;utm_medium=organic&amp;amp;utm_campaign=jun_26&amp;amp;utm_term=bh&amp;amp;utm_content=learning_hub" rel="noopener noreferrer"&gt;CI CD Interview Questions&lt;/a&gt; and Answers that will help you boost your confidence in an Interview.&lt;/strong&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2&gt;
  
  
  Implementation
&lt;/h2&gt;

&lt;p&gt;For better code maintainability, we have used P&lt;a href="https://www.lambdatest.com/blog/page-object-model-in-selenium-python/?utm_source=devto&amp;amp;utm_medium=organic&amp;amp;utm_campaign=jun_26&amp;amp;utm_term=bh&amp;amp;utm_content=blog" rel="noopener noreferrer"&gt;age Object Model (POM) in Selenium Python&lt;/a&gt; to decouple the dependency between the locators and test code. Let’s dive deep into the specifics of the implementation!&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Resources/PageObject/Locators&lt;/strong&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;To ensure that changes in the locators have minimal impact on the test code, locators used in the test cases are placed in the Locators.py file.&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;# ToDo Page Locators


FirstItem = "name:li1"
SecondItem = "name:li2"
FifthItem = "name:li5"
AddButton = "id:addbutton"
ToDoText = "id:sampletodotext"


NewItemText = "Yey Let's add it to list"
NewAdditionText = "xpath=/html/body/div/div/div/ul/li[6]/span"


# Selenium Playground Page Locators


SubmitButton = "xpath=//a[.='Input Form Submit']"
Name = "name:name"
email = "id:inputEmail4"
passwd = "//input[@name='password']"
company = "//input[@id='company']"
website = "css=#websitename"
country = "name:country"
city = "//input[@id='inputCity']"
address1 = "id:inputAddress1"
address2 = "id:inputAddress2"
state = "css=#inputState"
city = "//input[@id='inputCity']"
zip code = "css=#inputZip"
FinalSubmission = "xpath=//*[@id='seleniumform']/div[6]/button"


SuccessText = "Thanks for contacting us, we will get back to you shortly."
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;Here, we have used different &lt;a href="https://www.lambdatest.com/blog/selenium-locators-cheat-sheet/?utm_source=devto&amp;amp;utm_medium=organic&amp;amp;utm_campaign=jun_26&amp;amp;utm_term=bh&amp;amp;utm_content=blog" rel="noopener noreferrer"&gt;locators in Selenium&lt;/a&gt;, like ID, CSS Selector, linkText, XPath, etc., to locate WebElements on the page. The WebElements can be located using built-in Inspect Tools or plugins like &lt;a href="https://www.lambdatest.com/blog/selectorshub-the-next-gen-xpath-css-selectors-tool/?utm_source=devto&amp;amp;utm_medium=organic&amp;amp;utm_campaign=jun_26&amp;amp;utm_term=bh&amp;amp;utm_content=blog" rel="noopener noreferrer"&gt;SelectorsHub&lt;/a&gt;, POM Builder, etc.&lt;/p&gt;

&lt;p&gt;Let’s take the case of the URL &lt;a href="https://lambdatest.github.io/sample-todo-app/" rel="noopener noreferrer"&gt;https://lambdatest.github.io/sample-todo-app/&lt;/a&gt; used in Test Scenario — 1. The &lt;a href="https://www.lambdatest.com/blog/how-to-use-name-locator-in-selenium-automation-scripts/?utm_source=devto&amp;amp;utm_medium=organic&amp;amp;utm_campaign=jun_26&amp;amp;utm_term=bh&amp;amp;utm_content=blog" rel="noopener noreferrer"&gt;Name locator in Selenium&lt;/a&gt; is used for locating the elements ‘First Item’ and ‘Second Item’.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.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%2AHHa2Dwd_MvwciEJh.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.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%2AHHa2Dwd_MvwciEJh.png"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;On similar lines, the &lt;a href="https://www.lambdatest.com/blog/making-the-move-with-id-locator-in-selenium-webdriver/?utm_source=devto&amp;amp;utm_medium=organic&amp;amp;utm_campaign=jun_26&amp;amp;utm_term=bh&amp;amp;utm_content=blog" rel="noopener noreferrer"&gt;ID locator in Selenium&lt;/a&gt; is used for locating the ‘Add’ button on the page.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.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%2AZfB8pLMOn6zoTWLe.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.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%2AZfB8pLMOn6zoTWLe.png"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Lastly, once the new item is added to the ToDo list, it is located using the &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=jun_26&amp;amp;utm_term=bh&amp;amp;utm_content=blog" rel="noopener noreferrer"&gt;XPath locator in Selenium&lt;/a&gt;.&lt;/p&gt;

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

&lt;p&gt;On similar lines, we have used the combination of CSS Selectors, Name, ID, and XPath locators for locating WebElements on &lt;a href="https://www.lambdatest.com/selenium-playground/?utm_source=devto&amp;amp;utm_medium=organic&amp;amp;utm_campaign=jun_26&amp;amp;utm_term=bh&amp;amp;utm_content=web_page" rel="noopener noreferrer"&gt;LambdaTest Selenium Playground.&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.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%2AoBmxeMMA7KRyDG8V.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.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%2AoBmxeMMA7KRyDG8V.png"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.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%2Ae7du23ceJHyA9cqh.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.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%2Ae7du23ceJHyA9cqh.png"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;In case you have any confusion in choosing the best-suited selectors, do look at the &lt;a href="https://www.lambdatest.com/blog/xpath-vs-css-selectors/?utm_source=devto&amp;amp;utm_medium=organic&amp;amp;utm_campaign=jun_26&amp;amp;utm_term=bh&amp;amp;utm_content=blog" rel="noopener noreferrer"&gt;CSS Selectors vs XPath guide&lt;/a&gt; for making a more informed decision.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Resources/PageObject/KeyDefs&lt;/strong&gt;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Opening &amp;amp; Closing web browser on local grid&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Opening &amp;amp; Closing web browser on a particular OS on LambdaTest cloud grid&lt;br&gt;
&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt; Settings 
    Library  SeleniumLibrary
    Library  OperatingSystem
    Library  BuiltIn
    Library  ../Common/LambdaTestStatus.py


    *** Variables ***
    ${REMOTE_URL}       @hub.lambdatest.com/wd/hub"&amp;gt;http://%{LT_USERNAME}:%{LT_ACCESS_KEY}@hub.lambdatest.com/wd/hub
    ${TIMEOUT}          3000


    *** Keywords ***
    Open test browser
       [Arguments]     ${TEST_URL}     ${BROWSER}      ${CAPABILITIES}
       [Timeout]       ${TIMEOUT}
       Open browser    ${TEST_URL}     ${BROWSER}
       ...  remote_url=${REMOTE_URL}
       ...  desired_capabilities=${CAPABILITIES}


    Close test browser
       Run keyword if  '${REMOTE_URL}' != ''
       ...  report lambdatest status
       ...  ${TEST_NAME}
       ...  ${TEST_STATUS}
       Close Browser


    Open local test browser
       [Arguments]     ${TEST_URL}     ${BROWSER}
       [Timeout]       ${TIMEOUT}
       Open browser    ${TEST_URL}     ${BROWSER}


    Close local test browser
       Close Browser
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;To get started, we include all the relevant libraries, resource files, and variable files in the &lt;em&gt;Settings&lt;/em&gt; section. As seen below, we have imported the &lt;em&gt;SeleniumLibrary&lt;/em&gt;, which provides low-level keywords for automating interactions with the elements on the page.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.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%2Ao84xHMQW1RulKpQD.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.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%2Ao84xHMQW1RulKpQD.png"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;It is important to note that Selenium2Library has been renamed to SeleniumLibrary since Robot version 3.0. Hence, we have used SeleniumLibrary throughout this blog on using Robot Framework for parallel test execution.&lt;/p&gt;

&lt;p&gt;Open local test browser is a high-level keyword that takes two input arguments — Test URL and Test Browser. It internally uses the low-level keyword Open browser for instantiating the respective browser and opening the Test URL.&lt;/p&gt;

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

&lt;p&gt;&lt;em&gt;Close local test browser&lt;/em&gt; is a high-level keyword that internally uses the &lt;em&gt;Close browser&lt;/em&gt; for closing the instantiated browser.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.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%2A8ZlGTInLxwThMCFT.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.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%2A8ZlGTInLxwThMCFT.png"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Tests/LocalGrid&lt;/strong&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;This folder contains two .robot files that contain the implementation of the two test scenarios. Let’s look at some of the common aspects of the test files.&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;*** Settings ***


Resource    ../../Resources/PageObject/KeyDefs/Common.robot
Variables   ../../Resources/PageObject/Locators/Locators.py


Test Teardown  Common.Close local test browser


Library    BuiltIn


*** Variables ***


${site_url}         https://lambdatest.github.io/sample-todo-app/


*** Comments ***
# Configuration for first test scenario


*** Variables ***


&amp;amp;{lt_options_1}
   ...  browserName=Chrome


${BROWSER_1}          ${lt_options_1['browserName']}


*** Comments ***
# Configuration for second test scenario


*** Variables ***


&amp;amp;{lt_options_2}
   ...  browserName=Safari


${BROWSER_2}          ${lt_options_2['browserName']}


*** Test Cases ***


Example 1: [ToDo] Parallel Testing with Robot framework
   [tags]  ToDo App Automation - 1
   [Timeout]   ${TIMEOUT}
   Open local test browser ${site_url}     ${BROWSER_1}
   Maximize Browser Window
   Sleep  3s
   Page should contain element  ${FirstItem}
   Page should contain element  ${SecondItem}


   Click button  ${FirstItem} 
   Click button  ${SecondItem}

   Input text  ${ToDoText}  ${NewItemText}
   Click button  ${AddButton}
   ${response}    Get Text    ${NewAdditionText}
   Should Be Equal As Strings    ${response}    ${NewItemText}
   Sleep  5s
   Log    Completed - Example 1: [ToDo] Parallel Testing with Robot framework


Example 2: [ToDo] Parallel Testing with Robot framework
   [tags]  ToDo App Automation - 2
   [Timeout]   ${TIMEOUT}
   Open local test browser ${site_url}     ${BROWSER_2}
   Maximize Browser Window
   Sleep  3s
   Page should contain element  ${FirstItem}
   Page should contain element  ${SecondItem}
   Page should contain element  ${FifthItem}


   Click button  ${FirstItem} 
   Click button  ${SecondItem}
   Click button  ${FifthItem} 

   Input text  ${ToDoText}  ${NewItemText}
   Click button  ${AddButton}
   ${response}    Get Text    ${NewAdditionText}
   Should Be Equal As Strings    ${response}    ${NewItemText}
   Sleep  5s
   Log    Completed - Example 2: [ToDo] Parallel Testing with Robot framework
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;Since the &lt;em&gt;Test Teardown&lt;/em&gt; keyword executes after every test case and test suite, we invoke the Close local test browser keyword in &lt;em&gt;Common.robot&lt;/em&gt; for closing the browser instance.&lt;/p&gt;

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

&lt;p&gt;Test Scenarios (1) &amp;amp; (2) from Test Suite 1 will be executed in parallel on Chrome &amp;amp; Safari browsers. Two user-defined variables, &lt;em&gt;lt_options_1&lt;/em&gt; and &lt;em&gt;lt_options_2&lt;/em&gt;, are used for assigning the respective browser to the &lt;em&gt;browserName&lt;/em&gt; setting. In our case, they are set to Chrome and Safari respectively.&lt;/p&gt;

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

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Discover essential tips and &lt;a href="https://www.lambdatest.com/learning-hub/agile-interview-questions?utm_source=devto&amp;amp;utm_medium=organic&amp;amp;utm_campaign=jun_26&amp;amp;utm_term=bh&amp;amp;utm_content=learning_hub" rel="noopener noreferrer"&gt;agile interview question&lt;/a&gt; in our comprehensive tutorial. Prepare effectively and ace your next agile interview.&lt;/strong&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;The test URL is first opened using the &lt;em&gt;Open local test browser&lt;/em&gt; keyword with the &lt;a href="https://lambdatest.github.io/sample-todo-app/" rel="noopener noreferrer"&gt;test URL&lt;/a&gt; and test browser passed as input arguments. The web browser is maximized as it is considered a good Selenium practice.&lt;/p&gt;

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

&lt;p&gt;The &lt;em&gt;Page should contain element&lt;/em&gt; keyword of &lt;em&gt;SeleniumLibrary&lt;/em&gt; is used for checking the presence of the checkboxes (for first &amp;amp; second items) on the page. The items are checked using the &lt;em&gt;Click button&lt;/em&gt; keyword in &lt;em&gt;SeleniumLibrary&lt;/em&gt;.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.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%2ALAeN3a6GCTvk1yLR.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.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%2ALAeN3a6GCTvk1yLR.png"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Lastly, the &lt;em&gt;Input text&lt;/em&gt; keyword is used for inputting &lt;em&gt;“Yey Let’s add it to list” *as the new item in the ToDo app. The *Add&lt;/em&gt; button is clicked to add the said item to the list.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.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%2A0hlUW6Wxqt6W7Llb.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.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%2A0hlUW6Wxqt6W7Llb.png"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The &lt;em&gt;Get Text&lt;/em&gt; keyword in &lt;em&gt;SeleniumLibrary&lt;/em&gt; returns the text value of the element identified by the below-mentioned XPath.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.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%2APUHQmUHs_RKOHDNq.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.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%2APUHQmUHs_RKOHDNq.png"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The &lt;em&gt;Should Be Equal As Strings&lt;/em&gt; keyword in the &lt;em&gt;BuiltIn&lt;/em&gt; library compares the string value assigned to &lt;em&gt;${response}&lt;/em&gt; with the expected value. The test fails if both the string objects are unequal.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fcdn-images-1.medium.com%2Fmax%2F2480%2F0%2AwkXv2SSNcAU1Qh_G.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fcdn-images-1.medium.com%2Fmax%2F2480%2F0%2AwkXv2SSNcAU1Qh_G.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;*** Settings ***


Resource    ../../Resources/PageObject/KeyDefs/Common.robot
Variables   ../../Resources/PageObject/Locators/Locators.py


Test Teardown  Common.Close local test browser


Library    BuiltIn


*** Variables ***


${site_url}         https://www.lambdatest.com/selenium-playground/


*** Comments ***
# Configuration for first test scenario


*** Variables ***


&amp;amp;{lt_options}
   ...  browserName=Firefox


${BROWSER}          ${lt_options['browserName']}


*** Test Cases ***


Example 2: [Playground] Parallel Testing with Robot framework
   [tags]  Selenium Playground Automation
   [Timeout]   ${TIMEOUT}
   Open local test browser ${site_url}     ${BROWSER}
   Maximize Browser Window
   Page should contain element  xpath://a[.='Input Form Submit']


   Click link  ${SubmitButton}
   Page should contain element  ${Name}
   # Enter details in the input form


   # Name
   Input text  ${Name}   TestName
   # Email
   Input text  ${email}       testing@gmail.com
   # Password 
   Input text  ${passwd}       Password1
   # Company
   Input text  ${company}      LambdaTest
   # Website
   Input text  ${website}      https://wwww.lambdatest.com
   # Country
   select from list by value    ${country}     US
   # City
   Input text  ${city}       San Jose
   # Address 1
   Input text  ${address1}      Googleplex, 1600 Amphitheatre Pkwy
   # Website
   Input text  ${address2}       Mountain View, CA 94043
   # State
   Input text    ${state}          California
   # City
   Input text  ${city}       San Jose
   # Zip Code
   Input text  ${zipcode}      94088
   Sleep  5s


   Click button      ${FinalSubmission}
   Execute JavaScript    window.scrollTo(0, 0)
   Page should contain  ${SuccessText}
   Sleep  2s


   Log    Completed - Example 2: [Playground] Parallel Testing with Robot framework
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;blockquote&gt;
&lt;p&gt;*&lt;em&gt;Here’s 295+ &lt;a href="https://www.lambdatest.com/learning-hub/selenium-interview-questions?utm_source=devto&amp;amp;utm_medium=organic&amp;amp;utm_campaign=jun_26&amp;amp;utm_term=bh&amp;amp;utm_content=learning_hub" rel="noopener noreferrer"&gt;Selenium Interview Questions&lt;/a&gt; with Answers that will help you boost your confidence in an Interview. *&lt;/em&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;The test scenario in the above test suite is executed only on the local Firefox browser. Once the &lt;a href="https://lambdatest.com/selenium-playground?utm_source=devto&amp;amp;utm_medium=organic&amp;amp;utm_campaign=jun_26&amp;amp;utm_term=bh&amp;amp;utm_content=web_page" rel="noopener noreferrer"&gt;test URL&lt;/a&gt; is opened using the &lt;em&gt;Open local test browser *keyword, a check is performed to validate if *Input Form Submit *is present on the page. The WebElement is located using the XPath property, and its presence is validated using *Page should contain element *keyword of *SeleniumLibrary&lt;/em&gt;.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.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%2AJEjQZ4GmjpiUf52T.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.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%2AJEjQZ4GmjpiUf52T.png"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Once the click is performed, the &lt;a href="https://www.lambdatest.com/selenium-playground/input-form-demo?utm_source=devto&amp;amp;utm_medium=organic&amp;amp;utm_campaign=jun_26&amp;amp;utm_term=bh&amp;amp;utm_content=web_page" rel="noopener noreferrer"&gt;Input Form Demo&lt;/a&gt; opens up. Here, entries in the text boxes (i.e., Name, Email, Password, etc.) are populated using the &lt;em&gt;Input text *keyword in *SeleniumLibrary&lt;/em&gt;.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.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%2ASzFXkeSaJuJ6QsUc.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.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%2ASzFXkeSaJuJ6QsUc.png"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Get started with this complete &lt;a href="https://www.lambdatest.com/selenium?utm_source=devto&amp;amp;utm_medium=organic&amp;amp;utm_campaign=jun_26&amp;amp;utm_term=bh&amp;amp;utm_content=web_page" rel="noopener noreferrer"&gt;Selenium&lt;/a&gt; automation testing tutorial. Learn what Selenium is, its architecture, advantages and more for automated cross browser testing.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Lastly, the &lt;em&gt;select from list&lt;/em&gt; by value keyword is used for selecting “US” from the country drop-down.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.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%2AsBfvtlS2CGZGds8j.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.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%2AsBfvtlS2CGZGds8j.png"&gt;&lt;/a&gt;&lt;/p&gt;

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

&lt;p&gt;Once the inputted data is submitted by clicking the &lt;em&gt;Submit&lt;/em&gt; button, the &lt;em&gt;Execute JavaScript&lt;/em&gt; keyword in &lt;em&gt;SeleniumLibrary&lt;/em&gt; is used for triggering a window scroll to the start of the page. The keyword’s functionality is very much similar to that of &lt;a href="https://www.lambdatest.com/blog/how-to-use-javascriptexecutor-in-selenium-webdriver/?utm_source=devto&amp;amp;utm_medium=organic&amp;amp;utm_campaign=jun_26&amp;amp;utm_term=bh&amp;amp;utm_content=blog" rel="noopener noreferrer"&gt;JavaScriptExecutor in Selenium WebDriver&lt;/a&gt;!&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.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%2AHy8VV0fqlFlidlua.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.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%2AHy8VV0fqlFlidlua.png"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Finally, the presence of the text “&lt;em&gt;Thanks for contacting us, we will get back to you shortly&lt;/em&gt;.” is checked to verify if the page submission was successful.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.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%2AzbroKep71pJNJYvX.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.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%2AzbroKep71pJNJYvX.png"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;A comprehensive&lt;a href="https://www.lambdatest.com/learning-hub/user-acceptance-testing?utm_source=devto&amp;amp;utm_medium=organic&amp;amp;utm_campaign=jun_26&amp;amp;utm_term=bh&amp;amp;utm_content=learning_hub" rel="noopener noreferrer"&gt; Acceptance Testing &lt;/a&gt;(UAT) tutorial that covers what User Acceptance Testing is, its importance, benefits, and how to perform it with real-time examples.&lt;/strong&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;⚡💡&lt;em&gt;SeleniumLibrary&lt;/em&gt; is not thread-safe; hence there is no safety-net for concurrent access to browser instances or parallel test execution across multiple threads.&lt;/p&gt;

&lt;p&gt;Pabot runner eliminates the thread-safety issue, as parallelism is done at the process-level (not the thread-level). Each process in Pabot has its own memory space, due to which parallel processes can run in isolation without interfering with each other!&lt;/p&gt;

&lt;h2&gt;
  
  
  Execution
&lt;/h2&gt;

&lt;p&gt;Before using the Robot framework for parallel test execution, let’s run the same tests sequentially. This is to benchmark the test execution time (or performance) when the same test scenarios are run in parallel using Pabot runner.&lt;/p&gt;

&lt;p&gt;Please clone the repo in case you have not done the same. After completing the steps mentioned in the &lt;em&gt;Project Prerequisites&lt;/em&gt; section of this blog on using the Robot framework for parallel test execution.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Serial Test Execution (Purpose: Benchmarking)&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Invoke the following command to run the test scenarios located in &lt;em&gt;Tests/LocalGrid&lt;/em&gt; folder serially.&lt;/p&gt;

&lt;p&gt;It is essential to note that the time taken for execution also depends on factors like machine performance, browser performance, network connectivity, and more. Hence, the numbers being showcased might vary depending on the factors mentioned above.&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;robot Tests/LocalGrid/
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

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

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;A comprehensive &lt;a href="https://www.lambdatest.com/learning-hub/exploratory-testing?utm_source=devto&amp;amp;utm_medium=organic&amp;amp;utm_campaign=jun_26&amp;amp;utm_term=bh&amp;amp;utm_content=learning_hub" rel="noopener noreferrer"&gt;Exploratory Testing&lt;/a&gt; tutorial that covers what Exploratory Testing is, its importance, benefits, and how to perform it with real-time examples.&lt;/strong&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;As seen in the Test Report, the serial test execution of three test scenarios took around &lt;em&gt;50 seconds&lt;/em&gt; on my machine (i.e., MacBook Pro, 2 GHz Quad-Core Intel Core i5 processor).&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.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%2A4OhloKvx3tfSvhdy.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.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%2A4OhloKvx3tfSvhdy.png"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Let’s see what happens when the tests and/or test suites are run in parallel using Pabot runner.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Parallel Test Execution (Default: Suite-Level)&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;By default, Pabot splits the test execution at the test-suite level, with each process running a separate suite. Tests inside a test-suite (i.e., *.robot file) run in series, whereas the suites run in parallel.&lt;/p&gt;

&lt;p&gt;In our case, we have two test-suites where &lt;em&gt;test_todo_app.robot&lt;/em&gt; consists of two test scenarios, whereas &lt;em&gt;test_sel_playground.robot&lt;/em&gt; consists of a single test scenario.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.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%2A4ynsGejYtQy3qZmC.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.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%2A4ynsGejYtQy3qZmC.png"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Invoke the following command on the terminal to run Robot test suites in parallel using Pabot.&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;pabot — verbose — outputdir results — log local_test_parallel_log.html — report local_test_parallel_report.html Tests/LocalGrid/
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;&lt;em&gt;–&lt;/em&gt; &lt;em&gt;–verbose&lt;/em&gt; : Increase the verbosity level&lt;br&gt;
&lt;em&gt;–&lt;/em&gt; &lt;em&gt;–outputdir&lt;/em&gt; : Specify the directory used for storing the results&lt;br&gt;
&lt;em&gt;–&lt;/em&gt; &lt;em&gt;–log&lt;/em&gt; : Name of the log file generated post test execution&lt;br&gt;
&lt;em&gt;–&lt;/em&gt; &lt;em&gt;–report&lt;/em&gt; : Name of the report file (in specified format) generated post test execution&lt;/p&gt;

&lt;p&gt;Shown below is the execution snapshot which indicates the test execution was successful:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.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%2AJcB1ADkVG1DNX3DV.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.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%2AJcB1ADkVG1DNX3DV.png"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;A comprehensive &lt;a href="https://www.lambdatest.com/learning-hub/ui-testing?utm_source=devto&amp;amp;utm_medium=organic&amp;amp;utm_campaign=jun_26&amp;amp;utm_term=bh&amp;amp;utm_content=learning_hub" rel="noopener noreferrer"&gt;UI testing &lt;/a&gt;tutorial that covers what UI testing is, its importance, benefits, and how to perform it with real-time examples.&lt;/strong&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Here are some interesting things to notice from the test execution:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;Test suites are executing in parallel, with each process (PID: 78954 and PID:78955) running a separate suite&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;A file named &lt;em&gt;.pabotsuitenames&lt;/em&gt; is auto-generated during the phase of test execution. It contains information related to test suites when they are executed in parallel.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;em&gt;FileName — .pabotsuitenames&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.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%2AUZ1Se32_ilCjlR_o.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.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%2AUZ1Se32_ilCjlR_o.png"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;The total test execution time has come down from &lt;em&gt;50 seconds *(with serial execution) to *23 seconds&lt;/em&gt; (with suite-level parallelism).&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;a href="https://media2.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%2ACEMEAcvdPkH4Lsjo.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.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%2ACEMEAcvdPkH4Lsjo.png"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;In this &lt;a href="https://www.lambdatest.com/appium?utm_source=devto&amp;amp;utm_medium=organic&amp;amp;utm_campaign=jun_26&amp;amp;utm_term=bh&amp;amp;utm_content=web_page" rel="noopener noreferrer"&gt;Appium &lt;/a&gt;tutorial, learn about Appium and its benefits for mobile automation testing. Take a look at how Appium works and see how to perform Appium testing of your mobile applications.&lt;/strong&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;In a nutshell, suite-level parallelism with Pabot provided ~50 percent savings in test execution time!&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Parallel Test Execution (Test-Level)&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Pabot lets you achieve parallel execution at test-level through the &lt;em&gt;–testlevelsplit&lt;/em&gt; option. In case &lt;em&gt;.pabotsuitenames&lt;/em&gt; contains both tests and test-suites, then &lt;em&gt;–testlevelsplit&lt;/em&gt; will only affect new suites and split only them.&lt;/p&gt;

&lt;p&gt;Here, each process runs a single test case. For instance, if the test suite(s) have four tests &lt;em&gt;(e.g. T1, T2, T3, T4)&lt;/em&gt;, then four separate processes &lt;em&gt;(P1, P2, P3, P4)&lt;/em&gt; will run each test in parallel. Test-level parallelism must be preferred over suite-level parallelism in cases where suite(s) have a large number of &lt;em&gt;independent&lt;/em&gt; tests that can run concurrently.&lt;/p&gt;

&lt;p&gt;In our case, there are three test scenarios that can run in parallel.&lt;/p&gt;

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

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

&lt;p&gt;Invoke the following command on the terminal to run individual tests in parallel using Pabot.&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;pabot — verbose — testlevelsplit — outputdir results — log local_test_parallel_log.html — report local_test_parallel_report.html Tests/LocalGrid/
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;Since there are three tests in total, three parallel processes are invoked with each running a single test. As seen in the test execution screenshot, three parallel processes (PID: 92660, 92661, and 92662) handle one test scenario each.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.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%2A3PJlMtoepEiCV7vI.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.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%2A3PJlMtoepEiCV7vI.png"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The total test execution time has come down from &lt;em&gt;50 seconds&lt;/em&gt; (with serial execution) and &lt;em&gt;23 seconds&lt;/em&gt; (with suite-level parallelism) to &lt;em&gt;14 seconds&lt;/em&gt; (with test-level parallelism).&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.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%2ARFRtFNPjiid60dvX.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.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%2ARFRtFNPjiid60dvX.png"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;It is important to note that the total execution time might vary from one run to another. For instance, subsequent runs (with the same load conditions) resulted in &lt;em&gt;16 seconds &amp;amp; 17 seconds&lt;/em&gt;, respectively.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Customize Number of Parallel Executors&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;There are scenarios where you might want to invoke more/less parallel executors to achieve more out of parallelism with the Robot framework. This is where the &lt;em&gt;–processes&lt;/em&gt; option lets you customize the number of parallel executors for test case execution. It is recommended to take the available resources (physical CPU cores, memory, etc.) into account when customizing the number of parallel executors.&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;pabot — processes &amp;lt;num_parallel_processes&amp;gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;Let’s do a test split execution with 2 parallel processes and see the difference. Invoke the following command on the terminal:&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;pabot — verbose — testlevelsplit — processes 2 — outputdir results — log local_test_parallel_log.html — report local_test_parallel_report.html Tests/LocalGrid/
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;As seen from the execution snapshot and test report, the total test execution time is &lt;em&gt;25 seconds&lt;/em&gt; (i.e., same as test-suite level execution). However, the execution time might vary from one test run to another.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.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%2AbTGR23oPjwehfeNE.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.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%2AbTGR23oPjwehfeNE.png"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.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%2AjfuSxNVx7okEF5iU.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.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%2AjfuSxNVx7okEF5iU.png"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;To summarize, both thread-level and suite-level parallelism can potentially accelerate test execution time. However, you must keep a watchful eye on test dependencies and available resources (i.e., CPU cores, memory, etc.) to avoid conflicts or flaky tests (or unexpected results).&lt;/p&gt;

&lt;h2&gt;
  
  
  Parallel Testing using Pabot on Cloud Grid
&lt;/h2&gt;

&lt;p&gt;So far, we have demonstrated using the Robot framework for parallel test execution with Pabot on the local machine (macOS Ventura). So far, so good 🙂However, a challenge arises if tests have to be performed on different platforms and browser combinations. What if we have to execute the tests mentioned above on the latest and/or older version of Chrome, Firefox, etc., installed on Windows 10 and Windows 11 machines?&lt;/p&gt;

&lt;p&gt;In such cases, we can leverage the &lt;a href="https://www.lambdatest.com/blog/benefits-of-cloud-testing/#WATB?utm_source=devto&amp;amp;utm_medium=organic&amp;amp;utm_campaign=jun_26&amp;amp;utm_term=bh&amp;amp;utm_content=blog" rel="noopener noreferrer"&gt;benefits of cloud testing&lt;/a&gt; as it helps in executing automated UI tests at scale reliably and securely. With the rapid proliferation of cloud technologies, it is imperative to lean on &lt;a href="https://www.lambdatest.com/blog/benefits-of-cloud-testing/#WICT?utm_source=devto&amp;amp;utm_medium=organic&amp;amp;utm_campaign=jun_26&amp;amp;utm_term=bh&amp;amp;utm_content=blog" rel="noopener noreferrer"&gt;cloud testing&lt;/a&gt; to realize:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;Reduced infrastructure costs&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Better &lt;a href="https://www.lambdatest.com/learning-hub/test-coverage?utm_source=devto&amp;amp;utm_medium=organic&amp;amp;utm_campaign=jun_26&amp;amp;utm_term=bh&amp;amp;utm_content=learning_hub" rel="noopener noreferrer"&gt;test coverage&lt;/a&gt;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Shortened developer feedback&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Improved product quality&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;In this section of the &lt;a href="https://www.lambdatest.com/selenium-python-testing?utm_source=devto&amp;amp;utm_medium=organic&amp;amp;utm_campaign=jun_26&amp;amp;utm_term=bh&amp;amp;utm_content=web_page" rel="noopener noreferrer"&gt;Selenium Python testing&lt;/a&gt; tutorial using Robot framework, we will demonstrate how to port the existing Robot tests so that it runs on LambdaTest cloud grid. LambdaTest is a cloud-based digital experience testing platform that lets you run &lt;a href="https://www.lambdatest.com/selenium-automation-testing-with-robot-framework?utm_source=devto&amp;amp;utm_medium=organic&amp;amp;utm_campaign=jun_26&amp;amp;utm_term=bh&amp;amp;utm_content=web_page" rel="noopener noreferrer"&gt;Selenium automation tests with Robot&lt;/a&gt; on 3,000+ browsers and operating systems online!&lt;br&gt;
&lt;iframe width="710" height="399" src="https://www.youtube.com/embed/SqQ8SugRDos"&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?utm_source=devto&amp;amp;utm_medium=organic&amp;amp;utm_campaign=jun_26&amp;amp;utm_term=bh&amp;amp;utm_content=video" rel="noopener noreferrer"&gt;LambdaTest YouTube Channel &lt;/a&gt;and stay updated with the latest tutorials around &lt;a href="https://www.lambdatest.com/automation-testing?utm_source=devto&amp;amp;utm_medium=organic&amp;amp;utm_campaign=jun_26&amp;amp;utm_term=bh&amp;amp;utm_content=web_page" rel="noopener noreferrer"&gt;automation testing&lt;/a&gt;, &lt;a href="https://www.lambdatest.com/selenium-automation?utm_source=devto&amp;amp;utm_medium=organic&amp;amp;utm_campaign=jun_26&amp;amp;utm_term=bh&amp;amp;utm_content=web_page" rel="noopener noreferrer"&gt;Selenium testing&lt;/a&gt;, &lt;a href="https://www.lambdatest.com/learning-hub/cypress-tutorial?utm_source=devto&amp;amp;utm_medium=organic&amp;amp;utm_campaign=jun_26&amp;amp;utm_term=bh&amp;amp;utm_content=learning_hub" rel="noopener noreferrer"&gt;Cypress&lt;/a&gt;, CI/CD, and more.&lt;/p&gt;

&lt;p&gt;Visit our support documentation to &lt;a href="https://www.lambdatest.com/support/docs/python-with-selenium-running-python-automation-scripts-on-lambdatest-selenium-grid/?utm_source=devto&amp;amp;utm_medium=organic&amp;amp;utm_campaign=jun_26&amp;amp;utm_term=bh&amp;amp;utm_content=support_doc" rel="noopener noreferrer"&gt;get started with Selenium Python testing.&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;To get started, you need to create an account on LambdaTest. Post that, please make a note of your username and access key, which are available in the &lt;a href="https://accounts.lambdatest.com/detail/profile?utm_source=devto&amp;amp;utm_medium=organic&amp;amp;utm_campaign=jun_26&amp;amp;utm_term=bh&amp;amp;utm_content=web_page" rel="noopener noreferrer"&gt;LambdaTest Profile&lt;/a&gt; section.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.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%2ApLeJdZL8e5jtHPL4.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.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%2ApLeJdZL8e5jtHPL4.png"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Now export the environment variables &lt;em&gt;LT_USERNAME&lt;/em&gt; and &lt;em&gt;LT_ACCESS_KEY&lt;/em&gt;, as they are required for running tests on LambdaTest.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.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%2Aor5QT9FrmrvhU293.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.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%2Aor5QT9FrmrvhU293.png"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Project Setup&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Prerequisites&lt;/em&gt; and &lt;em&gt;Project setup&lt;/em&gt; remain almost the same as shown in the ‘Parallel Testing Demonstration’ earlier. The only difference is that the ported tests are located in the &lt;em&gt;Tests/CloudGrid&lt;/em&gt; folder.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.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%2As5Cy5jQOQdwEKovc.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.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%2As5Cy5jQOQdwEKovc.png"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Implementation (Porting Changes for Execution on Cloud Grid)
&lt;/h2&gt;

&lt;p&gt;As the first step, we copy the relevant capabilities from the &lt;a href="https://www.lambdatest.com/capabilities-generator/?utm_source=devto&amp;amp;utm_medium=organic&amp;amp;utm_campaign=jun_26&amp;amp;utm_term=bh&amp;amp;utm_content=web_page" rel="noopener noreferrer"&gt;LambdaTest Capabilities Generator.&lt;/a&gt; Since &lt;em&gt;SeleniumLibrary *is compatible with &lt;a href="https://www.lambdatest.com/learning-hub/selenium-4?utm_source=devto&amp;amp;utm_medium=organic&amp;amp;utm_campaign=jun_26&amp;amp;utm_term=bh&amp;amp;utm_content=learning_hub" rel="noopener noreferrer"&gt;Selenium 4,&lt;/a&gt; we choose the capabilities for Python with Selenium 4. Though there is no separate mention of *Robot&lt;/em&gt; in the &lt;em&gt;‘Select your framework’&lt;/em&gt;, you can still use the capabilities-related nomenclature in the implementation.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.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%2AJIJotwSAJkOakQt_.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.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%2AJIJotwSAJkOakQt_.png"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;For instance, you can use the following when setting the capabilities (all in String format):&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
    &lt;tr&gt;
        &lt;td&gt;CAPABILITIES (SELENIUM 4)&lt;/td&gt;
        &lt;td&gt;SAMPLE VALUES&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
        &lt;td&gt;platformName&lt;/td&gt;
        &lt;td&gt;Windows 11, MacOS Ventura, etc.&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
        &lt;td&gt;browserName&lt;/td&gt;
        &lt;td&gt;Chrome, Firefox, Safari, etc.&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
        &lt;td&gt;browserVersion&lt;/td&gt;
        &lt;td&gt;String value based on the browser used for testing&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
        &lt;td&gt;w3c&lt;/td&gt;
        &lt;td&gt;True (when tests are executed using Selenium 4)&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
        &lt;td&gt;Name (optional)&lt;/td&gt;
        &lt;td&gt;Test name&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
        &lt;td&gt;Build (optional)&lt;/td&gt;
        &lt;td&gt;Build name&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
        &lt;td&gt;Project (optional)&lt;/td&gt;
        &lt;td&gt;Project name&lt;/td&gt;
    &lt;/tr&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;The first test scenario (for ToDo app) is to be tested on Chrome (latest version) on Windows 11. Shown below are the capabilities:&lt;/p&gt;

&lt;p&gt;&lt;em&gt;FileName — Tests/CloudGrid/test_todo_app.robot&lt;/em&gt;&lt;/p&gt;

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

&lt;p&gt;&lt;em&gt;Capabilities for Test Scenario — 1 (Test Suite — 1)&lt;/em&gt;&lt;/p&gt;

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

&lt;p&gt;&lt;em&gt;Capabilities for Test Scenario — 2 (Test Suite — 1)&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;On similar lines, we updated the capabilities for browser &amp;amp; combinations used in Test Scenario — 1 (of Test Suite — 2).&lt;/p&gt;

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

&lt;p&gt;&lt;em&gt;Capabilities for Test Scenario — 1 (Test Suite — 2)&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;We have added a few &lt;em&gt;Variables&lt;/em&gt; and &lt;em&gt;High-level keywords&lt;/em&gt; that will be used for opening test browser(s) on the desired operating system (or platform).&lt;/p&gt;

&lt;p&gt;&lt;em&gt;FileName — Resources/PageObject/KeyDefs/Common.robot&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.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%2AcF7BJxQWJmfJE9I1.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.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%2AcF7BJxQWJmfJE9I1.png"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;em&gt;${REMOTE_URL}&lt;/em&gt; is the LambdaTest Hub address which will be further used by the &lt;em&gt;Open Browser&lt;/em&gt; keyword in Robot to open the desired &lt;em&gt;browser&lt;/em&gt; (e.g., Chrome) on the respective platform (e.g., Windows 11).&lt;/p&gt;

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

&lt;p&gt;&lt;a href="https://robotframework.org/SeleniumLibrary/SeleniumLibrary.html#Open%20Browser" rel="noopener noreferrer"&gt;*Image Source&lt;/a&gt;*&lt;/p&gt;

&lt;p&gt;Two high-level keywords are created for opening and terminating the browser instance instantiated on the desired platform (or OS). &lt;em&gt;LambdaTest Hub URL&lt;/em&gt; and &lt;em&gt;Desired Capabilities&lt;/em&gt; are passed as parameters to the low-level keyword &lt;em&gt;Open browser&lt;/em&gt;.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;FileName — Resources/PageObject/KeyDefs/Common.robot&lt;/em&gt;&lt;/p&gt;

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

&lt;p&gt;&lt;em&gt;High-level keyword: Open test browser&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.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%2AbXRiH76i_1l88bOw.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.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%2AbXRiH76i_1l88bOw.png"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;em&gt;High-level keyword: Close test browser&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;Now that the infrastructure-level settings are all done, let’s look at the changes in the test logic (&lt;em&gt;in Tests/CloudGrid/test_sel_playground.robot)&lt;/em&gt;.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Step 1 (Addition of Grid URL and Capabilities)&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;As mentioned earlier, we first add the desired capabilities comprising of the remote browser, version, and user-defined capabilities (i.e. Project name, Test name, etc.) for LambdaTest.&lt;/p&gt;

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

&lt;p&gt;&lt;strong&gt;Step 2 (Open Test Browser on Test OS )&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;The high-level keyword Open &lt;em&gt;test browser&lt;/em&gt; which accepts three parameters — &lt;em&gt;Test URL, Browser, and Desired Capabilities.&lt;/em&gt;&lt;/p&gt;

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

&lt;p&gt;There is no change in the core test logic, except that &lt;em&gt;Close test browser&lt;/em&gt; high-level keyword is used for closing the instantiated browser instance.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.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%2AnzUl-kT4TdRQMGn9.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.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%2AnzUl-kT4TdRQMGn9.png"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Execution
&lt;/h2&gt;

&lt;p&gt;Before execution, set the environment variables &lt;em&gt;LT_USERNAME&lt;/em&gt; and &lt;em&gt;LT_ACCESS_KEY&lt;/em&gt; on the terminal. You can find the same in the LambdaTest Profile section.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.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%2AoFYluNVBtLFz7E1T.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.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%2AoFYluNVBtLFz7E1T.png"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;macOS/Linux&lt;/strong&gt;&lt;/p&gt;

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

&lt;/div&gt;

&lt;p&gt;&lt;strong&gt;Windows&lt;/strong&gt;&lt;/p&gt;

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

&lt;/div&gt;

&lt;p&gt;Now that the environment variables are set, invoke the following command on the terminal to run individual tests in parallel (on LambdaTest Grid) using Pabot.&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;pabot — verbose — testlevelsplit — outputdir results — log cloud_test_parallel_log.html — report cloud_test_parallel_report.html Tests/CloudGrid/
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;The above command will kick-start three processes, each of which runs a test scenario (or test case) in parallel. Shown below is the terminal and LambdaTest dashboard screenshot(s) that indicates that the parallel execution resulted in Success. 🚀&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.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%2AJRROgZdD37ZbxyY0.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.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%2AJRROgZdD37ZbxyY0.png"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.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%2AxY9tQhKLIZD2J2LS.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.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%2AxY9tQhKLIZD2J2LS.png"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Execution in Progress&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.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%2AEHqQLKGfano3KxzH.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.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%2AEHqQLKGfano3KxzH.png"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Completion of Execution&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.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%2Azs6nqjic7_WSXPDp.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.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%2Azs6nqjic7_WSXPDp.png"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Video Grab of Test Execution&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;With this, we have successfully used the Robot framework for parallel test execution with Pabot on the local grid and LambdaTest cloud grid. Testing Cloud with Robot framework can be leveraged when you want to run tests at scale in continuous CI/CD pipelines!&lt;/p&gt;

&lt;p&gt;Are you enthusiastic about becoming proficient in &lt;a href="https://www.lambdatest.com/python-automation-testing?utm_source=devto&amp;amp;utm_medium=organic&amp;amp;utm_campaign=jun_26&amp;amp;utm_term=bh&amp;amp;utm_content=web_page" rel="noopener noreferrer"&gt;Python automation testing?&lt;/a&gt; Opting for a &lt;a href="https://www.lambdatest.com/certifications/selenium-python-101?utm_source=devto&amp;amp;utm_medium=organic&amp;amp;utm_campaign=jun_26&amp;amp;utm_term=bh&amp;amp;utm_content=certification" rel="noopener noreferrer"&gt;Selenium Python 101 certification&lt;/a&gt; program is an excellent choice to kickstart your path towards becoming an automation testing specialist and improving your Python abilities. Enrolling in this program will provide you with a solid groundwork for effectively utilizing Selenium Python in your testing projects, helping you excel in your automation testing journey.&lt;/p&gt;

&lt;p&gt;In addition, you can also refer to our &lt;a href="https://www.lambdatest.com/learning-hub/robot-framework-interview-questions?utm_source=devto&amp;amp;utm_medium=organic&amp;amp;utm_campaign=jun_26&amp;amp;utm_term=bh&amp;amp;utm_content=learning_hub" rel="noopener noreferrer"&gt;Robot framework interview questions and answers&lt;/a&gt; in case you are preparing for the interview or looking to expand your knowledge on Robot framework.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;&lt;a href="https://www.lambdatest.com/learning-hub/webdriver?utm_source=devto&amp;amp;utm_medium=organic&amp;amp;utm_campaign=jun_26&amp;amp;utm_term=bh&amp;amp;utm_content=learning_hub" rel="noopener noreferrer"&gt;WebDriver&lt;/a&gt; is a remote programming interface that can be used to control, or drive, a browser either locally or on a remote machine. Learn more in this complete Selenium WebDriver Tutorial.&lt;/strong&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2&gt;
  
  
  It’s a Wrap
&lt;/h2&gt;

&lt;p&gt;Thanks for getting this far! Though the Robot framework supports sequential test execution (out-of-the-box), tests can be run in parallel by leveraging the Pabot test runner. Pabot lets you parallelize test-suites as well as tests, that too without major code modifications.&lt;/p&gt;

&lt;p&gt;As we all know, excessive parallelism can cause test flakiness if the tests are not designed to handle concurrent execution. It becomes essential to follow Selenium’s best practices when opting for parallel test execution. You can also leverage the benefits of a cloud grid like LambdaTest in case you are looking to capitalize on the benefits of cloud testing and continuous integration.&lt;/p&gt;

&lt;p&gt;Until next time, happy testing!&lt;/p&gt;

&lt;h2&gt;
  
  
  Frequently Asked Questions (FAQs)
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Does Robot Framework support parallel execution?
&lt;/h3&gt;

&lt;p&gt;Yes, Robot Framework does support parallel execution. Starting from version 3.2, Robot Framework introduced native support for parallel test execution.&lt;/p&gt;

&lt;p&gt;To enable parallel execution in Robot Framework, you can use the &lt;em&gt;–parallel&lt;/em&gt; command-line option or the &lt;em&gt;–processes&lt;/em&gt; option to specify the number of parallel processes to use.&lt;/p&gt;

</description>
      <category>testing</category>
      <category>webdev</category>
      <category>beginners</category>
      <category>programming</category>
    </item>
    <item>
      <title>MSTest Setup Environment Tutorial: In 3 Easy Steps</title>
      <dc:creator>himanshuseth004</dc:creator>
      <pubDate>Wed, 12 Apr 2023 07:05:19 +0000</pubDate>
      <link>https://forem.com/testmuai/mstest-setup-environment-tutorial-in-3-easy-steps-47aj</link>
      <guid>https://forem.com/testmuai/mstest-setup-environment-tutorial-in-3-easy-steps-47aj</guid>
      <description>&lt;p&gt;MSTest setup is a popular open-source test framework that is shipped along with the Visual Studio IDE. It is also referred to as Visual Studio Unit Testing Framework. However, MSTest Setup is more synonymous within the developer community.&lt;/p&gt;

&lt;p&gt;As the MSTest framework comes pre-bundled with Visual Studio, many developers prefer MSTest over other C# frameworks such as NUnit, xUnit.net, etc. for &lt;a href="https://www.lambdatest.com/selenium-automation?utm_source=devto&amp;amp;utm_medium=organic&amp;amp;utm_campaign=apr12_bh&amp;amp;utm_term=bh&amp;amp;utm_content=webpage" rel="noopener noreferrer"&gt;test automation&lt;/a&gt;. The latest version of MSTest is 2, a major overhaul over its predecessor. The earlier version of MSTest i.e. MSTest V1 was not open-source and lacked many good features (particularly parallel test execution) which were supported by other &lt;a href="https://www.lambdatest.com/blog/top-selenium-csharp-framework/?utm_source=devto&amp;amp;utm_medium=organic&amp;amp;utm_campaign=apr12_bh&amp;amp;utm_term=bh&amp;amp;utm_content=blog" rel="noopener noreferrer"&gt;Selenium C# testing frameworks&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;MSTest V2 is cross-platform compatible, extensible, and supports parallel test execution which is one of the major requirements for performing Selenium C# testing. It also supports data-driven testing as methods can be executed multiple times by providing different input arguments. The MSTest framework recognizes the test via annotations/attributes under which the test code is present. [TestInitialize], [TestMethod], [TestCleanup] are the most widely used attributes by developers who make use of the &lt;a href="https://www.lambdatest.com/selenium-automation-testing-with-MSTest?utm_source=devto&amp;amp;utm_medium=organic&amp;amp;utm_campaign=apr12_bh&amp;amp;utm_term=bh&amp;amp;utm_content=webpage" rel="noopener noreferrer"&gt;MSTest framework for automation testing.&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;In this MSTest setup tutorial, I’ll take a detailed look at setting up the MSTest framework which can help you get started with MSTest on Visual Studio.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;&lt;em&gt;A comprehensive end to end Testing tutorial that covers what &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=apr12_bh&amp;amp;utm_term=bh&amp;amp;utm_content=learning_hub" rel="noopener noreferrer"&gt;E2E Testing&lt;/a&gt; is, its importance, benefits, and how to perform it with real-time examples.&lt;/em&gt;&lt;/strong&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  MSTest Setup Visual Studio For Development and Testing
&lt;/h2&gt;

&lt;p&gt;Before moving ahead in this MSTest tutorial for environment setup, I’ll quickly address how to set up Visual Studio IDE for MSTest test case development. Shown below are the steps for installing Visual Studio 2019 (VS 2019):&lt;/p&gt;

&lt;h3&gt;
  
  
  Download The Latest Visual Studio
&lt;/h3&gt;

&lt;p&gt;Download the latest version of Visual Studio i.e. Visual Studio 2019. You can choose from either Professional/Community/Edition of Visual Studio. The selection should be based on your project requirements. In this MSTest tutorial for environment setup, I’m using the Community Edition of VS 2019.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.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%2AZbkAkJQ-68Ro103D.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.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%2AZbkAkJQ-68Ro103D.png"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  Install Necessary Platform For Development &amp;amp; Testing
&lt;/h3&gt;

&lt;p&gt;In this MSTest tutorial for environment setup, the MSTest framework will be used for testing so we’ll be installing the necessary packages for development on the Windows platform.&lt;/p&gt;

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

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

&lt;h3&gt;
  
  
  Sign-In To Visual Studio IDE
&lt;/h3&gt;

&lt;p&gt;In order to leverage the features of Visual Studio 2019, it is recommended that you sign-in to the IDE so that you can make use of the useful features of Visual Studio 2019 such as pushing code to private Git repository, syncing settings, and more.&lt;/p&gt;

&lt;p&gt;In the next section of the MSTest tutorial on environment setup, I’ll take a look at steps to be followed for the installation of the mandatory packages required for executing the tests based on the MSTest framework.&lt;/p&gt;

&lt;p&gt;This MSTest Tutorial for beginners and professionals will help you learn how to use MSTest framework with Selenium C# for performing Selenium automation testing.&lt;/p&gt;

&lt;p&gt;&lt;iframe width="710" height="399" src="https://www.youtube.com/embed/2XUQdN-Bi2o"&gt;
&lt;/iframe&gt;
&lt;/p&gt;

&lt;h2&gt;
  
  
  Installing The MSTest Framework &amp;amp; MSTest Adapter
&lt;/h2&gt;

&lt;p&gt;For executing &lt;a href="https://www.lambdatest.com/?utm_source=devto&amp;amp;utm_medium=organic&amp;amp;utm_campaign=apr12_bh&amp;amp;utm_term=bh&amp;amp;utm_content=webpage" rel="noopener noreferrer"&gt;automated browser testing&lt;/a&gt; using the MSTest framework, you need to install the corresponding test adapter as the adapter enables the execution of the test code. Installation of the MSTest adapter can be done by executing the package manager commands on the console or by accessing the package manager from the GUI. In this MSTest tutorial for environment setup, I’ll show you how to set up using both the options.&lt;/p&gt;

&lt;p&gt;In order to install the required packages, we perform the following steps:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Step 1:&lt;/strong&gt; Create a new project of the type ‘MSTest Test Project (.Net Core)’ in Visual Studio.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.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%2AWQmwzRdBFhdhJ5tJ.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.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%2AWQmwzRdBFhdhJ5tJ.png"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Step 2:&lt;/strong&gt; Since the project is based on the MSTest framework, the default C# file that comes along with the project has attributes &lt;em&gt;[TestMethod]&lt;/em&gt; and &lt;em&gt;[TestClass]&lt;/em&gt; in it.&lt;/p&gt;

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

&lt;p&gt;Even if you have not created a MSTest Test Project, you could still make use of the MSTest framework by installing the framework using the NuGet Package Manager.&lt;/p&gt;

&lt;p&gt;Take this certification to master the fundamentals of Selenium automation testing with C# and prove your credibility as a tester.&lt;/p&gt;

&lt;p&gt;Here’s a short glimpse of the Selenium C# 101 certification from LambdaTest:&lt;br&gt;
&lt;iframe width="710" height="399" src="https://www.youtube.com/embed/ZU85Mjwgv54"&gt;
&lt;/iframe&gt;
&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;&lt;em&gt;Automated &lt;a href="https://www.lambdatest.com/blog/automated-functional-testing-what-it-is-how-it-helps/?utm_source=devto&amp;amp;utm_medium=organic&amp;amp;utm_campaign=apr12_bh&amp;amp;utm_term=bh&amp;amp;utm_content=blog" rel="noopener noreferrer"&gt;Functional Testing&lt;/a&gt; tests helps to ensure that your web app works as it was intented to. Learn more about functional tests, and how automating them can give you a faster release cycle.&lt;/em&gt;&lt;/strong&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Installing MSTest With NuGet Package
&lt;/h2&gt;

&lt;p&gt;For installing the required MSTest packages for &lt;a href="https://www.lambdatest.com/?utm_source=devto&amp;amp;utm_medium=organic&amp;amp;utm_campaign=apr12_bh&amp;amp;utm_term=bh&amp;amp;utm_content=webpage" rel="noopener noreferrer"&gt;cross browser testing&lt;/a&gt;, you can either make use of the Package Manager commands or use the IDE to achieve the task. In case you are just getting started with Selenium C# testing using MSTest, I’ll recommend the IDE option.&lt;/p&gt;

&lt;h3&gt;
  
  
  Using Visual Studio IDE To Install The MSTest Framework
&lt;/h3&gt;

&lt;p&gt;To install the MSTest framework and the other mandatory packages for Selenium test automation with MSTest, perform the following steps using the Visual Studio IDE:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Step 1:&lt;/strong&gt; Add Console.WriteLine(“Selenium C#”) to the newly created .cs file.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Step 2:&lt;/strong&gt; Navigate to &lt;strong&gt;Tools -&amp;gt; NuGet Package Manager -&amp;gt; Manager NuGet Packages&lt;/strong&gt; for Solution and search for the following packages in the Browse tab.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;MSTest.TestAdapter&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;MSTest.TestFramework&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Microsoft.NET.Test.Sdk&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;a href="https://media2.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%2AiYzEEpGoYJ3q9Lid.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.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%2AiYzEEpGoYJ3q9Lid.png"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Step 3:&lt;/strong&gt; Click on the &lt;strong&gt;Install&lt;/strong&gt; button and press &lt;strong&gt;OK&lt;/strong&gt; for each of these packages so that package is installed for the current project.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.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%2A9OFKtql0bcF0QpC0.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.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%2A9OFKtql0bcF0QpC0.png"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  Using Package Manager Commands To Install The MSTest framework
&lt;/h3&gt;

&lt;p&gt;Instead of using the IDE, the Package Manager (PM) commands can also be used for installing the required MSTest packages.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Step 1:&lt;/strong&gt; For executing commands from the PM console, navigate to &lt;strong&gt;Tools -&amp;gt; NuGet Package Manager -&amp;gt; Package Manager Console&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.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%2AmAfmgjvwOHQbnFXa.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.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%2AmAfmgjvwOHQbnFXa.png"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Step 2:&lt;/strong&gt; For installation of the packages, we use the Install-Package command with the required as the argument to the command.&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Install-Package MSTest.TestAdapter
Install-Package MSTest.TestFramework
Install-Package Microsoft.NET.Test.Sdk
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;Shown below is the installation screenshot:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.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%2AN727-Gb_oDVVCAUK.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.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%2AN727-Gb_oDVVCAUK.png"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Step 3:&lt;/strong&gt; You can confirm whether the packages are installed by executing the Get-Package command on Package Manager (PM) Console.&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;PM&amp;gt; Get-Package

Id                                  Versions
--                                  --------
MSTest.TestFramework                {2.0.0}
MSTest.TestAdapter                  {2.0.0}
coverlet.collector                  {1.0.1}
Microsoft.NET.Test.Sdk              {16.2.0}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;The version of MSTest framework is 2.0.0 or MSTest V2 which is currently the latest version of the MSTest framework.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;&lt;em&gt;Through this &lt;a href="https://www.lambdatest.com/learning-hub/usability-testing?utm_source=devto&amp;amp;utm_medium=organic&amp;amp;utm_campaign=apr12_bh&amp;amp;utm_term=bh&amp;amp;utm_content=learning_hub" rel="noopener noreferrer"&gt;usability testing&lt;/a&gt; tutorial, you will learn how usability testing is a great way to discover unexpected bugs, find what is unnecessary or unused before going any further, and have unbiased opinions from an outsider.&lt;/em&gt;&lt;/strong&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Wrapping It Up
&lt;/h2&gt;

&lt;p&gt;With this MSTest environment setup example, I demonstrated how to get started with the MSTest framework for Visual Studio. The steps listed in this MSTest tutorial for environment setup will help you in performing automation testing with C#.&lt;/p&gt;

&lt;p&gt;That’s all for this MSTest tutorial on environment setup, I hope this cleared all your doubts with respect to the setup. In case of any further questions, please feel free to reach out to us in the comments section below. Help us to reach out to your peers and colleagues looking by retweeting us or sharing our posts on Social Media. &lt;strong&gt;Happy Testing!!&lt;/strong&gt;?&lt;/p&gt;

</description>
      <category>webdev</category>
      <category>beginners</category>
      <category>programming</category>
      <category>tutorial</category>
    </item>
    <item>
      <title>MSTest‌ ‌Tutorial:‌ ‌Running‌ ‌First‌ ‌Selenium‌ ‌Automation‌ ‌Script‌</title>
      <dc:creator>himanshuseth004</dc:creator>
      <pubDate>Tue, 11 Apr 2023 10:27:50 +0000</pubDate>
      <link>https://forem.com/testmuai/hbhv-521b</link>
      <guid>https://forem.com/testmuai/hbhv-521b</guid>
      <description>&lt;p&gt;MSTest is the default &lt;a href="https://www.lambdatest.com/automation-testing?utm_source=devto&amp;amp;utm_medium=organic&amp;amp;utm_campaign=apr12_bh&amp;amp;utm_term=bh&amp;amp;utm_content=webpage" rel="noopener noreferrer"&gt;test automation&lt;/a&gt; framework which comes bundled with Visual Studio. It started out as a command-line tool for executing tests and was referred as Visual Studio Unit Testing Framework; however the name MSTest is more synonymous with the developers.&lt;/p&gt;

&lt;p&gt;In the previous MSTest tutorial, I had a look at &lt;a href="https://www.lambdatest.com/blog/mstest-tutorial-environment-setup-for-selenium-testing/?utm_source=devto&amp;amp;utm_medium=organic&amp;amp;utm_campaign=apr12_bh&amp;amp;utm_term=bh&amp;amp;utm_content=blog" rel="noopener noreferrer"&gt;setting up the MSTest framework&lt;/a&gt; in Visual Studio. In this MSTest tutorial for Selenium automation, I’ll help you run your first script for automated web testing with Selenium C# and MSTest.&lt;/p&gt;

&lt;p&gt;If you’re new to Selenium and wondering what it is then we recommend checking out our guide — &lt;a href="https://www.lambdatest.com/selenium?utm_source=devto&amp;amp;utm_medium=organic&amp;amp;utm_campaign=apr12_bh&amp;amp;utm_term=bh&amp;amp;utm_content=webpage" rel="noopener noreferrer"&gt;What is Selenium?&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;&lt;em&gt;Test your native, hybrid, and web apps across all legacy and latest mobile operating systems on the most powerful Android emulator online: &lt;a href="https://www.lambdatest.com/android-emulator-online?utm_source=devto&amp;amp;utm_medium=organic&amp;amp;utm_campaign=apr12_bh&amp;amp;utm_term=bh&amp;amp;utm_content=webpage" rel="noopener noreferrer"&gt;https://www.lambdatest.com/android-emulator-online&lt;/a&gt;&lt;/em&gt;&lt;/strong&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Requirements For Running Your First MSTest Script
&lt;/h2&gt;

&lt;p&gt;In this MSTest tutorial, I’ll use Chrome browser to verify the functionalities in the code. So, you’ll need to install the Chrome WebDriver on the machine where you’ve installed the MSTest framework and &lt;a href="https://www.lambdatest.com/blog/selenium-webdriver-tutorial-with-examples/?utm_source=devto&amp;amp;utm_medium=organic&amp;amp;utm_campaign=apr12_bh&amp;amp;utm_term=bh&amp;amp;utm_content=blog" rel="noopener noreferrer"&gt;Selenium WebDriver.&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Different web browsers have their own corresponding Selenium WebDrivers. These WebDrivers are crucial to interact with the web elements on a page for automated web testing with Selenium C#. You can download the Selenium WebDriver for browsers such as Opera, Firefox, Chrome, Internet Explorer, Microsoft Edge, etc. using the following links:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
    &lt;tr&gt;
        &lt;td&gt;BROWSER&lt;/td&gt;
        &lt;td&gt;DOWNLOAD LOCATION&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
        &lt;td&gt;Opera&lt;/td&gt;
        &lt;td&gt;https://github.com/operasoftware/operachromiumdriver/releases&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
        &lt;td&gt;Firefox&lt;/td&gt;
        &lt;td&gt;https://github.com/mozilla/geckodriver/releases&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
        &lt;td&gt;Chrome&lt;/td&gt;
        &lt;td&gt;http://chromedriver.chromium.org/downloads&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
        &lt;td&gt;Internet Explorer&lt;/td&gt;
        &lt;td&gt;https://github.com/SeleniumHQ/selenium/wiki/InternetExplorerDriver&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
        &lt;td&gt;Microsoft Edge&lt;/td&gt;
        &lt;td&gt;https://blogs.windows.com/msedgedev/2015/07/23/bringing-automated-testing-to-microsoft-edge-through-webdriver/&lt;/td&gt;
    &lt;/tr&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;Since I’ll be using Chrome, I’ll download the Selenium WebDriver for Chrome i.e. ChromeDriver. Though the downloaded ChromeDriver can be placed in any directory, it is a good practice to install the ChromeDriver in the location where Chrome browser (i.e. Chrome.exe) is present.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.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%2AHtk7lTjvZVlZMYX9.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.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%2AHtk7lTjvZVlZMYX9.png" width="696" height="236"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The advantage of following this practice is that you do not need to specify the path where the ChromeDriver is present while invoking the WebDriver.&lt;/p&gt;

&lt;h2&gt;
  
  
  How To Run MSTest Scripts With Selenium?
&lt;/h2&gt;

&lt;p&gt;For demonstrating the usage of Selenium WebDriver with MSTest, I’ll use the following &lt;a href="https://www.lambdatest.com/selenium-automation?utm_source=devto&amp;amp;utm_medium=organic&amp;amp;utm_campaign=apr12_bh&amp;amp;utm_term=bh&amp;amp;utm_content=webpage" rel="noopener noreferrer"&gt;Selenium &lt;/a&gt;scenario for this MSTest tutorial:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;&lt;p&gt;Navigate to the URL &lt;a href="https://lambdatest.github.io/sample-todo-app/" rel="noopener noreferrer"&gt;https://lambdatest.github.io/sample-todo-app/&lt;/a&gt;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Select the first two checkboxes&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Send ‘Adding item to the list’ to the textbox with id = sampletodotext&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Click the Add Button and verify whether the text has been added or not&lt;/p&gt;&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Take this certification to master the fundamentals of Selenium automation testing with C# and prove your credibility as a tester.&lt;/p&gt;

&lt;p&gt;Here’s a short glimpse of the Selenium C# 101 certification from LambdaTest:&lt;br&gt;
&lt;iframe width="710" height="399" src="https://www.youtube.com/embed/ZU85Mjwgv54"&gt;
&lt;/iframe&gt;
&lt;br&gt;
&lt;strong&gt;&lt;em&gt;Get started with this complete &lt;a href="https://www.lambdatest.com/selenium?utm_source=devto&amp;amp;utm_medium=organic&amp;amp;utm_campaign=apr12_bh&amp;amp;utm_term=bh&amp;amp;utm_content=webpage" rel="noopener noreferrer"&gt;Selenium&lt;/a&gt; automation testing tutorial. Learn what Selenium is, its architecture, advantages and more for automated cross browser testing.&lt;/em&gt;&lt;/strong&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Implementation Of The MSTest Script
&lt;/h2&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;#Run first MStest script for Selenium test automation 
using Microsoft.VisualStudio.TestTools.UnitTesting;
using OpenQA.Selenium;
using OpenQA.Selenium.Chrome;
using System;

namespace MS_Test_Cross_Browser
{
    [TestClass]
    public class MS_Test_Cross_Browser_Test
    {
        String test_url = "https://lambdatest.github.io/sample-todo-app/";
        String itemName = "Yey, Let's add it to list";

        [TestMethod]
        public void NavigateToDoApp()
        {
            IWebDriver driver;

            // Local Selenium WebDriver
            driver = new ChromeDriver();
            driver.Manage().Window.Maximize();

            driver.Navigate().GoToUrl(test_url);

            driver.Manage().Window.Maximize();

            // Click on First Check box
            IWebElement firstCheckBox = driver.FindElement(By.Name("li1"));
            firstCheckBox.Click();

            // Click on Second Check box
            IWebElement secondCheckBox = driver.FindElement(By.Name("li2"));
            secondCheckBox.Click();

            // Enter Item name
            IWebElement textfield = driver.FindElement(By.Id("sampletodotext"));
            textfield.SendKeys(itemName);

            // Click on Add button
            IWebElement addButton = driver.FindElement(By.Id("addbutton"));
            addButton.Click();

            // Verified Added Item name
            IWebElement itemtext = driver.FindElement(By.XPath("/html/body/div/div/div/ul/li[6]/span"));
            String getText = itemtext.Text;
            Assert.IsTrue(itemName.Contains(getText));

            /* Perform wait to check the output in this MSTest tutorial for Selenium */
            //System.Threading.Thread.Sleep(4000);

            Console.WriteLine("LT_ToDo_Test Passed");

            driver.Quit();
        }
    }
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;
&lt;h2&gt;
  
  
  Code WalkThrough
&lt;/h2&gt;

&lt;p&gt;*&lt;em&gt;Step 1 *&lt;/em&gt;— All the necessary packages are included in the beginning of the code. In this MSTest tutorial for Selenium; the following packages are included for automated web testing with Selenium C#:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;MSTest framework (Microsoft.VisualStudio.TestTools.UnitTesting)&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Selenium (OpenQA.Selenium)&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Chrome WebDriver (OpenQA.Selenium.Chrome)&lt;/p&gt;

&lt;p&gt;using Microsoft.VisualStudio.TestTools.UnitTesting;&lt;br&gt;
using OpenQA.Selenium;&lt;br&gt;
using OpenQA.Selenium.Chrome;&lt;br&gt;
using System;&lt;br&gt;
……………………………………………………………………………..&lt;br&gt;
……………………………………………………………………………..&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;*&lt;em&gt;Step 2 *&lt;/em&gt;— The [TestClass] attribute in MSTest represents the class containing the unit tests. In this example, MS_Test_Cross_Browser_Test() is the class containing all the tests.&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;namespace MS_Test_Cross_Browser
{
    [TestClass]
    public class MS_Test_Cross_Browser_Test
    {
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;*&lt;em&gt;Step 3 *&lt;/em&gt;— The [TestMethod] attribute in MSTest indicates a method that contains the test method. In this example, the test method is NavigateToDoApp() and the same is under the [TestMethod] attribute.&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;[TestMethod]
public void NavigateToDoApp()
{
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;*&lt;em&gt;Step 4 *&lt;/em&gt;— The IWebDriver interface is used for enabling the browser interactions. Hence, we create an instance of the IWebDriver interface in the code. A new instance of Chrome browser is initiated using the command driver = new ChromeDriver().&lt;/p&gt;

&lt;p&gt;The URL is set to &lt;a href="https://lambdatest.github.io/sample-todo-app/" rel="noopener noreferrer"&gt;https://lambdatest.github.io/sample-todo-app/&lt;/a&gt; and the browser window is maximized using the command driver.Manage().Window.Maximize() for this MSTest tutorial for automated web testing with Selenium C#.&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;public void NavigateToDoApp()
{
 IWebDriver driver;

 // Local Selenium WebDriver
 driver = new ChromeDriver();
 driver.Manage().Window.Maximize();

 driver.Navigate().GoToUrl(test_url);

 driver.Manage().Window.Maximize();
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;*&lt;em&gt;Step 4 *&lt;/em&gt;— Once the Chrome browser is initiated and the target URL is loaded, we locate the required elements on the web page for automated web testing with Selenium C#. The details of the required Web Elements are obtained using the Inspect Tool in the Chrome Browser. As seen in the screenshot below for &lt;a href="https://www.lambdatest.com/?utm_source=devto&amp;amp;utm_medium=organic&amp;amp;utm_campaign=apr12_bh&amp;amp;utm_term=bh&amp;amp;utm_content=webpage" rel="noopener noreferrer"&gt;cross browser testing,&lt;/a&gt; the element with Name “li1” is located using the tool.&lt;/p&gt;

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

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;[TestMethod]
public void NavigateToDoApp()
{
 IWebDriver driver;

 ...............................................................
 ...............................................................
 ...............................................................

 // Click on First Check box
 IWebElement firstCheckBox = driver.FindElement(By.Name("li1"));
 firstCheckBox.Click();

 // Click on Second Check box
 IWebElement secondCheckBox = driver.FindElement(By.Name("li2"));
 secondCheckBox.Click();

 // Enter Item name
 IWebElement textfield = driver.FindElement(By.Id("sampletodotext"));
 textfield.SendKeys(itemName);
 ...............................................................
 ...............................................................
 ...............................................................
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;The same approach is followed for locating the other web elements having Name — li2, li3. The other elements are located using the Id of those elements.&lt;/p&gt;

&lt;p&gt;The FindElement command in Selenium is used along with the appropriate web locators to identify the web element. Once the web element is identified (or located), appropriate action i.e. Click, SendKeys, etc. is performed on the web element.&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;// Click on Second Check box
IWebElement secondCheckBox = driver.FindElement(By.Name("li2"));
secondCheckBox.Click();

// Enter Item name
IWebElement textfield = driver.FindElement(By.Id("sampletodotext"));
textfield.SendKeys(itemName);
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;*&lt;em&gt;Step 5 *&lt;/em&gt;— A new item (Yey, Let’s add it to list) is added to the list. An assert is raised if the text in the newly added element does not match with the expected text.&lt;/p&gt;

&lt;p&gt;The Quit WebDriver command (driver.Quit) is called once the test is complete so that the WebDriver session is closed properly and resources used by the WebDriver are freed in this MSTest tutorial for Selenium automation.&lt;/p&gt;


&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;// Click on Second Check box&lt;br&gt;
IWebElement secondCheckBox = driver.FindElement(By.Name("li2"));&lt;br&gt;
secondCheckBox.Click();

&lt;p&gt;// Enter Item name&lt;br&gt;
IWebElement textfield = driver.FindElement(By.Id("sampletodotext"));&lt;br&gt;
textfield.SendKeys(itemName);&lt;br&gt;
&lt;/p&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;
&lt;h2&gt;
&lt;br&gt;
  &lt;br&gt;
  &lt;br&gt;
  Executing The MSTest Script&lt;br&gt;
&lt;/h2&gt;

&lt;p&gt;To execute the newly created test (NavigateToDoApp), go to &lt;strong&gt;Test&lt;/strong&gt; and press &lt;strong&gt;Run All Tests&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.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%2AH7LLJDABFjgRgjGP.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.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%2AH7LLJDABFjgRgjGP.png" width="512" height="278"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The execution progress can be seen in the Test Explorer window which can be seen by going to menu-item &lt;strong&gt;View -&amp;gt; Test Explorer&lt;/strong&gt;.&lt;/p&gt;

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

&lt;p&gt;Shown below is an execution snapshot from Visual Studio. The Green tick mark indicates that the test execution completed successfully.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.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%2Ac2vhFOq3AIVZnrkU.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.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%2Ac2vhFOq3AIVZnrkU.png" width="512" height="206"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Shown below is the &lt;a href="https://www.lambdatest.com/?utm_source=devto&amp;amp;utm_medium=organic&amp;amp;utm_campaign=apr12_bh&amp;amp;utm_term=bh&amp;amp;utm_content=webpage" rel="noopener noreferrer"&gt;automated browser testing &lt;/a&gt;execution in action in the Chrome web browser:&lt;/p&gt;

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

&lt;p&gt;This MSTest Tutorial for beginners and professionals will help you learn how to use MSTest framework with Selenium C# for performing Selenium automation testing.&lt;br&gt;
&lt;iframe width="710" height="399" src="https://www.youtube.com/embed/2XUQdN-Bi2o"&gt;
&lt;/iframe&gt;
&lt;br&gt;
&lt;strong&gt;&lt;em&gt;A comprehensive &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=apr12_bh&amp;amp;utm_term=bh&amp;amp;utm_content=learning_hub" rel="noopener noreferrer"&gt;end to end testing&lt;/a&gt; tutorial that covers what E2E Testing is, its importance, benefits, and how to perform it with real-time examples.&lt;/em&gt;&lt;/strong&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  All In All
&lt;/h2&gt;

&lt;p&gt;In this MSTest tutorial for Selenium, I took a deep-dive into automated web testing with Selenium C# using the MSTest framework to run your first MSTest Script for Selenium test automation. Interactions with the elements on the web-page were performed by using the relevant Selenium APIs. The code implementation used in this article can be scaled to execute complex Selenium test automation scenarios using Selenium C# with the MSTest framework.&lt;/p&gt;

&lt;p&gt;This concludes this MSTest tutorial for Selenium to run your first MSTest script for Selenium test automation. I hope you found this article informative! We’ve also covered a whole series on &lt;a href="https://www.lambdatest.com/blog/selenium-c-sharp-tutorial-introduction/?utm_source=devto&amp;amp;utm_medium=organic&amp;amp;utm_campaign=apr12_bh&amp;amp;utm_term=bh&amp;amp;utm_content=blog" rel="noopener noreferrer"&gt;Selenium C# tutorial,&lt;/a&gt; you can check it out to know more about automated browser testing using Selenium C#. Do share this article with your peers and colleagues looking to learn about &lt;a href="https://www.lambdatest.com/selenium-automation-testing-with-MSTest?utm_source=devto&amp;amp;utm_medium=organic&amp;amp;utm_campaign=apr12_bh&amp;amp;utm_term=bh&amp;amp;utm_content=webpage" rel="noopener noreferrer"&gt;Selenium test automation using MSTest. &lt;/a&gt;A retweet and share on LinkedIn is always welcome with open arms. That’s all for now. &lt;strong&gt;Happy Testing!!!&lt;/strong&gt;&lt;/p&gt;

</description>
      <category>testing</category>
      <category>automation</category>
      <category>webdev</category>
      <category>tutorial</category>
    </item>
  </channel>
</rss>
