<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: Abubakkar Sithik</title>
    <description>The latest articles on DEV Community by Abubakkar Sithik (@abusithik).</description>
    <link>https://dev.to/abusithik</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%2F647123%2F309890d1-08d9-4a79-b5aa-bfae61be0e60.jpeg</url>
      <title>DEV Community: Abubakkar Sithik</title>
      <link>https://dev.to/abusithik</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/abusithik"/>
    <language>en</language>
    <item>
      <title>Running Postman Collections in Jenkins</title>
      <dc:creator>Abubakkar Sithik</dc:creator>
      <pubDate>Sun, 09 Feb 2025 11:32:36 +0000</pubDate>
      <link>https://dev.to/abusithik/running-postman-collections-in-jenkins-7k</link>
      <guid>https://dev.to/abusithik/running-postman-collections-in-jenkins-7k</guid>
      <description>&lt;p&gt;As APIs become increasingly crucial in modern software development, maintaining comprehensive test coverage is essential. In this post, I'll share a complete solution for running Postman collections using Jenkins, perfect for regression testing and continuous integration.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Problem
&lt;/h2&gt;

&lt;p&gt;Many organizations maintain extensive API test suites using Postman collections. However, running these tests manually is time-consuming and prone to human error. We needed a solution that would:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Automate the execution of multiple Postman collections&lt;/li&gt;
&lt;li&gt;Generate detailed HTML reports&lt;/li&gt;
&lt;li&gt;Send email notifications with results&lt;/li&gt;
&lt;li&gt;Support different environments (QA/Staging/Prod)&lt;/li&gt;
&lt;li&gt;Integrate seamlessly with our CI/CD pipeline&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  The Solution
&lt;/h2&gt;

&lt;p&gt;I've created a Jenkins pipeline that automates the entire process using Newman (Postman's command-line collection runner). Here are the key features:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Parallel execution of multiple Postman collections&lt;/li&gt;
&lt;li&gt;Customizable HTML reports with detailed test results&lt;/li&gt;
&lt;li&gt;Email notifications with a summary table&lt;/li&gt;
&lt;li&gt;Environment-specific configurations&lt;/li&gt;
&lt;li&gt;Artifact archiving for historical tracking&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Prerequisites
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;Jenkins server&lt;/li&gt;
&lt;li&gt;NodeJS installed on Jenkins&lt;/li&gt;
&lt;li&gt;Postman collections and environments&lt;/li&gt;
&lt;li&gt;Postman API key&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Implementation Details
&lt;/h2&gt;

&lt;h3&gt;
  
  
  1. Pipeline Configuration
&lt;/h3&gt;

&lt;p&gt;The pipeline uses Jenkins parameters to make it flexible:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight groovy"&gt;&lt;code&gt;&lt;span class="n"&gt;parameters&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
    &lt;span class="n"&gt;text&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;
        &lt;span class="nl"&gt;name:&lt;/span&gt; &lt;span class="s1"&gt;'COLLECTIONS_JSON'&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt;
        &lt;span class="nl"&gt;defaultValue:&lt;/span&gt; &lt;span class="s1"&gt;'''[
            {"name": "Sample_Collection",     
             "url": "https://api.postman.com/collections/YOUR_COLLECTION_ID?apikey=${POSTMAN_API_KEY}"
                }
            // ... other collections
        ]'''&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt;
        &lt;span class="nl"&gt;description:&lt;/span&gt; &lt;span class="s1"&gt;'JSON array of collections to run'&lt;/span&gt;
    &lt;span class="o"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;choice&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;
        &lt;span class="nl"&gt;name:&lt;/span&gt; &lt;span class="s1"&gt;'ENVIRONMENT'&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt;
        &lt;span class="nl"&gt;choices:&lt;/span&gt; &lt;span class="o"&gt;[&lt;/span&gt;&lt;span class="s1"&gt;'QA'&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;'Prod'&lt;/span&gt;&lt;span class="o"&gt;],&lt;/span&gt;
        &lt;span class="nl"&gt;description:&lt;/span&gt; &lt;span class="s1"&gt;'Select the environment'&lt;/span&gt;
    &lt;span class="o"&gt;)&lt;/span&gt;
&lt;span class="o"&gt;}&lt;/span&gt;
&lt;span class="c1"&gt;// ... The complete code is available on GitHub (https://github.com/abu-sithik/jenkins_postman_pipeline).&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  2. Newman Integration
&lt;/h3&gt;

&lt;p&gt;The pipeline installs Newman and its HTML reporter:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight groovy"&gt;&lt;code&gt;&lt;span class="n"&gt;stage&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'Install Newman'&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
    &lt;span class="n"&gt;steps&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
        &lt;span class="n"&gt;sh&lt;/span&gt; &lt;span class="s1"&gt;'npm install -g newman'&lt;/span&gt;
        &lt;span class="n"&gt;sh&lt;/span&gt; &lt;span class="s1"&gt;'npm install -g newman-reporter-htmlextra'&lt;/span&gt;
    &lt;span class="o"&gt;}&lt;/span&gt;
&lt;span class="o"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  3. Collection Execution
&lt;/h3&gt;

&lt;p&gt;Each collection runs with environment-specific configurations:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight groovy"&gt;&lt;code&gt;&lt;span class="kt"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;runCollection&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;collection&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="n"&gt;environment_url&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
    &lt;span class="c1"&gt;// ... collection execution logic&lt;/span&gt;
    &lt;span class="c1"&gt;// ... The complete code is available on GitHub (https://github.com/abu-sithik/jenkins_postman_pipeline).&lt;/span&gt;
&lt;span class="o"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  4. HTML Report Generation
&lt;/h3&gt;

&lt;p&gt;The solution includes a custom HTML report generator:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight groovy"&gt;&lt;code&gt;&lt;span class="kt"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;generateHtmlReport&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;reportData&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
    &lt;span class="c1"&gt;// ... HTML generation logic&lt;/span&gt;
    &lt;span class="c1"&gt;// ... The complete code is available on GitHub (https://github.com/abu-sithik/jenkins_postman_pipeline).&lt;/span&gt;
&lt;span class="o"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  5. Email Notifications
&lt;/h3&gt;

&lt;p&gt;Results are automatically emailed to stakeholders:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight groovy"&gt;&lt;code&gt;&lt;span class="kt"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;sendEmail&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;htmlContent&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="n"&gt;emailConfig&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
    &lt;span class="n"&gt;emailext&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;
        &lt;span class="nl"&gt;subject:&lt;/span&gt; &lt;span class="s2"&gt;"${emailConfig.subject}"&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt;
        &lt;span class="nl"&gt;body:&lt;/span&gt; &lt;span class="n"&gt;htmlContent&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt;
        &lt;span class="nl"&gt;to:&lt;/span&gt; &lt;span class="n"&gt;emailConfig&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;recipients&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt;
        &lt;span class="nl"&gt;from:&lt;/span&gt; &lt;span class="n"&gt;emailConfig&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;sender&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt;
        &lt;span class="nl"&gt;mimeType:&lt;/span&gt; &lt;span class="s1"&gt;'text/html'&lt;/span&gt;
    &lt;span class="o"&gt;)&lt;/span&gt;
&lt;span class="o"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Setting Up the Pipeline
&lt;/h2&gt;

&lt;h3&gt;
  
  
  1. Required Plugins
&lt;/h3&gt;

&lt;p&gt;Install the following Jenkins plugins:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;NodeJS Plugin&lt;/li&gt;
&lt;li&gt;Email Extension Plugin&lt;/li&gt;
&lt;li&gt;Pipeline Plugin&lt;/li&gt;
&lt;li&gt;Credentials Plugin
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Manage Jenkins &amp;gt; Manage Plugins &amp;gt; Available &amp;gt; Search and install each plugin
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  2. NodeJS Configuration
&lt;/h3&gt;

&lt;ol&gt;
&lt;li&gt;Navigate to &lt;code&gt;Manage Jenkins &amp;gt; Tools&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;Click "Add NodeJS"&lt;/li&gt;
&lt;li&gt;Configure as follows:

&lt;ul&gt;
&lt;li&gt;Name: &lt;code&gt;NodeJS_22&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;Install automatically: Check&lt;/li&gt;
&lt;li&gt;Version: Select latest LTS version&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;Click Save&lt;/li&gt;
&lt;/ol&gt;

&lt;h3&gt;
  
  
  3. Credentials Setup
&lt;/h3&gt;

&lt;ol&gt;
&lt;li&gt;Navigate to &lt;code&gt;Manage Jenkins &amp;gt; Manage Credentials&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;Click on "Jenkins" under Stores scoped to Jenkins&lt;/li&gt;
&lt;li&gt;Click "Global credentials"&lt;/li&gt;
&lt;li&gt;Click "Add Credentials"&lt;/li&gt;
&lt;li&gt;Configure:

&lt;ul&gt;
&lt;li&gt;Kind: Secret text&lt;/li&gt;
&lt;li&gt;Scope: Global&lt;/li&gt;
&lt;li&gt;Secret: Your Postman API key&lt;/li&gt;
&lt;li&gt;ID: POSTMAN_API_KEY&lt;/li&gt;
&lt;li&gt;Description: Postman API Key for Collection Runner&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ol&gt;

&lt;h3&gt;
  
  
  4. Email Configuration
&lt;/h3&gt;

&lt;ol&gt;
&lt;li&gt;Navigate to &lt;code&gt;Manage Jenkins &amp;gt; Configure System&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;Find "Extended E-mail Notification"&lt;/li&gt;
&lt;li&gt;Configure:

&lt;ul&gt;
&lt;li&gt;SMTP server&lt;/li&gt;
&lt;li&gt;SMTP port&lt;/li&gt;
&lt;li&gt;Credentials if required&lt;/li&gt;
&lt;li&gt;Default recipients&lt;/li&gt;
&lt;li&gt;Default subject&lt;/li&gt;
&lt;li&gt;Default content&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ol&gt;

&lt;h2&gt;
  
  
  Pipeline Configuration
&lt;/h2&gt;

&lt;h3&gt;
  
  
  1. Create Pipeline Job
&lt;/h3&gt;

&lt;ol&gt;
&lt;li&gt;Click "New Item" on Jenkins dashboard&lt;/li&gt;
&lt;li&gt;Enter name for your pipeline&lt;/li&gt;
&lt;li&gt;Select "Pipeline"&lt;/li&gt;
&lt;li&gt;Click OK&lt;/li&gt;
&lt;/ol&gt;

&lt;h3&gt;
  
  
  2. Configure Pipeline
&lt;/h3&gt;

&lt;ol&gt;
&lt;li&gt;In pipeline configuration:

&lt;ul&gt;
&lt;li&gt;Select "Pipeline script" or "Pipeline script from SCM"&lt;/li&gt;
&lt;li&gt;If using SCM:

&lt;ul&gt;
&lt;li&gt;Select Git&lt;/li&gt;
&lt;li&gt;Enter repository URL&lt;/li&gt;
&lt;li&gt;Specify branch&lt;/li&gt;
&lt;li&gt;Script Path: Jenkinsfile&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ol&gt;

&lt;h3&gt;
  
  
  3. Environment Configuration
&lt;/h3&gt;

&lt;p&gt;Update the &lt;code&gt;ENVIRONMENT_URLS&lt;/code&gt; variable in Jenkinsfile:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight groovy"&gt;&lt;code&gt;&lt;span class="n"&gt;ENVIRONMENT_URLS&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="o"&gt;[&lt;/span&gt;
    &lt;span class="nl"&gt;QA:&lt;/span&gt; &lt;span class="o"&gt;[&lt;/span&gt;&lt;span class="nl"&gt;id:&lt;/span&gt; &lt;span class="s1"&gt;'YOUR_QA_ENV_ID'&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="nl"&gt;url:&lt;/span&gt; &lt;span class="s1"&gt;''&lt;/span&gt;&lt;span class="o"&gt;],&lt;/span&gt;
    &lt;span class="nl"&gt;Staging:&lt;/span&gt; &lt;span class="o"&gt;[&lt;/span&gt;&lt;span class="nl"&gt;id:&lt;/span&gt; &lt;span class="s1"&gt;'YOUR_STAGING_ENV_ID'&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="nl"&gt;url:&lt;/span&gt; &lt;span class="s1"&gt;''&lt;/span&gt;&lt;span class="o"&gt;],&lt;/span&gt;
    &lt;span class="nl"&gt;Production:&lt;/span&gt; &lt;span class="o"&gt;[&lt;/span&gt;&lt;span class="nl"&gt;id:&lt;/span&gt; &lt;span class="s1"&gt;'YOUR_PROD_ENV_ID'&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="nl"&gt;url:&lt;/span&gt; &lt;span class="s1"&gt;''&lt;/span&gt;&lt;span class="o"&gt;]&lt;/span&gt;
