Friday, May 1, 2020

Automated chromedriver management on windows agents

When it comes to Web testing, we always have to test on latest and latest-1 version of web browsers. While that is smooth in manual testing but it becomes challenging while running an automation suite on windows agents since with each browser version we might need to update browser drivers.
Today, there are many readymade code modules available which will handle this problem and you would have to send browser version as input but that demands an additional package installation and you might not want that. Recently we faced a similar issue when our tests were running on a pipeline with windows agent and we had to update chromedriver for our tests to run successfully. We did not want to hardcode driver or driver version and so we used a small PowerShell script and embedded that as a step in the pipeline code.

Now this script will always check for the version of chrome browser and will download the suitable chromedriver if it is not already present in the project directory. This script made it this entire process of getting the latest chromedriver automated without human intervention and thus saved lots of our time.

Here is the script and I hope it will be useful for people having windows agents.


$thisScriptRoot = "c:\agent\_work\1\s\"    #path of your workdirctory

$chromeDriverRelativeDir = "browsers"      #path where chromedriver needs to be copied
$chromeDriverDir = $(Join-Path $thisScriptRoot $chromeDriverRelativeDir)
$chromeDriverFileLocation = $(Join-Path $chromeDriverDir "chromedriver.exe")
$chromeVersion = [System.Diagnostics.FileVersionInfo]::GetVersionInfo("C:\Program Files (x86)\Google\Chrome\Application\chrome.exe").FileVersion
$chromeMajorVersion = $chromeVersion.split(".")[0]

if (-Not (Test-Path $chromeDriverDir -PathType Container)) {
  New-Item -ItemType directory -Path $chromeDriverDir
}

if (Test-Path $chromeDriverFileLocation -PathType Leaf) {
  # get version of curent chromedriver.exe
  $chromeDriverFileVersion = (& $chromeDriverFileLocation --version)
  $chromeDriverFileVersionHasMatch = $chromeDriverFileVersion -match "ChromeDriver (\d+\.\d+\.\d+(\.\d+)?)"
  $chromeDriverCurrentVersion = $matches[1]

  if (-Not $chromeDriverFileVersionHasMatch) {
    Exit
  }
}
else {
  # if chromedriver.exe not found, will download it
  $chromeDriverCurrentVersion = ''
}

if ($chromeMajorVersion -lt 73) {
  # for chrome versions < 73 will use chromedriver v2.46 (which supports chrome v71-73)
  $chromeDriverExpectedVersion = "2.46"
  $chromeDriverVersionUrl = "https://chromedriver.storage.googleapis.com/LATEST_RELEASE"
}
else {
  $chromeDriverExpectedVersion = $chromeVersion.split(".")[0..2] -join "."
  $chromeDriverVersionUrl = "https://chromedriver.storage.googleapis.com/LATEST_RELEASE_" + $chromeDriverExpectedVersion
}

$chromeDriverLatestVersion = Invoke-RestMethod -Uri $chromeDriverVersionUrl

Write-Output "chrome version:       $chromeVersion"
Write-Output "chromedriver version: $chromeDriverCurrentVersion"
Write-Output "chromedriver latest:  $chromeDriverLatestVersion"

# will update chromedriver.exe if MAJOR.MINOR.PATCH
$needUpdateChromeDriver = $chromeDriverCurrentVersion -ne $chromeDriverLatestVersion
if ($needUpdateChromeDriver) {
  $chromeDriverZipLink = "https://chromedriver.storage.googleapis.com/" + $chromeDriverLatestVersion + "/chromedriver_win32.zip"
  Write-Output "Will download $chromeDriverZipLink"

  $chromeDriverZipFileLocation = $(Join-Path $chromeDriverDir "chromedriver_win32.zip")

  Invoke-WebRequest -Uri $chromeDriverZipLink -OutFile $chromeDriverZipFileLocation
  Expand-Archive $chromeDriverZipFileLocation -DestinationPath $(Join-Path $thisScriptRoot $chromeDriverRelativeDir) -Force
  Remove-Item -Path $chromeDriverZipFileLocation -Force
  $chromeDriverFileVersion = (& $chromeDriverFileLocation --version)
  Write-Output "chromedriver updated to version $chromeDriverFileVersion"
}
else {
  Write-Output "chromedriver is actual"
}

Tuesday, December 3, 2019

Clean code practices with S.O.L.I.D. Principles

Writing code is the thing which most of software engineers do on daily basis but only few of them write clean code i.e. a code that is easy to understand, manage, scale and amend. Those people who write clean code are the most sought after developers and also the ones who has least number of production issues.

While there are many techniques of writing clean code but the most famous one is following S.O.L.I.D principles. It basically has 5 principles:
  • Single Responsibility Principle
  • Open/Closed Principle
  • Liskov Substitution Principle
  • Interface Segregation Principle
  • Dependency Inversion principle
We will try to learn more about these principles with the use of code examples from Python. Read through each of the below principles.

1. Single responsibility principle       “…You had one job” 
            — Loki to Skurge in Thor: Ragnarok

A class should have only one job.  If a class has more than one responsibility, it becomes coupled.  A change to one responsibility results to modification of the other responsibility.


2. Open/Closed principle
      Software entities (Classes, modules, functions) should be open for extension, not modification.


3. Liskov Substitution Principle
         A sub-class must be substitutable for its super-class.  The aim of this principle is to ascertain that a sub-class can assume the place of its super-class without errors.  If the code finds itself checking the type of class then, it must have violated this principle.


4. Interface Segregation Principle
         Make fine grained interfaces that are client specific clients should not be forced to depend upon interfaces that they do not use.  This principle deals with the disadvantages of implementing big interfaces.


5. Dependency Inversion Principle
         Dependency should be on abstractions not concretions
            A. High-level modules should not depend upon low-level modules. Both should depend upon abstractions.
            B. Abstractions should not depend on details. Details should depend upon abstractions.

There comes a point in software development where our app will be largely composed of modules.  When this happens, we have to clear things up by using dependency injection.  High-level components depending on low-level components to function.

Friday, August 17, 2018

getElements method in nightwatch js


Nightwatch js does not have getElements methods to locate multiple elements but it has provided with WebDriver methods specified in the WebDriver Specification. These methods can be used to create your own getElements method. Below is one such implementation

To achieve this, we have to set selector strategy which can be done by examining locator string. Below is my implementation

    /**
     * It will return the slector strategy for the given selector string
     */
    getSelectorStrategy: function (locator) {
        if (locator.startsWith("//")) {
            return "xpath";
        } else {
            return "css selector";
        }
    } 

Using above method, elements method can be implemented as below
const {client} = require('nightwatch-cucumber');

 /**
   * This will retrun array of elements for the given selectors
   */
   getElements: function (locator, callback) {
       var selectorStrategy = this.getSelectorStrategy(locator);
       return client.elements(selectorStrategy, locator, function (result) {
           callback(result.value)
       });
    }

Tuesday, July 24, 2018

Simple HTTP server on Node JS

Ever needed an HTTP server just to test out something. If you are working on Node platform then you can do so very easily by following the mentioned steps.


1. Simply paste below mentioned code in a file server.js
var connect = require('connect');
var serveStatic = require('serve-static');
connect().use(serveStatic(__dirname)).listen(8080, function(){
    console.log('Server running on 8080...');
}); 

2. Run this server.js via command prompt
node server.js

3. This will start a HTTP server on port 8080 and pickup the content from current directory

 This is quite useful when you want to see some static content using a server