&lt;span class="o"&gt;]&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Postman Setup
&lt;/h2&gt;

&lt;h3&gt;
  
  
  1. Collection Preparation
&lt;/h3&gt;

&lt;ol&gt;
&lt;li&gt;Create/organize your Postman collections&lt;/li&gt;
&lt;li&gt;Ensure all environment variables are properly set&lt;/li&gt;
&lt;li&gt;Add appropriate tests to requests&lt;/li&gt;
&lt;li&gt;Get collection IDs from Postman&lt;/li&gt;
&lt;/ol&gt;

&lt;h3&gt;
  
  
  2. Environment Setup
&lt;/h3&gt;

&lt;ol&gt;
&lt;li&gt;Create environments in Postman for each target (QA, Staging, Production)&lt;/li&gt;
&lt;li&gt;Set appropriate variables&lt;/li&gt;
&lt;li&gt;Get environment IDs from Postman&lt;/li&gt;
&lt;/ol&gt;

&lt;h2&gt;
  
  
  First Run
&lt;/h2&gt;

&lt;ol&gt;
&lt;li&gt;Open pipeline in Jenkins&lt;/li&gt;
&lt;li&gt;Click "Build with Parameters"&lt;/li&gt;
&lt;li&gt;Enter test configuration:
&lt;/li&gt;
&lt;/ol&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="w"&gt;   &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="w"&gt;
       &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
           &lt;/span&gt;&lt;span class="nl"&gt;"name"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Test_Collection"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
           &lt;/span&gt;&lt;span class="nl"&gt;"url"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"https://api.postman.com/collections/YOUR_COLLECTION_ID?apikey=${POSTMAN_API_KEY}"&lt;/span&gt;&lt;span class="w"&gt;
       &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
   &lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ol&gt;
&lt;li&gt;Select environment&lt;/li&gt;
&lt;li&gt;Enter email recipients&lt;/li&gt;
&lt;li&gt;Click Build&lt;/li&gt;
&lt;/ol&gt;

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

&lt;p&gt;This solution has significantly improved our API testing process by:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Reducing manual effort&lt;/li&gt;
&lt;li&gt;Providing consistent test execution&lt;/li&gt;
&lt;li&gt;Generating comprehensive reports&lt;/li&gt;
&lt;li&gt;Enabling early detection of API issues&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The complete script is available on &lt;a href="https://github.com/abu-sithik/jenkins_postman_pipeline" rel="noopener noreferrer"&gt;GitHub&lt;/a&gt;.&lt;/p&gt;

</description>
      <category>jenkins</category>
      <category>automation</category>
      <category>devops</category>
      <category>testing</category>
    </item>
    <item>
      <title>Cleaning Up Stale Branches to Speed Up CI/CD and Reduce GitHub API Load</title>
      <dc:creator>Abubakkar Sithik</dc:creator>
      <pubDate>Wed, 05 Feb 2025 10:52:49 +0000</pubDate>
      <link>https://dev.to/abusithik/cleaning-up-stale-branches-to-speed-up-cicd-and-reduce-github-api-load-1de5</link>
      <guid>https://dev.to/abusithik/cleaning-up-stale-branches-to-speed-up-cicd-and-reduce-github-api-load-1de5</guid>
      <description>&lt;h2&gt;
  
  
  The Problem: Too Many Stale Branches
&lt;/h2&gt;

&lt;p&gt;In my company, we manage multiple microservices, each with its own repository. Over time, our repositories accumulated hundreds—sometimes ~800+ stale branches. Many of these were created over the span of &lt;strong&gt;6+ years&lt;/strong&gt; and were never deleted. This led to several issues:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Jenkins build auto-start times skyrocketed&lt;/strong&gt; to ~20 minutes due to multi-branch scanning.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;GitHub API request limits&lt;/strong&gt; became a concern as our build processes constantly queried branch metadata.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Codebase clutter&lt;/strong&gt; made it difficult to navigate active branches.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;We needed a &lt;strong&gt;safe and automated&lt;/strong&gt; way to identify and delete these branches without disrupting active development. Enter &lt;strong&gt;GitHub CLI (gh) + Bash!&lt;/strong&gt;&lt;/p&gt;




&lt;h2&gt;
  
  
  The Solution: Automating Stale Branch Cleanup
&lt;/h2&gt;

&lt;p&gt;To solve this problem, I wrote a &lt;strong&gt;Bash script&lt;/strong&gt; leveraging the GitHub CLI to:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;Fetch all branches in a repository.&lt;/strong&gt;&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Check their last commit date.&lt;/strong&gt;&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Log stale branches to a CSV file (for review).&lt;/strong&gt;&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;(Optional) Automatically delete branches&lt;/strong&gt; older than a specified date.&lt;/li&gt;
&lt;/ol&gt;

&lt;h3&gt;
  
  
  The Script:
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;#!/bin/bash&lt;/span&gt;

&lt;span class="c"&gt;# Ensure gh CLI is installed and authenticated&lt;/span&gt;
&lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="o"&gt;!&lt;/span&gt; &lt;span class="nb"&gt;command&lt;/span&gt; &lt;span class="nt"&gt;-v&lt;/span&gt; gh &amp;amp;&amp;gt; /dev/null&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="k"&gt;then
    &lt;/span&gt;&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"GitHub CLI (gh) is not installed. Please install it first."&lt;/span&gt;
    &lt;span class="nb"&gt;exit &lt;/span&gt;1
&lt;span class="k"&gt;fi&lt;/span&gt;

&lt;span class="c"&gt;# Get repository name (default: current repo)&lt;/span&gt;
&lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="o"&gt;[&lt;/span&gt; &lt;span class="nt"&gt;-z&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$1&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; &lt;span class="o"&gt;]&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="k"&gt;then
    &lt;/span&gt;&lt;span class="nv"&gt;REPO&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="si"&gt;$(&lt;/span&gt;gh repo view &lt;span class="nt"&gt;--json&lt;/span&gt; nameWithOwner &lt;span class="nt"&gt;-q&lt;/span&gt; .nameWithOwner&lt;span class="si"&gt;)&lt;/span&gt;
&lt;span class="k"&gt;else
    &lt;/span&gt;&lt;span class="nv"&gt;REPO&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="nv"&gt;$1&lt;/span&gt;
&lt;span class="k"&gt;fi

&lt;/span&gt;&lt;span class="nv"&gt;OUTPUT_FILE&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"stale_branches.csv"&lt;/span&gt;

&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"branch_name,last_commit_date"&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="nv"&gt;$OUTPUT_FILE&lt;/span&gt;

&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"Fetching branches for &lt;/span&gt;&lt;span class="nv"&gt;$REPO&lt;/span&gt;&lt;span class="s2"&gt;..."&lt;/span&gt;
&lt;span class="nv"&gt;branches&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="si"&gt;$(&lt;/span&gt;gh api repos/&lt;span class="nv"&gt;$REPO&lt;/span&gt;/branches &lt;span class="nt"&gt;--paginate&lt;/span&gt; &lt;span class="nt"&gt;--jq&lt;/span&gt; &lt;span class="s1"&gt;'.[].name'&lt;/span&gt;&lt;span class="si"&gt;)&lt;/span&gt;

&lt;span class="nv"&gt;target_date&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"2024-01-01T00:00:00Z"&lt;/span&gt;
&lt;span class="nv"&gt;count&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;0
&lt;span class="nv"&gt;processed&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;0

&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"Analyzing branches..."&lt;/span&gt;
&lt;span class="k"&gt;for &lt;/span&gt;branch &lt;span class="k"&gt;in&lt;/span&gt; &lt;span class="nv"&gt;$branches&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
    &lt;span class="o"&gt;((&lt;/span&gt;processed++&lt;span class="o"&gt;))&lt;/span&gt;
    &lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="nt"&gt;-ne&lt;/span&gt; &lt;span class="s2"&gt;"Progress: &lt;/span&gt;&lt;span class="nv"&gt;$processed&lt;/span&gt;&lt;span class="s2"&gt; branches processed&lt;/span&gt;&lt;span class="se"&gt;\r&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;

    &lt;span class="nv"&gt;last_commit_date&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="si"&gt;$(&lt;/span&gt;gh api repos/&lt;span class="nv"&gt;$REPO&lt;/span&gt;/commits/&lt;span class="nv"&gt;$branch&lt;/span&gt; &lt;span class="nt"&gt;--jq&lt;/span&gt; &lt;span class="s1"&gt;'.commit.committer.date'&lt;/span&gt; 2&amp;gt;/dev/null &lt;span class="o"&gt;||&lt;/span&gt; &lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;""&lt;/span&gt;&lt;span class="si"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="o"&gt;[&lt;/span&gt; &lt;span class="o"&gt;!&lt;/span&gt; &lt;span class="nt"&gt;-z&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$last_commit_date&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; &lt;span class="o"&gt;]&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="o"&gt;[&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$last_commit_date&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; &lt;span class="se"&gt;\&amp;lt;&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$target_date&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; &lt;span class="o"&gt;]&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="k"&gt;then
        &lt;/span&gt;&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$branch&lt;/span&gt;&lt;span class="s2"&gt;,&lt;/span&gt;&lt;span class="nv"&gt;$last_commit_date&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&amp;gt;&lt;/span&gt; &lt;span class="nv"&gt;$OUTPUT_FILE&lt;/span&gt;
        &lt;span class="o"&gt;((&lt;/span&gt;count++&lt;span class="o"&gt;))&lt;/span&gt;
    &lt;span class="k"&gt;fi
done

&lt;/span&gt;&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="nt"&gt;-e&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="s2"&gt;Found &lt;/span&gt;&lt;span class="nv"&gt;$count&lt;/span&gt;&lt;span class="s2"&gt; stale branches (last commit before &lt;/span&gt;&lt;span class="nv"&gt;$target_date&lt;/span&gt;&lt;span class="s2"&gt;)"&lt;/span&gt;
&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"Results written to &lt;/span&gt;&lt;span class="nv"&gt;$OUTPUT_FILE&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;

&lt;span class="c"&gt;# Deleting stale branches&lt;/span&gt;
&lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="o"&gt;[&lt;/span&gt; &lt;span class="nv"&gt;$count&lt;/span&gt; &lt;span class="nt"&gt;-gt&lt;/span&gt; 0 &lt;span class="o"&gt;]&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="k"&gt;then
    &lt;/span&gt;&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"Deleting stale branches..."&lt;/span&gt;
    &lt;span class="nv"&gt;deleted&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;0
    &lt;span class="nv"&gt;failed&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;0
    &lt;span class="k"&gt;while &lt;/span&gt;&lt;span class="nv"&gt;IFS&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;, &lt;span class="nb"&gt;read&lt;/span&gt; &lt;span class="nt"&gt;-r&lt;/span&gt; branch_name last_commit&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="k"&gt;do
        if&lt;/span&gt; &lt;span class="o"&gt;[&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$branch_name&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; &lt;span class="o"&gt;!=&lt;/span&gt; &lt;span class="s2"&gt;"branch_name"&lt;/span&gt; &lt;span class="o"&gt;]&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="k"&gt;then&lt;/span&gt;  &lt;span class="c"&gt;# Skip header&lt;/span&gt;
            &lt;span class="k"&gt;if &lt;/span&gt;gh api &lt;span class="nt"&gt;-X&lt;/span&gt; DELETE repos/&lt;span class="nv"&gt;$REPO&lt;/span&gt;/git/refs/heads/&lt;span class="nv"&gt;$branch_name&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&lt;/span&gt;/dev/null 2&amp;gt;&amp;amp;1&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="k"&gt;then&lt;/span&gt;
                &lt;span class="o"&gt;((&lt;/span&gt;deleted++&lt;span class="o"&gt;))&lt;/span&gt;
                &lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"Deleted: &lt;/span&gt;&lt;span class="nv"&gt;$branch_name&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;
            &lt;span class="k"&gt;else&lt;/span&gt;
                &lt;span class="o"&gt;((&lt;/span&gt;failed++&lt;span class="o"&gt;))&lt;/span&gt;
                &lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"Failed to delete: &lt;/span&gt;&lt;span class="nv"&gt;$branch_name&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;
            &lt;span class="k"&gt;fi
        fi
    done&lt;/span&gt; &amp;lt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$OUTPUT_FILE&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;
    &lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"Successfully deleted: &lt;/span&gt;&lt;span class="nv"&gt;$deleted&lt;/span&gt;&lt;span class="s2"&gt; branches"&lt;/span&gt;
    &lt;span class="o"&gt;[&lt;/span&gt; &lt;span class="nv"&gt;$failed&lt;/span&gt; &lt;span class="nt"&gt;-gt&lt;/span&gt; 0 &lt;span class="o"&gt;]&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"Failed to delete: &lt;/span&gt;&lt;span class="nv"&gt;$failed&lt;/span&gt;&lt;span class="s2"&gt; branches"&lt;/span&gt;
&lt;span class="k"&gt;fi

&lt;/span&gt;&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"Total branches processed: &lt;/span&gt;&lt;span class="nv"&gt;$processed&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h2&gt;
  
  
  How It Works:
&lt;/h2&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Fetches all branches&lt;/strong&gt; in the given repository.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Checks the last commit date&lt;/strong&gt; for each branch.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Logs stale branches&lt;/strong&gt; (last commit before 2024-01-01) to a CSV file.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Deletes stale branches automatically&lt;/strong&gt; (if enabled).&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;By running this script, we &lt;strong&gt;cleaned up hundreds of old branches&lt;/strong&gt;, reducing our Jenkins build start time significantly and staying within GitHub’s API limits.&lt;/p&gt;




&lt;h2&gt;
  
  
  Lessons Learned &amp;amp; Best Practices
&lt;/h2&gt;

&lt;h3&gt;
  
  
  1️⃣ Automate but Review First
&lt;/h3&gt;

&lt;p&gt;Before enabling automatic deletion, review the CSV file. This prevents accidental deletion of branches that might still be needed.&lt;/p&gt;

&lt;h3&gt;
  
  
  2️⃣ Protect Important Branches
&lt;/h3&gt;

&lt;p&gt;Make sure to &lt;strong&gt;exclude &lt;code&gt;main&lt;/code&gt;, &lt;code&gt;develop&lt;/code&gt;, or any critical branches&lt;/strong&gt; if necessary.&lt;/p&gt;

&lt;h3&gt;
  
  
  3️⃣ Schedule Regular Cleanup
&lt;/h3&gt;

&lt;p&gt;Stale branches will accumulate again. Automate this script via &lt;strong&gt;a scheduled GitHub Action or Jenkins job&lt;/strong&gt; to run quarterly or semi-annually.&lt;/p&gt;

&lt;h3&gt;
  
  
  4️⃣ Use GitHub API Efficiently
&lt;/h3&gt;

&lt;p&gt;To avoid hitting GitHub API rate limits, paginate API calls and consider caching results if working with many repositories.&lt;/p&gt;




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

&lt;p&gt;This simple script helped us &lt;strong&gt;streamline our GitHub repositories, improve CI/CD performance, and prevent API overuse.&lt;/strong&gt; If your team struggles with stale branches, give this a try!&lt;/p&gt;

&lt;p&gt;👉 What are your strategies for handling stale branches? Let me know in the comments!&lt;/p&gt;

</description>
      <category>github</category>
      <category>cicd</category>
      <category>jenkins</category>
      <category>bash</category>
    </item>
    <item>
      <title>Automating Mirroring Between Bitbucket and GitHub</title>
      <dc:creator>Abubakkar Sithik</dc:creator>
      <pubDate>Mon, 20 May 2024 10:55:53 +0000</pubDate>
      <link>https://dev.to/abusithik/automating-mirroring-between-bitbucket-and-github-40e8</link>
      <guid>https://dev.to/abusithik/automating-mirroring-between-bitbucket-and-github-40e8</guid>
      <description>&lt;p&gt;💡 The primary objective of this process is to demonstrate the feasibility and functionality of setting up automated mirroring between Bitbucket and GitHub repositories.&lt;/p&gt;

&lt;h2&gt;
  
  
  Overview
&lt;/h2&gt;

&lt;p&gt;This article outlines the steps to mirror a Bitbucket repository to a GitHub repository. The process involves configuring Bitbucket Pipelines to automate the synchronization of code changes between the two repositories. This setup can be particularly useful for teams looking to leverage GitHub’s enhanced collaboration features and seamless integration with existing toolsets while still maintaining their existing Bitbucket repositories.&lt;/p&gt;

&lt;h2&gt;
  
  
  Steps to Automate Mirroring
&lt;/h2&gt;

&lt;p&gt;The following steps will guide you through establishing automated mirroring from Bitbucket to GitHub:&lt;/p&gt;

&lt;h3&gt;
  
  
  1. GitHub Repository Setup
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;Create a new repository on GitHub. For this example, we'll refer to it as "github-repo".&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  2. Bitbucket Pipelines Configuration
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;Enable Pipelines under &lt;em&gt;Repository settings &amp;gt; Pipelines &amp;gt; Settings&lt;/em&gt; on Bitbucket.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F542hqoaic0oiaa0e9d3f.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F542hqoaic0oiaa0e9d3f.png" alt="Enable Pipelines" width="800" height="331"&gt;&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  3. SSH Key Generation (Bitbucket)
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;Generate SSH keys under &lt;em&gt;Repository settings &amp;gt; Pipelines &amp;gt; SSH keys&lt;/em&gt; on Bitbucket.&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Copy the &lt;strong&gt;public key&lt;/strong&gt; to the clipboard.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F7w6qtv5fr3nog4ipnqn4.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F7w6qtv5fr3nog4ipnqn4.png" alt="Generate SSH Keys" width="800" height="326"&gt;&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  4. Known Hosts Configuration (Bitbucket)
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;Add github.com as the Host address under &lt;em&gt;Known hosts&lt;/em&gt;.&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Click Fetch followed by Add host.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fc3xl35x4wo32t4zxpyy4.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fc3xl35x4wo32t4zxpyy4.png" alt="Configure Known Hosts" width="800" height="217"&gt;&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  5. Deploy Key Configuration (GitHub)
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;Add the &lt;strong&gt;public key&lt;/strong&gt; under &lt;em&gt;Settings &amp;gt; Security &amp;gt; Deploy keys &amp;gt; Add deploy key&lt;/em&gt; on GitHub.&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Enable write access for the deploy key.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fro9ooc9frkceum7ou4sy.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fro9ooc9frkceum7ou4sy.png" alt="Add Deploy Key" width="800" height="412"&gt;&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  6. Access Key Configuration (Bitbucket)
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;Add the &lt;strong&gt;public key&lt;/strong&gt; under &lt;em&gt;Repository settings &amp;gt; Security &amp;gt; Access keys &amp;gt; Add key&lt;/em&gt; on Bitbucket.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fr9d1mu47l9m6xz1j8x5o.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fr9d1mu47l9m6xz1j8x5o.png" alt="Add Access Key" width="800" height="357"&gt;&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  7. Pipeline Configuration (Bitbucket)
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;Create a &lt;code&gt;bitbucket-pipelines.yml&lt;/code&gt; file inside the Bitbucket repository with the following content:&lt;br&gt;
&lt;/p&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="na"&gt;image&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;atlassian/default-image:3&lt;/span&gt;

&lt;span class="na"&gt;pipelines&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="c1"&gt;# Automated pipeline for syncing Bitbucket repository with its GitHub mirror, ensuring real-time updates across platforms&lt;/span&gt;
  &lt;span class="na"&gt;branches&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;*'&lt;/span&gt;&lt;span class="err"&gt;:&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;step&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
          &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Sync GitHub Mirror&lt;/span&gt;
          &lt;span class="na"&gt;image&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;alpine/git:latest&lt;/span&gt;
          &lt;span class="na"&gt;clone&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
            &lt;span class="na"&gt;enabled&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;false&lt;/span&gt;
          &lt;span class="na"&gt;script&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
            &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;git clone --bare git@bitbucket.org:your-org/bitbucket-repo.git&lt;/span&gt;
            &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;cd bitbucket-repo.git&lt;/span&gt;
            &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;git push --mirror git@github.com:your-org/github-repo.git&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;This configuration ensures that any code push to the Bitbucket repository will initiate a Bitbucket pipeline job, triggering automatic mirroring to the GitHub repository. The synchronization of code changes prepares the team for a seamless transition to using GitHub as their primary repository platform.&lt;/p&gt;

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

&lt;p&gt;By following these steps, you can automate the mirroring of a Bitbucket repository to a GitHub repository. This setup ensures that any code push to the Bitbucket repository triggers an automatic pipeline job, mirroring the changes to the GitHub repository. This process is beneficial for teams looking to utilize GitHub’s advanced features while maintaining their existing workflows on Bitbucket.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Note: Further optimization and fine-tuning may be required for production deployment.&lt;/em&gt;&lt;/p&gt;

</description>
      <category>git</category>
      <category>devops</category>
      <category>bitbucket</category>
      <category>productivity</category>
    </item>
    <item>
      <title>How to Build a Selenium UI Testing Framework</title>
      <dc:creator>Abubakkar Sithik</dc:creator>
      <pubDate>Mon, 12 Jun 2023 13:26:47 +0000</pubDate>
      <link>https://dev.to/abusithik/how-to-build-a-selenium-ui-testing-framework-3df9</link>
      <guid>https://dev.to/abusithik/how-to-build-a-selenium-ui-testing-framework-3df9</guid>
      <description>&lt;h2&gt;
  
  
  Selenium UI Testing Automation Framework
&lt;/h2&gt;

&lt;p&gt;I recently created a Selenium UI testing automation framework for a sample flight/hotel booking website. The framework is based on the PageFactory and Fluent Interface patterns, and it uses Selenium, TestNG, and Extent Reports.&lt;/p&gt;

&lt;p&gt;The framework is designed to be easy to use and maintain. It includes a set of page classes that represent the different pages of the website, as well as a set of test classes that exercise the page classes. The test classes are written using the Fluent Interface pattern, which makes them more readable and maintainable.&lt;/p&gt;

&lt;p&gt;The framework also includes a report generator that creates interactive and detailed reports for the test results. The reports include information such as the test name, the test status, and the test steps. &lt;/p&gt;

&lt;p&gt;This framework can be used for UI automation testing of any web application. I am confident that it will be a valuable tool for other developers as well.&lt;/p&gt;

&lt;h3&gt;
  
  
  Framework Details
&lt;/h3&gt;

&lt;h4&gt;
  
  
  Test Application
&lt;/h4&gt;

&lt;p&gt;For this framework, I used &lt;a href="https://cleartrip.com" rel="noopener noreferrer"&gt;ClearTrip&lt;/a&gt; as the sample flight/hotel booking website.&lt;/p&gt;

&lt;h4&gt;
  
  
  Framework - What and Why?
&lt;/h4&gt;

&lt;p&gt;A framework is a pre-existing codebase that provides common functionalities and solves basic tasks for software requirements. By building on top of a framework, developers can focus on solving specific requirements without reinventing the wheel. The chosen framework serves as the foundation for implementing the software requirements specific to the project.&lt;/p&gt;

&lt;h4&gt;
  
  
  Project Structure
&lt;/h4&gt;

&lt;p&gt;The project follows a standard directory structure. The page classes and related files are located in the &lt;code&gt;src/main/java/com/cleartrip/casestudy&lt;/code&gt; path, while the test classes and related files can be found in the &lt;code&gt;src/test/java/com/cleartrip/casestudy&lt;/code&gt; path. This separation allows for better organization and maintainability.&lt;/p&gt;

&lt;h4&gt;
  
  
  Key features of the framework:
&lt;/h4&gt;

&lt;ul&gt;
&lt;li&gt;Page classes for each page under test, utilizing the PageFactory model to find and initialize web elements.&lt;/li&gt;
&lt;li&gt;Test classes that create instances of the page classes to access their methods, decoupling tests from the page elements.&lt;/li&gt;
&lt;li&gt;Static instantiation of the WebDriver to ensure only one instance is shared across all test classes.&lt;/li&gt;
&lt;li&gt;Relevant dependencies and plugins defined in the &lt;code&gt;pom.xml&lt;/code&gt; file.&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Packages
&lt;/h3&gt;

&lt;h4&gt;
  
  
  Main Package
&lt;/h4&gt;

&lt;blockquote&gt;
&lt;p&gt;The &lt;code&gt;src/main/java/&lt;/code&gt; package contains the core framework code. All the page classes and related files are located in this main package. Detailed documentation can be found in the Javadoc at &lt;code&gt;&amp;lt;working-dir&amp;gt;/doc/index.html&lt;/code&gt;.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h4&gt;
  
  
  Test Package
&lt;/h4&gt;

&lt;blockquote&gt;
&lt;p&gt;The &lt;code&gt;src/test/java/&lt;/code&gt; package contains the actual test classes. The subpackage &lt;code&gt;com.cleartrip.casestudy.tests&lt;/code&gt; holds all the TestNG test classes related to the ClearTrip application. The Javadoc for these test classes can be found at &lt;code&gt;&amp;lt;working-dir&amp;gt;/doc/com/cleartrip/casestudy/tests/package-summary.html&lt;/code&gt;.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h4&gt;
  
  
  Reports
&lt;/h4&gt;

&lt;blockquote&gt;
&lt;p&gt;With the Extent library, we can generate interactive and detailed reports for our UI test results. The test reports can be found at &lt;code&gt;workingDir/ExtentReports/ExtentReportResults.html&lt;/code&gt;.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h3&gt;
  
  
  Installation
&lt;/h3&gt;

&lt;p&gt;To set up and run the UI automation framework on your local system, follow these steps:&lt;/p&gt;

&lt;h4&gt;
  
  
  Steps to follow to set up Selenium in the local system:
&lt;/h4&gt;

&lt;p&gt;&lt;strong&gt;1. Install Java:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Check if Java is already installed by running &lt;code&gt;java -version&lt;/code&gt; and &lt;code&gt;javac -version&lt;/code&gt; in the terminal.&lt;/li&gt;
&lt;li&gt;If Java is not installed or an outdated version is present, install the latest Java Development Kit (JDK).&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;2. Install Eclipse or any other latest IDE:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Download the Eclipse installer and run it.&lt;/li&gt;
&lt;li&gt;Select the option to install Eclipse for Java developers.&lt;/li&gt;
&lt;li&gt;Open the Eclipse workbench.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;3. Get the Codebase:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Clone the &lt;code&gt;selenium-pom-factory-fluent-pattern&lt;/code&gt; repository from &lt;a href="https://github.com/abu-sithik/selenium-pom-factory-fluent-pattern.git" rel="noopener noreferrer"&gt;GitHub&lt;/a&gt;.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;4. Set up the Project in Eclipse:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Open Eclipse and go to &lt;code&gt;File -&amp;gt; Open Project from File System&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;Browse the cloned project folder and open it.&lt;/li&gt;
&lt;li&gt;Install the TestNG plugin for Eclipse by going to &lt;code&gt;Help -&amp;gt; Eclipse Marketplace&lt;/code&gt; -&amp;gt; search for TestNG and Install it and Restart Eclipse after the installation.&lt;/li&gt;
&lt;li&gt;The Maven plugin should be available by default. To verify, right-click on the project and check if there is an option called "Maven".&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Possible issues:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;If there is an error in the &lt;code&gt;pom.xml&lt;/code&gt; file, try the following steps:

&lt;ul&gt;
&lt;li&gt;Open the terminal and navigate to &lt;code&gt;Users/&amp;lt;profile_name&amp;gt;/.m2&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;Run &lt;code&gt;rm -r repository&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;Right-click on the project and select "Update project".&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;li&gt;If there are errors in all import statements, try the following steps:

&lt;ul&gt;
&lt;li&gt;Click on the &lt;code&gt;src/main/java&lt;/code&gt; folder, go to &lt;code&gt;Build Path&lt;/code&gt;, and remove it from the build path.&lt;/li&gt;
&lt;li&gt;Click on &lt;code&gt;src -&amp;gt; main -&amp;gt; java&lt;/code&gt;, right-click, go to &lt;code&gt;Build Path&lt;/code&gt;, and select "Use as Source Folder".&lt;/li&gt;
&lt;li&gt;Refresh the project.&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;5. Set up verification:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;In the test package, right-click on any Java file and select "Run As TestNG Test".&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Simple Example Test
&lt;/h3&gt;

&lt;p&gt;Let's get started with a simple example of creating a test for flight booking using the page factory/fluent interface pattern.&lt;/p&gt;

&lt;p&gt;Test Steps:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Go to &lt;a href="http://www.cleartrip.com" rel="noopener noreferrer"&gt;www.cleartrip.com&lt;/a&gt;.&lt;/li&gt;
&lt;li&gt;Select the trip type (One Way Trip).&lt;/li&gt;
&lt;li&gt;Select the origin (Bangalore).&lt;/li&gt;
&lt;li&gt;Select the destination (Delhi).&lt;/li&gt;
&lt;li&gt;Select a date (Any random date).&lt;/li&gt;
&lt;li&gt;Click on the "Search" button.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Refer to the image below to understand the page classes and test classes for different application pages:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fgithub.com%2Fabu-sithik%2Fselenium-pom-factory-fluent-pattern%2Fblob%2Fmaster%2FExtentReports%2Fpackage.png%3Fraw%3Dtrue" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fgithub.com%2Fabu-sithik%2Fselenium-pom-factory-fluent-pattern%2Fblob%2Fmaster%2FExtentReports%2Fpackage.png%3Fraw%3Dtrue" alt="Package Structure"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h4&gt;
  
  
  Step 1: Create a Page Class
&lt;/h4&gt;

&lt;p&gt;We need to create page objects for all the implemented pages in the ClearTrip site: &lt;code&gt;HomePage.java&lt;/code&gt;, &lt;code&gt;FlightResultsPage.java&lt;/code&gt;, &lt;code&gt;HotelResultsPage.java&lt;/code&gt;, &lt;code&gt;HotelsPage.java&lt;/code&gt;, and &lt;code&gt;SignInPopup.java&lt;/code&gt;. These page classes will be used in the test classes to perform actions and assertions.&lt;/p&gt;

&lt;p&gt;Let's consider the following scenario: On the home page, the user selects the flight booking option, enters valid details, and clicks the search button, which redirects them to the flight results page.&lt;/p&gt;

&lt;p&gt;Example page class for the Home page (&lt;code&gt;HomePage.java&lt;/code&gt;):&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight java"&gt;&lt;code&gt;

&lt;span class="kd"&gt;public&lt;/span&gt; &lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;HomePage&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
    &lt;span class="kd"&gt;private&lt;/span&gt; &lt;span class="nc"&gt;WebDriver&lt;/span&gt; &lt;span class="n"&gt;driver&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;
    &lt;span class="kd"&gt;private&lt;/span&gt; &lt;span class="nc"&gt;WebDriverWait&lt;/span&gt; &lt;span class="n"&gt;wait&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;

    &lt;span class="nd"&gt;@FindBy&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;xpath&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"//nav[@class='row fieldRow tripType']"&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt;
    &lt;span class="kd"&gt;private&lt;/span&gt; &lt;span class="nc"&gt;WebElement&lt;/span&gt; &lt;span class="n"&gt;tripType&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;

    &lt;span class="nd"&gt;@FindBy&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;id&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"FromTag"&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt;
    &lt;span class="kd"&gt;private&lt;/span&gt; &lt;span class="nc"&gt;WebElement&lt;/span&gt; &lt;span class="n"&gt;origin&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;

    &lt;span class="c1"&gt;// Other web elements and methods...&lt;/span&gt;

    &lt;span class="kd"&gt;public&lt;/span&gt; &lt;span class="nf"&gt;HomePage&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;WebDriver&lt;/span&gt; &lt;span class="n"&gt;driver&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;driver&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;driver&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;
        &lt;span class="nc"&gt;PageFactory&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;initElements&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;driver&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;
        &lt;span class="n"&gt;wait&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;WebDriverWait&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;driver&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="nc"&gt;WaitTimes&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;TIME_5_SECONDS&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;getWaitTime&lt;/span&gt;&lt;span class="o"&gt;());&lt;/span&gt;
    &lt;span class="o"&gt;}&lt;/span&gt;


    &lt;span class="c1"&gt;// Methods for interacting with the elements and performing actions on the Home page...&lt;/span&gt;

    &lt;span class="cm"&gt;/**
     * This method launches the home page
     */&lt;/span&gt;
    &lt;span class="kd"&gt;public&lt;/span&gt; &lt;span class="nc"&gt;HomePage&lt;/span&gt; &lt;span class="nf"&gt;launch&lt;/span&gt;&lt;span class="o"&gt;()&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
        &lt;span class="n"&gt;driver&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;get&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"https://www.cleartrip.com/"&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;
    &lt;span class="o"&gt;}&lt;/span&gt;

    &lt;span class="cm"&gt;/**
     * This method selects the trip type
     *
     * @param tripTypeOption Value of Trip Type
     */&lt;/span&gt;
    &lt;span class="kd"&gt;public&lt;/span&gt; &lt;span class="nc"&gt;HomePage&lt;/span&gt; &lt;span class="nf"&gt;selectTripType&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;String&lt;/span&gt; &lt;span class="n"&gt;tripTypeOption&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
        &lt;span class="n"&gt;tripType&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;findElement&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;By&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;xpath&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"//input[@value='"&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="n"&gt;tripTypeOption&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="s"&gt;"']"&lt;/span&gt;&lt;span class="o"&gt;)).&lt;/span&gt;&lt;span class="na"&gt;click&lt;/span&gt;&lt;span class="o"&gt;();&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;
    &lt;span class="o"&gt;}&lt;/span&gt;

    &lt;span class="cm"&gt;/**
     * This method selects the place of origin
     *
     * @param origin Value of Origin
     */&lt;/span&gt;
    &lt;span class="kd"&gt;public&lt;/span&gt; &lt;span class="nc"&gt;HomePage&lt;/span&gt; &lt;span class="nf"&gt;selectOrigin&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;String&lt;/span&gt; &lt;span class="n"&gt;origin&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;origin&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;clear&lt;/span&gt;&lt;span class="o"&gt;();&lt;/span&gt;
        &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;origin&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;sendKeys&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;origin&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;
        &lt;span class="n"&gt;wait&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;until&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;ExpectedConditions&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;visibilityOfAllElements&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;originOptions&lt;/span&gt;&lt;span class="o"&gt;));&lt;/span&gt;
        &lt;span class="n"&gt;originOptions&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;get&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;Indices&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;INDEX_0&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;getIndexValue&lt;/span&gt;&lt;span class="o"&gt;()).&lt;/span&gt;&lt;span class="na"&gt;click&lt;/span&gt;&lt;span class="o"&gt;();&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;
    &lt;span class="o"&gt;}&lt;/span&gt;

    &lt;span class="cm"&gt;/**
     * This method clicks on the search button after
     */&lt;/span&gt;
    &lt;span class="kd"&gt;public&lt;/span&gt; &lt;span class="nc"&gt;FlightResultsPage&lt;/span&gt; &lt;span class="nf"&gt;clickSearchButton&lt;/span&gt;&lt;span class="o"&gt;()&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
        &lt;span class="n"&gt;searchButton&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;click&lt;/span&gt;&lt;span class="o"&gt;();&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nf"&gt;FlightResultsPage&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;driver&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;
    &lt;span class="o"&gt;}&lt;/span&gt;

    &lt;span class="cm"&gt;/**
     * This method clicks on the "Hotels" link in the home page
     */&lt;/span&gt;
    &lt;span class="kd"&gt;public&lt;/span&gt; &lt;span class="nc"&gt;HotelsPage&lt;/span&gt; &lt;span class="nf"&gt;clickHotelsLink&lt;/span&gt;&lt;span class="o"&gt;()&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
        &lt;span class="n"&gt;hotelLink&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;click&lt;/span&gt;&lt;span class="o"&gt;();&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nf"&gt;HotelsPage&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;driver&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;
    &lt;span class="o"&gt;}&lt;/span&gt;

    &lt;span class="cm"&gt;/**
     * This method clicks on the SignIn option which appears after clicking the "Your Trips" options
     */&lt;/span&gt;
    &lt;span class="kd"&gt;public&lt;/span&gt; &lt;span class="nc"&gt;SignInPopup&lt;/span&gt; &lt;span class="nf"&gt;clickSignInOption&lt;/span&gt;&lt;span class="o"&gt;()&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
        &lt;span class="n"&gt;signIn&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;click&lt;/span&gt;&lt;span class="o"&gt;();&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nf"&gt;SignInPopup&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;driver&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;
    &lt;span class="o"&gt;}&lt;/span&gt;

&lt;span class="o"&gt;}&lt;/span&gt;


&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;In the above example, we have created a page class for the ClearTrip Home page. It contains the necessary web elements and action methods associated with the Home page, initialized using the PageFactory model.&lt;/p&gt;

&lt;p&gt;Please note that this is just an example, and you'll need to create similar page classes for other pages in the application.&lt;/p&gt;

&lt;h4&gt;
  
  
  Step 2: Create a TestBase Class
&lt;/h4&gt;

&lt;blockquote&gt;
&lt;p&gt;Create a Base Test Class which is extended in all the Test Classes. So The WebDriver is instantiated in such a way that it is static and only one instance will be created and shared across all the test classes. The Driver will only quit once all the tests are done.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;&lt;strong&gt;TestBase.java&lt;/strong&gt;&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight java"&gt;&lt;code&gt;

&lt;span class="cm"&gt;/**
 * Base Test Class which is extended Test Classes
 */&lt;/span&gt;
&lt;span class="kd"&gt;public&lt;/span&gt; &lt;span class="kd"&gt;abstract&lt;/span&gt; &lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;TestBase&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;

    &lt;span class="cm"&gt;/**
     * Static WebDriver Instance
     */&lt;/span&gt;
    &lt;span class="kd"&gt;protected&lt;/span&gt; &lt;span class="kd"&gt;static&lt;/span&gt; &lt;span class="nc"&gt;WebDriver&lt;/span&gt; &lt;span class="n"&gt;driver&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;


    &lt;span class="kd"&gt;public&lt;/span&gt; &lt;span class="nc"&gt;WebDriver&lt;/span&gt; &lt;span class="nf"&gt;getDriver&lt;/span&gt;&lt;span class="o"&gt;()&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;driver&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;
    &lt;span class="o"&gt;}&lt;/span&gt;

    &lt;span class="cm"&gt;/**
     * Suite level setup method where the static instance of the WebDriver is instantiated
     */&lt;/span&gt;
    &lt;span class="nd"&gt;@BeforeSuite&lt;/span&gt;
    &lt;span class="kd"&gt;public&lt;/span&gt; &lt;span class="kd"&gt;static&lt;/span&gt; &lt;span class="kt"&gt;void&lt;/span&gt; &lt;span class="nf"&gt;initDriver&lt;/span&gt;&lt;span class="o"&gt;()&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;

        &lt;span class="nc"&gt;WebDriverManager&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;chromedriver&lt;/span&gt;&lt;span class="o"&gt;().&lt;/span&gt;&lt;span class="na"&gt;setup&lt;/span&gt;&lt;span class="o"&gt;();&lt;/span&gt;
    &lt;span class="n"&gt;driver&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;ChromeDriver&lt;/span&gt;&lt;span class="o"&gt;();&lt;/span&gt;

    &lt;span class="nc"&gt;ChromeOptions&lt;/span&gt; &lt;span class="n"&gt;options&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;ChromeOptions&lt;/span&gt;&lt;span class="o"&gt;();&lt;/span&gt;

        &lt;span class="c1"&gt;// The following statement sets the argument to disable notification in chrome when executing the tests&lt;/span&gt;
        &lt;span class="n"&gt;options&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;addArguments&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"--disable-notifications"&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;
        &lt;span class="n"&gt;driver&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;ChromeDriver&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;options&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;
        &lt;span class="n"&gt;driver&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;manage&lt;/span&gt;&lt;span class="o"&gt;().&lt;/span&gt;&lt;span class="na"&gt;window&lt;/span&gt;&lt;span class="o"&gt;().&lt;/span&gt;&lt;span class="na"&gt;maximize&lt;/span&gt;&lt;span class="o"&gt;();&lt;/span&gt;
    &lt;span class="o"&gt;}&lt;/span&gt;


    &lt;span class="cm"&gt;/**
     * Suite level tear-down method to quit the WebDriver instance
     */&lt;/span&gt;
    &lt;span class="nd"&gt;@AfterSuite&lt;/span&gt;
    &lt;span class="kd"&gt;public&lt;/span&gt; &lt;span class="kt"&gt;void&lt;/span&gt; &lt;span class="nf"&gt;quitDriver&lt;/span&gt;&lt;span class="o"&gt;()&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
        &lt;span class="n"&gt;driver&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;quit&lt;/span&gt;&lt;span class="o"&gt;();&lt;/span&gt;
    &lt;span class="o"&gt;}&lt;/span&gt;
&lt;span class="o"&gt;}&lt;/span&gt;


&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;
&lt;h4&gt;
  
  
  Step 3: Create a Test Class
&lt;/h4&gt;

&lt;blockquote&gt;
&lt;p&gt;In test class, instances of the page classes will be created to access methods in the page classes. Thus, tests are decoupled from the pages/ elements under test. This makes the framework more maintainable.&lt;/p&gt;

&lt;p&gt;So in this &lt;code&gt;FlightBookingTest&lt;/code&gt; class we have to create the objects of HomePage, FlightResultsPage to access the methods available in those pages.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;&lt;strong&gt;FlightBookingTest.java&lt;/strong&gt;&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight java"&gt;&lt;code&gt;

&lt;span class="cm"&gt;/**
 * Class containing test for validating search-flight results
 */&lt;/span&gt;
&lt;span class="kd"&gt;public&lt;/span&gt; &lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;FlightBookingTest&lt;/span&gt; &lt;span class="kd"&gt;extends&lt;/span&gt; &lt;span class="nc"&gt;TestBase&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;

    &lt;span class="cm"&gt;/**
     * Instance of HomePage class
     */&lt;/span&gt;
    &lt;span class="kd"&gt;private&lt;/span&gt; &lt;span class="nc"&gt;HomePage&lt;/span&gt; &lt;span class="n"&gt;homePage&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;

    &lt;span class="cm"&gt;/**
     * Instance of FlightResultsPage class
     */&lt;/span&gt;
    &lt;span class="kd"&gt;private&lt;/span&gt; &lt;span class="nc"&gt;FlightResultsPage&lt;/span&gt; &lt;span class="n"&gt;flightResultsPage&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;

    &lt;span class="cm"&gt;/**
     * Class level test-Setup method that intantiates {@link HomePage} and {@link FlightResultsPage}
     */&lt;/span&gt;
    &lt;span class="nd"&gt;@BeforeClass&lt;/span&gt;
    &lt;span class="kd"&gt;public&lt;/span&gt; &lt;span class="kt"&gt;void&lt;/span&gt; &lt;span class="nf"&gt;testClasSetup&lt;/span&gt;&lt;span class="o"&gt;()&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
        &lt;span class="n"&gt;homePage&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;HomePage&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;driver&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;
        &lt;span class="n"&gt;flightResultsPage&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;FlightResultsPage&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;driver&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;
    &lt;span class="o"&gt;}&lt;/span&gt;

    &lt;span class="cm"&gt;/**
     * Test method for verifying whether search summary is rightly displayed when searching for flights
     */&lt;/span&gt;
    &lt;span class="nd"&gt;@Test&lt;/span&gt;
    &lt;span class="kd"&gt;public&lt;/span&gt; &lt;span class="kt"&gt;void&lt;/span&gt; &lt;span class="nf"&gt;testThatResultsAppearForAOneWayJourney&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;Method&lt;/span&gt; &lt;span class="n"&gt;method&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;

        &lt;span class="nc"&gt;ExtentTestManager&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;startTest&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;method&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;getName&lt;/span&gt;&lt;span class="o"&gt;(),&lt;/span&gt; &lt;span class="s"&gt;"Test method for verifying whether search summary is rightly displayed when searching for flights"&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;

        &lt;span class="nc"&gt;ExtentTestManager&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;getTest&lt;/span&gt;&lt;span class="o"&gt;().&lt;/span&gt;&lt;span class="na"&gt;log&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;LogStatus&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;INFO&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="s"&gt;"Launching the browser"&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;
        &lt;span class="n"&gt;homePage&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;launch&lt;/span&gt;&lt;span class="o"&gt;()&lt;/span&gt; 
            &lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;selectTripType&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;TripTypes&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;ONE_WAY&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;getTripType&lt;/span&gt;&lt;span class="o"&gt;())&lt;/span&gt;
            &lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;selectOrigin&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;Cities&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;BANGALORE&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;getCity&lt;/span&gt;&lt;span class="o"&gt;())&lt;/span&gt;
            &lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;selectDestination&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;Cities&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;Delhi&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;getCity&lt;/span&gt;&lt;span class="o"&gt;())&lt;/span&gt;
            &lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;selectDate&lt;/span&gt;&lt;span class="o"&gt;()&lt;/span&gt;
            &lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;clickSearchButton&lt;/span&gt;&lt;span class="o"&gt;();&lt;/span&gt;

        &lt;span class="nc"&gt;ExtentTestManager&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;getTest&lt;/span&gt;&lt;span class="o"&gt;().&lt;/span&gt;&lt;span class="na"&gt;log&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;LogStatus&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;INFO&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="s"&gt;"Asserting flight search summary details"&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;
        &lt;span class="nc"&gt;Assert&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;assertTrue&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;
                &lt;span class="n"&gt;flightResultsPage&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;isSearchSummaryAvailable&lt;/span&gt;&lt;span class="o"&gt;(),&lt;/span&gt;
                &lt;span class="s"&gt;"Search Summary is not available after clicking the Search Button when searching for flights"&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;
    &lt;span class="o"&gt;}&lt;/span&gt;


&lt;span class="o"&gt;}&lt;/span&gt;


&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;In the above example, we create a test class &lt;code&gt;FlightBookingTest&lt;/code&gt; that uses the &lt;code&gt;HomePage&lt;/code&gt; page class to perform actions on the ClearTrip website's Home page and then asserts or performs further actions on the Flight Results page.&lt;/p&gt;

&lt;h4&gt;
  
  
  Step 4: Run the Test
&lt;/h4&gt;

&lt;p&gt;To run the test, right-click on the test class &lt;code&gt;FlightBookingTest&lt;/code&gt; and select "Run As TestNG Test". This will execute the test steps and display the test results in the console.&lt;/p&gt;

&lt;h4&gt;
  
  
  Extent HTML report&lt;a&gt;&lt;/a&gt;
&lt;/h4&gt;

&lt;blockquote&gt;
&lt;p&gt;Test Reports can be found in &lt;code&gt;workingDir/ExtentReports/ExtentReportResults.html&lt;/code&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;&lt;strong&gt;Test Summary&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fgithub.com%2Fabu-sithik%2Fselenium-pom-factory-fluent-pattern%2Fblob%2Fmaster%2FExtentReports%2Foverview.png%3Fraw%3Dtrue" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fgithub.com%2Fabu-sithik%2Fselenium-pom-factory-fluent-pattern%2Fblob%2Fmaster%2FExtentReports%2Foverview.png%3Fraw%3Dtrue" alt="alt text"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Specific Test details&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fgithub.com%2Fabu-sithik%2Fselenium-pom-factory-fluent-pattern%2Fblob%2Fmaster%2FExtentReports%2FspecificTest.gif%3Fraw%3Dtrue" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fgithub.com%2Fabu-sithik%2Fselenium-pom-factory-fluent-pattern%2Fblob%2Fmaster%2FExtentReports%2FspecificTest.gif%3Fraw%3Dtrue" alt="alt text"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  Conclusion
&lt;/h3&gt;

&lt;p&gt;The framework provides an oranized structure for creating page classes, test classes, and generating interactive test reports using Extent Reports.&lt;/p&gt;

&lt;p&gt;By following the installation steps and the provided example, you can set up the framework on your local system and start creating UI automation tests for web applications.&lt;/p&gt;

&lt;p&gt;For a more detailed understanding of each component and the complete source code, please refer to the &lt;a href="https://github.com/abu-sithik/selenium-pom-factory-fluent-pattern" rel="noopener noreferrer"&gt;GitHub repository&lt;/a&gt;.&lt;/p&gt;

</description>
      <category>selenium</category>
      <category>test</category>
      <category>java</category>
      <category>automation</category>
    </item>
    <item>
      <title>Integrating Newman API test collections with Jenkins and publish reports using Flock messenger</title>
      <dc:creator>Abubakkar Sithik</dc:creator>
      <pubDate>Fri, 11 Jun 2021 15:01:20 +0000</pubDate>
      <link>https://dev.to/abusithik/integrating-newman-api-test-collections-with-jenkins-and-publish-reports-using-flock-messenger-57cm</link>
      <guid>https://dev.to/abusithik/integrating-newman-api-test-collections-with-jenkins-and-publish-reports-using-flock-messenger-57cm</guid>
      <description>&lt;blockquote&gt;
&lt;p&gt;&lt;em&gt;In this article, we will be exploring about how to run Postman collections on Jenkins with Freestyle Project. And we will try to generate our test run reports using Allure Jenkins plugin and will publish the test run status notifications to Flock messenger.&lt;/em&gt; &lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2&gt;
  
  
  Contents
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;
Jenkins Setup

&lt;ul&gt;
&lt;li&gt;Step1:Install NodeJS plugin&lt;/li&gt;
&lt;li&gt;Step2:Add Newman and Allure Packages&lt;/li&gt;
&lt;li&gt;Step3:Create a Newman job with freestyle.&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;
&lt;li&gt;How to Run&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Jenkins Setup&lt;a&gt;&lt;/a&gt;
&lt;/h2&gt;

&lt;p&gt;Before we get started, you’ll need to install Jenkins on your machine. If you don’t have a setup Jenkins on your machine please follow this official &lt;a href="https://www.jenkins.io/doc/book/installing/"&gt;documentation&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;After installing Jenkins, we need to configure NodeJS on Jenkins.&lt;/p&gt;

&lt;h3&gt;
  
  
  Step1: Install &lt;a href="https://plugins.jenkins.io/nodejs/"&gt;NodeJS plugin&lt;/a&gt; :&lt;a&gt;&lt;/a&gt;
&lt;/h3&gt;

&lt;p&gt;&lt;code&gt;Go to → Manage Jenkins → Select Manage Plugins → Select Available tab and Search Active Choice Plugin.&lt;/code&gt;&lt;br&gt;
&lt;a href="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fvsyucb6zhr8xnwqy241r.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fvsyucb6zhr8xnwqy241r.png" alt="Alt Text" width="800" height="204"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Install and restart the Jenkins for proper installation of the plugin.&lt;/p&gt;
&lt;h3&gt;
  
  
  Step2: Add Newman and Allure Packages in NodeJS&lt;a&gt;&lt;/a&gt;
&lt;/h3&gt;

&lt;p&gt;&lt;code&gt;Open Jenkins: Manage Jenkins → Global Tool Configuration → NodeJS&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;Set your compatible node version. We can set multiple NodeJS versions for multiple applications.&lt;/p&gt;

&lt;p&gt;&lt;span&gt; Make sure you follow these steps: &lt;/span&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Check the “Install automatically” option&lt;/li&gt;
&lt;li&gt;Select compatible Node version (NodeJs-16.2.0)&lt;/li&gt;
&lt;li&gt;Add required packages in the “Global Packages to Install” section In our case, add the following packages newman newman-reporter-allure&lt;/li&gt;
&lt;li&gt;Save settings.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fb3t2n2k95ch5wv6xo2b9.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fb3t2n2k95ch5wv6xo2b9.png" alt="Alt Text" width="800" height="480"&gt;&lt;/a&gt;&lt;/p&gt;
&lt;h3&gt;
  
  
  Step3: Create a Newman job with freestyle.&lt;a&gt;&lt;/a&gt;
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;1. Create a Freestyle job to execute Newman collections scripts.&lt;/strong&gt;&lt;br&gt;
    &lt;code&gt;Open Jenkins → New Item → Enter any job name→ Choose Freestyle Project → Click on Save button&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;2. Parameterising the Jenkins build for different Newman environment&lt;/strong&gt;&lt;br&gt;
&lt;em&gt;A build parameter allows us to pass data into our Jenkins jobs. Build parameters are a powerful way to make any Jenkins job more dynamic.So by using build parameters, we can pass any data we want. In our use case, we will be passing the Newman environment variables. Note: Follow this &lt;a href="https://docs.api.getpostman.com/"&gt;tutorial&lt;/a&gt;, If you want to get a Newman environment variables’ unique id.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://plugins.jenkins.io/extended-choice-parameter/"&gt;Extended Choice Parameter&lt;/a&gt; Plug-In Installation:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Go to 'Manage Jenkins' from the Jenkins homepage.&lt;/li&gt;
&lt;li&gt;Navigate to Manage Plugins&lt;/li&gt;
&lt;li&gt;Change the tab to Available&lt;/li&gt;
&lt;li&gt;Search for extended choice parameter&lt;/li&gt;
&lt;li&gt;Click “Download now and install after restart”&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;code&gt;General → Check “This project is parameterized” → Add parameter → Choice parameter → add respective choice values for parameterization [QA / Prod].&lt;/code&gt;&lt;br&gt;
&lt;a href="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fzvn2ce30cfd2p0610t2d.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fzvn2ce30cfd2p0610t2d.png" alt="Alt Text" width="800" height="683"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;code&gt;3.Source Code Management → Check “None”&lt;/code&gt;&lt;br&gt;
&lt;a href="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fyhmo9fllt3b580r48l54.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fyhmo9fllt3b580r48l54.png" alt="Alt Text" width="800" height="260"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;code&gt;4.Build environment → Check “Delete workspace before build starts”&lt;br&gt;
5.Build environment → Check “Provide Node &amp;amp; npm bin/ folder to PATH” → select NodeJs name&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fnotmdpf2njjaf21qove3.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fnotmdpf2njjaf21qove3.png" alt="Alt Text" width="800" height="488"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;code&gt;6.Build → Add build step → Select Execute Shell → And Paste the below code. → Save Your config.&lt;/code&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;eg:- newman run [collections] -e [environment] -r allure --reporter-allure-export target/allure-results --suppress-exit-code 1
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;newman run "https://api.getpostman.com/collections/[collection_uid]?apikey=[apiKey]" \
    --environment "https://api.getpostman.com/environments/$server?apikey=[apiKey]" \ 
    -r allure --reporter-allure-export target/allure-results \ 
    -- suppress-exit-code 1
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Note: If you want to run Newman via shell command using a unique collection id &amp;amp; API Key follow this &lt;a href="https://docs.api.getpostman.com/"&gt;tutorial&lt;/a&gt;. &lt;br&gt;
&lt;a href="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Frabuvyzxe2wvz2p5fcf6.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Frabuvyzxe2wvz2p5fcf6.png" alt="Alt Text" width="800" height="593"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Allure and Flock Plugin Installation&lt;/em&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Go to 'Manage Jenkins' from the Jenkins homepage.&lt;/li&gt;
&lt;li&gt;Navigate to Manage Plugins&lt;/li&gt;
&lt;li&gt;Change the tab to Available&lt;/li&gt;
&lt;li&gt;Search for Flock and Check the box&lt;/li&gt;
&lt;li&gt;Search for Allure Report and Check the box&lt;/li&gt;
&lt;li&gt;Click “Download now and install after restart”&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;code&gt;7.Post Build Action → Allure Report → Add Path (“target/allure-results”)&lt;/code&gt;&lt;br&gt;
Allure Reports will be generated after every successful builds and report files are placed under the given $PATH&lt;br&gt;
&lt;a href="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F3krw7o6qedcb9wbqomch.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F3krw7o6qedcb9wbqomch.png" alt="Alt Text" width="800" height="266"&gt;&lt;/a&gt;&lt;br&gt;
&lt;code&gt;8.Post Build Action → Send Flock Notification (add flock webhook URL)&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Please follow this guide to generate a Webhook URL&lt;/em&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Go to &lt;a href="https://apps.flock.com/?source=launcherbar"&gt;https://apps.flock.com/?source=launcherbar&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;Search for Jenkins&lt;/li&gt;
&lt;li&gt;Install and Configure to generate webhook URL.
&lt;img src="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F6eyahrec5ebpekxbpw9w.png" alt="Alt Text" width="800" height="314"&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  How to Run&lt;a&gt;&lt;/a&gt;
&lt;/h3&gt;

&lt;p&gt;Now we just have to build our job to execute our tests. And we can do so by clicking on the “Build with Parameters” button on the left side of the dashboard.&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Go to Jenkins Job [jenkins_url/job/[#JobName]]&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Click on Build with Parameter → Select the options that are required to be passed in the branch parameter [QA/ Prod] and click on → BUILD&lt;br&gt;
&lt;a href="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fsea58fci6gwc64l03agx.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fsea58fci6gwc64l03agx.png" alt="Alt Text" width="800" height="345"&gt;&lt;/a&gt;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Flock messenger would notify you about the Jenkins build status.&lt;br&gt;
&lt;a href="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fkoefiespv4zg2r5tprcb.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fkoefiespv4zg2r5tprcb.png" alt="Alt Text" width="800" height="364"&gt;&lt;/a&gt;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;To view reports click on “View” in flock → Will be redirected to Jenkins [After successful authentication]→ Click on Allure Report&lt;br&gt;
&lt;a href="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fkivtk74tgo38c3o8m9p0.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fkivtk74tgo38c3o8m9p0.png" alt="Alt Text" width="800" height="332"&gt;&lt;/a&gt;&lt;/p&gt;&lt;/li&gt;
&lt;/ol&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;em&gt;In this article we have seen how to use Jenkins to run Newman tests, generate &amp;amp; publish reports using Allure / Flock plugins. I hope you may have found this useful. Cheers!!&lt;/em&gt;&lt;/p&gt;
&lt;/blockquote&gt;

</description>
      <category>testing</category>
      <category>beginners</category>
      <category>jenkins</category>
      <category>node</category>
    </item>
    <item>
      <title>Automated Visual Regression Testing with BackstopJS</title>
      <dc:creator>Abubakkar Sithik</dc:creator>
      <pubDate>Thu, 10 Jun 2021 11:24:06 +0000</pubDate>
      <link>https://dev.to/abusithik/automated-visual-regression-testing-with-backstopjs-3bc4</link>
      <guid>https://dev.to/abusithik/automated-visual-regression-testing-with-backstopjs-3bc4</guid>
      <description>&lt;h3&gt;
  
  
  What is Visual Regression Testing?
&lt;/h3&gt;

&lt;blockquote&gt;
&lt;p&gt;In a definition from more famous Wikipedia, Regression testing is re-running functional and non-functional tests to ensure that previously developed and tested software still performs after a change. In &lt;strong&gt;Visual Regression Testing&lt;/strong&gt;, we're trying to run a test to verify the application has no visual difference from its former or less developed state. So in visual regression testing(VRT), most of the time we're not going to test some sort of functionality that we've built. &lt;/p&gt;

&lt;p&gt;Typically, the main use-case of VRT comes in a few different ways. One of them is just doing updates for web app. When an application is getting an update(like upgrading internal libraries or security updates), we usually don't expect anything to be visually different. So, after an update application should look exactly the same and work exactly the same way before and after the update. So in VRT, we make sure that everything is working fine before the code goes live.&lt;/p&gt;

&lt;p&gt;Another possible use case is doing production/live sanity testing. So, after the prod server code update, the application in the prod server should look exactly the same and work exactly the same way as dev or test environment.&lt;/p&gt;

&lt;p&gt;While doing visual regression manually, we might be able to catch very blatant differences, but it's really difficult for us to catch subtle differences and also performing testing repeatedly. That's where a tool like &lt;a href="https://github.com/garris/BackstopJS" rel="noopener noreferrer"&gt;BackstopJS&lt;/a&gt; helps us to automatically highlight the differences between the two screenshots that will make our testing a much easier.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h1&gt;
  
  
  Contents
&lt;/h1&gt;

&lt;ul&gt;
&lt;li&gt;BackstopJS&lt;/li&gt;
&lt;li&gt;Backstop Features&lt;/li&gt;
&lt;li&gt;BackstopJS workflow&lt;/li&gt;
&lt;li&gt;BackstopJS Benefits&lt;/li&gt;
&lt;li&gt;
Installation

&lt;ul&gt;
&lt;li&gt;BackstopJS Installation&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;li&gt;BackstopJS project structure&lt;/li&gt;

&lt;li&gt;

Creating a visual regression test

&lt;ul&gt;
&lt;li&gt;1. Navigate to your local project&lt;/li&gt;
&lt;li&gt;2. Initialize Backstop&lt;/li&gt;
&lt;li&gt;3. Edit backstop.json&lt;/li&gt;
&lt;li&gt;Add viewports&lt;/li&gt;
&lt;li&gt;Add scenarios&lt;/li&gt;
&lt;li&gt;How to handle cookies / sessions in backstopJS&lt;/li&gt;
&lt;li&gt;4. Create new reference screenshots&lt;/li&gt;
&lt;li&gt;5. Run tests&lt;/li&gt;
&lt;li&gt;6. Backstop approve&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;/ul&gt;

&lt;h2&gt;
  
  
  BackstopJS&lt;a&gt;&lt;/a&gt;
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://github.com/garris/BackstopJS" rel="noopener noreferrer"&gt;BackstopJS&lt;/a&gt; is a framework that automates visual regression testing. This framework is written in JS and consists of the following tools:Puppeteer(headless chrome)+ ResembleJS(diff library). It uses a headless Chrome, in that way it's not actually opening up our Chrome browser and it's not taking screenshots that we can see. All we have to do is, write a script for simulating user scenarios and run backstopJS commands and it goes through and simulates user flows with headless chrome automatically. All we have to do is, run a simple command in the command-line tool so that it will take care of all the work for us.&lt;/p&gt;

&lt;h2&gt;
  
  
  Backstop Features&lt;a&gt;&lt;/a&gt;
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;Renders with headless chrome&lt;/li&gt;
&lt;li&gt;Simulates user interactions with simple JS / Puppeteer scripts&lt;/li&gt;
&lt;li&gt;Browser reports with visual diffs, CLI reports, and JUnit reports&lt;/li&gt;
&lt;li&gt;Easy to integrate with CI(Jenkins) and source control&lt;/li&gt;
&lt;li&gt;Very easy to use, just 3 commands to do everything.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  BackstopJS workflow&lt;a&gt;&lt;/a&gt;
&lt;/h2&gt;

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

&lt;h2&gt;
  
  
  BackstopJS Benefits&lt;a&gt;&lt;/a&gt;
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;"Reference (production env) vs Test (test/staging env)" Comparison&lt;/li&gt;
&lt;li&gt;Multiple viewports support (desktop browsers, mobile browsers,..etc.)&lt;/li&gt;
&lt;li&gt;Easy way to write UI tests (supports puppeteer scripts)&lt;/li&gt;
&lt;li&gt;Inbuilt interactive and detailed reports&lt;/li&gt;
&lt;li&gt;Easily way to scan our web application (backstop-crawl)&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  BackstopJS Installation&lt;a&gt;&lt;/a&gt;
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Check Node
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;The first thing you should do is check to see if you have a version of Node.js already installed. To do that in mac:
1. Open the Terminal &amp;amp; Enter `node - v` in the Terminal and press Enter
2. If you do have Node.js installed, it will output the version. Update to the latest version using `npm i -g npm`.
3. If you do not have Node.js installed, it will output something like `-bash: node: command not found` Continue with these instructions to install it. Go to https://nodejs.org/en/download/ You'll see download links for macOS. After Download, open the file &amp;amp; Go through the entire installation process. 
4. Enter node - v in the Terminal to verify that Node.js is installed correctly.
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  BackstopJS Installation&lt;a&gt;&lt;/a&gt;
&lt;/h3&gt;

&lt;h4&gt;
  
  
  Global installation (recommended)
&lt;/h4&gt;

&lt;p&gt;&lt;code&gt;$ npm install -g backstopjs&lt;/code&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  BackstopJS project structure&lt;a&gt;&lt;/a&gt;
&lt;/h3&gt;

&lt;blockquote&gt;
&lt;p&gt;Inorder to create a new visual automation testing project, First of all, we need to initialize Backstop. To do that run &lt;code&gt;backtop init&lt;/code&gt; in the terminal.&lt;br&gt;
&lt;/p&gt;
&lt;/blockquote&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;mkdir ./sample_visualTestingProject
cd ./sample_visualTestingProject
backstop init
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;blockquote&gt;
&lt;p&gt;Now, the script will generate a new backstop config file &lt;code&gt;backstop.json&lt;/code&gt; and a folder &lt;code&gt;backstop_data&lt;/code&gt;. &lt;code&gt;backstop.json&lt;/code&gt; has some pre-configured stuff in it. &lt;code&gt;backstop_data&lt;/code&gt; folder has some file structure in place to save the screenshots it's gonna be generating.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;The generated &lt;code&gt;backstop.json&lt;/code&gt; file, should look something like this&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;{
  "id": "backstop_default",
  "viewports": [
    {
      "label": "phone",
      "width": 320,
      "height": 480
    },
    {
      "label": "tablet",
      "width": 1024,
      "height": 768
    }
  ],
  "onBeforeScript": "puppet/onBefore.js",
  "onReadyScript": "puppet/onReady.js",
  "scenarios": [
    {
      "label": "BackstopJS Homepage",
      "cookiePath": "backstop_data/engine_scripts/cookies.json",
      "url": "https://garris.github.io/BackstopJS/",
      "referenceUrl": "",
      "readyEvent": "",
      "readySelector": "",
      "delay": 0,
      "hideSelectors": [],
      "removeSelectors": [],
      "hoverSelector": "",
      "clickSelector": "",
      "postInteractionWait": 0,
      "selectors": [],
      "selectorExpansion": true,
      "expect": 0,
      "misMatchThreshold" : 0.1,
      "requireSameDimensions": true
    }
  ],
  "paths": {
    "bitmaps_reference": "backstop_data/bitmaps_reference",
    "bitmaps_test": "backstop_data/bitmaps_test",
    "engine_scripts": "backstop_data/engine_scripts",
    "html_report": "backstop_data/html_report",
    "ci_report": "backstop_data/ci_report"
  },
  "report": ["browser"],
  "engine": "puppeteer",
  "engineOptions": {
    "args": ["--no-sandbox"]
  },
  "asyncCaptureLimit": 5,
  "asyncCompareLimit": 50,
  "debug": false,
  "debugWindow": false
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;blockquote&gt;
&lt;p&gt;&lt;code&gt;backstop.json&lt;/code&gt; is a configuration file, everything related to tests are defined in this file.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Lets start off with &lt;code&gt;viewports&lt;/code&gt;.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;"viewports": [
    {
      "label": "phone",
      "width": 320,
      "height": 480
    },
    {
      "label": "tablet",
      "width": 1024,
      "height": 768
    }
  ]
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;code&gt;viewports&lt;/code&gt; are just a dimensions of the application site that we want to test /capture screenshots. The above example has one for phone and another one for tablet, maybe if we want to do desktop or whatever other dimensions are relevant for testing, we can simply add those dimenesion in &lt;code&gt;viewports&lt;/code&gt; section.&lt;/p&gt;

&lt;p&gt;In below section of &lt;code&gt;backstop.json&lt;/code&gt;, we have a section for &lt;code&gt;scenarios&lt;/code&gt; where we can define different pages on our application and all the scenarios/flows for that.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;"scenarios": [
    {
      "label": "BackstopJS Homepage",
      "cookiePath": "backstop_data/engine_scripts/cookies.json",
      "url": "https://garris.github.io/BackstopJS/",
      "referenceUrl": "",
      "readyEvent": "",
      "readySelector": "",
      "delay": 0,
      "hideSelectors": [],
      "removeSelectors": [],
      "hoverSelector": "",
      "clickSelector": "",
      "postInteractionWait": 0,
      "selectors": [],
      "selectorExpansion": true,
      "expect": 0,
      "misMatchThreshold" : 0.1,
      "requireSameDimensions": true
    }
  ]
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;We have a &lt;code&gt;label&lt;/code&gt; which is describing what this particular scenario is, and we have the &lt;code&gt;URL&lt;/code&gt; which is the URL that we're testing, this is gonna be our production application URL and then the &lt;code&gt;reference URL&lt;/code&gt; which is the baseline URL that we're testing it against. All these scenario properties are described here,&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;label                    // [required] Tag saved with your reference images
onBeforeScript           // Used to set up browser state e.g. cookies.
cookiePath               // import cookies in JSON format (available with default onBeforeScript see setting cookies below)
url                      // [required] The url of your app state
referenceUrl             // Specify a different state or environment when creating reference.
readyEvent               // Wait until this string has been logged to the console.
readySelector            // Wait until this selector exists before continuing.
delay                    // Wait for x milliseconds
hideSelectors            // Array of selectors set to visibility: hidden
removeSelectors          // Array of selectors set to display: none
onReadyScript            // After the above conditions are met -- use this script to modify UI state prior to screen shots e.g. hovers, clicks etc.
keyPressSelectors        // Takes array of selector and string values -- simulates multiple sequential keypress interactions.
hoverSelectors           // *Puppeteer only* takes array of selectors -- simulates multiple sequential hover interactions.
clickSelectors           // *Puppeteer only* takes array of selectors -- simulates multiple sequential click interactions.
postInteractionWait      // Wait for a selector after interacting with hoverSelector or clickSelector (optionally accepts wait time in ms. Idea for use with a click or hover element transition. available with default onReadyScript)
scrollToSelector         // Scrolls the specified DOM element into view prior to screen shot (available with default onReadyScript)
selectors                // Array of selectors to capture. Defaults to document if omitted. Use "viewport" to capture the viewport size. See Targeting elements in the next section for more info...
viewports                // An array of screen size objects your DOM will be tested against. This configuration will override the viewports property assigned at the config root.
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;for more details refer this &lt;a href="https://github.com/garris/BackstopJS#using-backstopjs" rel="noopener noreferrer"&gt;doc&lt;/a&gt;.&lt;/p&gt;

&lt;h1&gt;
  
  
  Creating a visual regression test&lt;a&gt;&lt;/a&gt;
&lt;/h1&gt;

&lt;blockquote&gt;
&lt;p&gt;Now, let's get started with the simple user flow: &lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Land on page 1 -&amp;gt; do some action (click a link/button) -&amp;gt; go to page 2&lt;/p&gt;

&lt;h2&gt;
  
  
  Scenarios:
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;Scenario 1: land on page 1, BackstopJS takes screenshot for page 1&lt;/li&gt;
&lt;li&gt;Scenario 2: from page 1, do some action and go to page 2, BackstopJS takes screenshot for page 2&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  BackstopJS Flow
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;Navigate to your local project&lt;/li&gt;
&lt;li&gt;Initialize Backstop: backstop init&lt;/li&gt;
&lt;li&gt;Edit your backstop.json file

&lt;ul&gt;
&lt;li&gt;Add &lt;code&gt;viewports&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;Add &lt;code&gt;scenarios&lt;/code&gt;
&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;li&gt;Create new reference screenshots: backstop reference&lt;/li&gt;

&lt;li&gt;Run tests: backstop test&lt;/li&gt;

&lt;li&gt;Approve tests: backstop approve&lt;/li&gt;

&lt;/ul&gt;

&lt;h3&gt;
  
  
  1. Navigate to your local project&lt;a&gt;&lt;/a&gt;
&lt;/h3&gt;

&lt;p&gt;After installing BackstopJS, create (or) navigate to project folder,&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;mkdir ./sample_visualTestingProject
cd ./sample_visualTestingProject
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  2. Initialize Backstop&lt;a&gt;&lt;/a&gt;
&lt;/h3&gt;

&lt;p&gt;Intialize backstop setup by running the following command. This command will generate a new backstop config file &lt;code&gt;backstop.json&lt;/code&gt; and a folder &lt;code&gt;backstop_data&lt;/code&gt;. This is just an one time command, if backstop files are already there, we need not to run this command again.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;backstop init
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  3. Edit backstop.json&lt;a&gt;&lt;/a&gt;
&lt;/h3&gt;

&lt;h5&gt;
  
  
  3.1 Add viewports&lt;a&gt;&lt;/a&gt;
&lt;/h5&gt;

&lt;p&gt;open &lt;code&gt;backstop.json&lt;/code&gt; in any editor, and set &lt;code&gt;viewports&lt;/code&gt; for desktop and mobile chrome,&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;"viewports": [
    {
      "label": "Desktop Chrome",
      "width": 1280,
      "height": 800
    },
    {
      "label": "Mobile Chrome",
      "width": 375,
      "height": 812
    }
  ]
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h5&gt;
  
  
  3.2 Add scenarios&lt;a&gt;&lt;/a&gt;
&lt;/h5&gt;

&lt;h6&gt;
  
  
  Scenario 1: land on page 1, BackstopJS takes screenshot for page 1
&lt;/h6&gt;

&lt;p&gt;Let's use this &lt;a href="https://www.walmart.com/ip/Super-Mario-Odyssey-Nintendo-Switch/56011600" rel="noopener noreferrer"&gt;walmart&lt;/a&gt; product page as base page. So after user landing on this page, we have to take screenshot of this page using backstopJS.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;    {
      "label": "walmartProductPage",
      "url": "https://www.walmart.com/ip/Super-Mario-Odyssey-Nintendo-Switch/56011600",
      "referenceUrl": "https://www.walmart.com/ip/Super-Mario-Odyssey-Nintendo-Switch/56011600",
      "delay": 5000
    }
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;label&lt;/code&gt;: name of your scenario&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;url&lt;/code&gt;: Test URL &lt;/li&gt;
&lt;li&gt;
&lt;code&gt;referenceUrl&lt;/code&gt;: baseline URL (since we don't have any development environment url, using same &lt;code&gt;url&lt;/code&gt; here for demo purpose.)&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;delay&lt;/code&gt;: It allows you to set a time to wait for the page to load before assuming it'll be ready to test.&lt;/li&gt;
&lt;/ul&gt;

&lt;h6&gt;
  
  
  Scenario 2: from page 1, do some action and go to page 2, BackstopJS takes screenshot for page 2
&lt;/h6&gt;

&lt;p&gt;In this scenario, we are moving from product page -&amp;gt; cart page by clicking on &lt;code&gt;add to cart&lt;/code&gt; button &lt;code&gt;button.prod-ProductCTA--primary&lt;/code&gt;. After landing on this cart page, we have to take screenshot of this page using backstopJS.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;  {
    "label": "walmartCartPage",
    "url": "https://www.walmart.com/ip/Super-Mario-Odyssey-Nintendo-Switch/56011600",
    "referenceUrl": "https://www.walmart.com/ip/Super-Mario-Odyssey-Nintendo-Switch/56011600",
    "readySelector": "button.prod-ProductCTA--primary",
    "clickSelector": "button.prod-ProductCTA--primary",
    "delay": 5000
  }
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;readySelector&lt;/code&gt; - Making Backstop to wait until a particular element (defined by CSS selector) is appearing before starting.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;clickSelector&lt;/code&gt; - Making Backstop to click a particular element (defined by CSS selector)&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Final &lt;code&gt;backstop.json&lt;/code&gt; file should look like this for the given scenario,&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;{
  "id": "sample_project",
  "viewports": [
    {
      "label": "Desktop Chrome",
      "width": 1280,
      "height": 800
    },
    {
      "label": "Mobile Chrome",
      "width": 375,
      "height": 812
    }
  ],
  "onBeforeScript": "puppet/onBefore.js",
  "onReadyScript": "puppet/onReady.js",
  "scenarios": [
    {
      "label": "walmartProductPage",
      "url": "https://www.walmart.com/ip/Super-Mario-Odyssey-Nintendo-Switch/56011600",
      "referenceUrl": "https://www.walmart.com/ip/Super-Mario-Odyssey-Nintendo-Switch/56011600",
      "delay": 5000
    },
    {
      "label": "walmartCartPage",
      "url": "https://www.walmart.com/ip/Super-Mario-Odyssey-Nintendo-Switch/56011600",
      "referenceUrl": "https://www.walmart.com/ip/Super-Mario-Odyssey-Nintendo-Switch/56011600",
      "readySelector": "button.prod-ProductCTA--primary",
      "clickSelector": "button.prod-ProductCTA--primary",
      "delay": 5000
    }
  ],
  "paths": {
    "bitmaps_reference": "backstop_data/bitmaps_reference",
    "bitmaps_test": "backstop_data/bitmaps_test",
    "engine_scripts": "backstop_data/engine_scripts",
    "html_report": "backstop_data/html_report",
    "json_report": "backstop_data/json_report",
    "ci_report": "backstop_data/ci_report"
  },
  "report": ["browser"],
  "engine": "puppeteer",
  "engineOptions": {
    "args": ["--no-sandbox"]
  },
  "asyncCaptureLimit": 5,
  "asyncCompareLimit": 50,
  "debug": false,
  "debugWindow": false
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;In this demo project other than &lt;code&gt;viewports&lt;/code&gt; &amp;amp; &lt;code&gt;scenarios&lt;/code&gt;, we don't really have to change anything else. Based on the requirement,  We can change the all the other variables if needed. For more details about &lt;code&gt;backstop.json&lt;/code&gt; properties, refer this &lt;a href="https://github.com/garris/BackstopJS#using-backstopjs" rel="noopener noreferrer"&gt;doc&lt;/a&gt;.&lt;/p&gt;

&lt;h6&gt;
  
  
  How to handle cookies / sessions in backstopJS&lt;a&gt;&lt;/a&gt;
&lt;/h6&gt;

&lt;p&gt;In some scenarios, we might are try to access urls without repeating any login actions. When an application url has a cookie dependencies, backstopJS provides us a way to import cookies through json files. In such scenarios, we have to add the following backstop property in &lt;code&gt;backstop.json&lt;/code&gt; file&lt;br&gt;
&lt;/p&gt;

&lt;p&gt;&lt;code&gt;"cookiePath": "backstop_data/engine_scripts/cookies.json"&lt;/code&gt;&lt;br&gt;
&lt;/p&gt;

&lt;p&gt;In backstopJS, it is possible to first run a Puppeteer script that logins into application portal, then save the session cookies in a file. Now a subsequent scenarios can read those json file to load  cookies and proceeds to do some action - all without having to log in again.&lt;/p&gt;

&lt;p&gt;To run a custom script, add this step &lt;code&gt;"onReadyScript": "puppet/getCookies.js"&lt;/code&gt; in &lt;code&gt;backstop.json&lt;/code&gt; file to execute custom puppeteer script to handle cookies/session dynamically. You can find all custom puppeteer scripts in this location &lt;code&gt;workingdir &amp;gt; backstop_data &amp;gt; engine_scripts &amp;gt; puppet&lt;/code&gt;.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;The following &lt;code&gt;getCookies.js&lt;/code&gt; script, grabs cookies by logging into the app and stores the cookies in mentioned &lt;code&gt;cookiePath&lt;/code&gt; location.&lt;br&gt;
&lt;/p&gt;
&lt;/blockquote&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;const fs = require('fs');
const  cookiePath = "backstop_data/engine_scripts/cookies.json";

module.exports = async (page, scenario, vp) =&amp;gt; {

    console.log('SCENARIO &amp;gt; ' + scenario.label);

    console.log("Closing cookie consent");
    await page.waitForSelector('button.form__button.form__button--green.cookie-button');
    await page.click('button.form__button.form__button--green.cookie-button');

   // Waits until the `email &amp;amp; password` meta element is rendered
    await page.waitForSelector('input[name="email"]');
    await page.waitForSelector('input[name="password"]');

    await page.type('input[name="email"]', 'userEmail@email.com',{delay: 5});
    await page.type('input[name="password"]', 'Test1234!',{delay: 5});

    console.log("Clicking Submit");
    await page.waitForSelector('button[type='login']');
    await page.click('button[type='login']');

    await page.waitForNavigation();

    const cookies = await page.cookies();

    console.log("The cookie is:", cookies);

    fs.writeFile(cookiePath, JSON.stringify(cookies, null, 2), function(err) {
        if (err) throw err;
        console.log('completed write of cookies');
    });
};
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  4. Create reference screenshots: &lt;code&gt;backstop reference&lt;/code&gt;&lt;a&gt;&lt;/a&gt;
&lt;/h3&gt;

&lt;p&gt;After saving the &lt;code&gt;backstop.json&lt;/code&gt; file, switch back to command line, and start with a executing Backstop reference command &lt;code&gt;backstop reference&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;This command is gonna generate the initial reference screenshots. It will generate screenshots for the different view ports for the given scenarios, and then once that's done, we can see the generated screenshot images in &lt;code&gt;workingDir &amp;gt; backstop_data &amp;gt; bitmaps_reference&lt;/code&gt;. &lt;/p&gt;

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

&lt;p&gt;If there's any failure, we could see the errors in command-line window. Modify the &lt;code&gt;backstop.json&lt;/code&gt; &amp;amp; rerun the reference command until getting a successful run.&lt;/p&gt;

&lt;h3&gt;
  
  
  5. Run tests: &lt;code&gt;backstop test&lt;/code&gt;&lt;a&gt;&lt;/a&gt;
&lt;/h3&gt;

&lt;p&gt;Once the reference images are generated successfully, we have to run this command &lt;code&gt;backstop test&lt;/code&gt; in command-line. Now backstopJS will generate screenshots of test application first and after that it will generate a report, by comparing them(reference vs test).&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fx39v7ummdn69uux6iutq.gif" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fx39v7ummdn69uux6iutq.gif" alt="Alt Text" width="500" height="312"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Lets just go through the report, &lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;We can take a look at the reference and see the screenshot and kind of see what it looks like.&lt;/li&gt;
&lt;li&gt;We can jump over to the test, see what that looks like.&lt;/li&gt;
&lt;li&gt;We can also look at the DIFF to see everything highlighted that's different between the two, and then we can actually go to the scrubber and we can see a comparison of the two versions and scrub them back and forth.&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  6. Backstop approve: &lt;code&gt;backstop approve&lt;/code&gt;&lt;a&gt;&lt;/a&gt;
&lt;/h3&gt;

&lt;p&gt;If the test we ran looks good, then go ahead and approve it. Approving changes will update your reference files with the results from your last test. Future tests are compared against your most recent approved test screenshots.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;What we are trying to kind of learn here is that we can run BackstopJS on our computer in a pretty easy &amp;amp; quick way to automate our most of manual visual regression tests. In this demo project, we have explored the very simple test scenario to understand the BackstopJS. In real time scenarios, we might have to use puppeteer JS scripts to handle a complicated scenarios.&lt;/p&gt;
&lt;/blockquote&gt;

</description>
      <category>testing</category>
      <category>beginners</category>
      <category>javascript</category>
      <category>backstopjs</category>
    </item>
  </channel>
</rss>
