Using momentjs-timezone in Postman

External libraries can be executed in postman if CDN equivalent of these libraries are available.

You can get CDN in equivalent from websites like: https://cdnjs.com/

Now to use momentTZ in postman, copy the below code into script section and execute it once and then comment out:

This will store the CDN equivalent of momentTZ and momentJS libraries to environment variables.

pm.sendRequest("https://cdnjs.cloudflare.com/ajax/libs/moment-timezone/0.5.33/moment-timezone-with-data-2012-2022.min.js", (mtzErr, mtzRes) => {
pm.sendRequest("https://cdnjs.cloudflare.com/ajax/libs/moment.js/2.29.1/moment.min.js", (mjsErr, mjsRes) => {
//you have to initialize the momentjs to make momenttz work so we are storing both into a variable
pm.environment.set("momentjs", mjsRes.text());
pm.environment.set("momenttz", mtzRes.text());
})
})

Now you initiate the libraries in either of the two approaches:

First Approach:

Now comment out the above code you don’t need it anymore. Now you can use momenttz anywhere in your collection’s script sections as :

eval(pm.environment.get("momentjs"));
eval(pm.environment.get("momenttz"));
console.log(this.moment.utc().tz("America/Los_Angeles", "UTC").format())

in this approach, the libraries are initialized in the local scope, so if you want to use them again then you have to run the 3 lines of code again in other script sections whenever it’s used.

Second approach:

new Function triggers function in the global scope. so you just have to execute the first two lines only ones in your collection run and it will be available globally throughout:

Add in first scripts pre-request script:

(new Function(pm.environment.get("momentjs")))();
(new Function(pm.environment.get("momenttz")))();

Now you can use momentTZ library anywhere as :

console.log(moment.utc().tz("America/Los_Angeles", "UTC").format())

😀 😬 😁 😂 😃 😄 😅

Auto Detect Chrome version and auto-download compatible chrome version !!!

Introduction:

Thanks to @webdriverio , i came to know about the chromedriver npm package. It’s a cool library that allows you to detect the installed chrome version and download the compatible chromedriver version automatically

https://www.npmjs.com/package/chromedriver

Pre-requisite:

install nodejs >v 10 : install

you can also download just the binary file instead of installing nodejs

now just run the below command

<oath_to_npm>/npm install -g chromedriver --detect_chromedriver_version --scripts-prepend-node-path

you can see the supported command-line flags at :

https://www.npmjs.com/package/chromedriver

Note: — scripts-prepend-node-path should be last argument, this flag is mandatory only if you are using node binaries than installation

if you want to download the binary to a specific folder then use the below repo instead:

I have added features to set target directory and download driver when used as a library. I have created a pull request for these but till that gets merged use the below repo instead

https://www.npmjs.com/package/chromedriver

download the zip or clone the files, unzip the content and then use the below command

C:\Users\test\Downloads\node-v14.17.0-win-x64\npm install -g "C:\Users\test\Downloads\chromedriver-main\chromedriver-main" --chromedriver_download_to_target_folder="C:\Users\test\Downloads\chromedriver-main\output" --scripts-prepend-node-path

Note: — scripts-prepend-node-path should be given as the last argument if you are running nodejs binary zip directly instead of installing nodejs

This will add node to PATH automatically for the scripts to detect

Now let see how we can run this library from nodejs , Note I have given information about alternative simple native libraries that does the same :

Running from Java:

You can run this nodejs library from java as:

package test_suites;
import java.io.IOException;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
public class Testcase1 {
public static void main(String[] args) throws IOException, InterruptedException {
// just donwload the nodejs binary zip file and extract it
//point to the npm.cmd inside the extracted folder
//if you already have nodejs installed then point to the npm.cmd of the installed library
ProcessBuilder pb = new ProcessBuilder("C:\\Users\\test\\Downloads\\node-v14.17.0-win-x64\\npm.cmd",
"install",
"-g",
"C:\\Users\\test\\Downloads\\chromedriver-main\\chromedriver-main",
"--detect_chromedriver_version",
//  "--chromedriver-force-download",  // uncomment to force chromedriver dwonload even if it exists 
// "--chromedriver_version=88.0.4324.96", //comment detect version and hard code the version
// "--chromedriver_download_to_target_folder=\"C:\\Users\\test\\Downloads\\temp_2\\github_chromedriver\\output\"" // available only for praveendvd/chromedriver repo
"--scripts-prepend-node-path"); //"--scripts-prepend-node-path" should be always the last argument
Process p = null;
String result = null;
String error = null;
p = pb.start();
System.out.println("Started download ###############################");
//wait for download to complete
p.waitFor();
System.out.println("Output  ###############################");
System.out.println("Output stream ###############################");
result = new String(p.getInputStream().readAllBytes());
System.out.println(result);
System.out.println("Error stream ###############################");
error = new String(p.getErrorStream().readAllBytes());
System.out.println(error);
System.out.println("Output end ###############################");
Pattern pattern = Pattern.compile("ChromeDriver binary available at(.*?)\n", Pattern.DOTALL);
Matcher matcher = pattern.matcher(result);
matcher.find();
String chromedriverpath = matcher.group(1).trim();
System.out.println("****The driver is copied to**** : " + chromedriverpath  );
System.out.println("Completed download ###############################");
}
}

Output:

Or use :

GitHub — bonigarcia/webdrivermanager: Automated driver management for Selenium WebDriver

Mvn:

<dependency>
<groupId>io.github.bonigarcia</groupId>
<artifactId>webdrivermanager</artifactId>
<version>4.4.3</version>
</dependency>

Code:

import io.github.bonigarcia.wdm.WebDriverManager;
import io.github.bonigarcia.wdm.config.DriverManagerType;
public class Testcase1 {
public static void main(String[] args) throws IOException, InterruptedException {
WebDriverManager.getInstance(DriverManagerType.CHROME).forceDownload();
WebDriverManager.chromedriver().setup();
System.out.println(WebDriverManager.getInstance(DriverManagerType.CHROME).getDownloadedDriverPath());
}
}

Running from Python:

import subprocess
import sys
import re
command = subprocess.run(
[
"C:\\Users\\Downloads\\node-v14.17.0-win-x64\\npm.cmd",
"install",
"-g",
"C:\\Users\\Downloads\\chromedriver-main\\chromedriver-main",
"--detect_chromedriver_version",
# "--chromedriver-force-download",  # uncomment to force chromedriver dwonload even if it exists
# "--chromedriver_version=88.0.4324.96", #comment detect version and hard code the version
# "--chromedriver_download_to_target_folder=\"C:\\Downloads\\temp_2\\github_chromedriver\\output\"" # available only for praveendvd/chromedriver repo
"--scripts-prepend-node-path",
],
capture_output=True,
)
# --scripts-prepend-node-path" should be always the last argument'''
print("Started download ###############################")
print("Output  ###############################")
print("Output stream ###############################")
sys.stdout.buffer.write(command.stdout)
print("Error stream ###############################")
sys.stderr.buffer.write(command.stderr)
print("Output end ###############################")
output = command.stdout
result = re.search("ChromeDriver binary available at(.*?)\n", output.decode('utf-8') )
chromedriverpath = result.group(1)
print("****The driver is copied to**** : " + chromedriverpath  )
print(("Completed download ###############################"))
sys.exit(command.returncode)

Output:

Or use:

library: chromedriver-autoinstaller · PyPI

pip install chromedriver-autoinstaller

code:

import chromedriver_autoinstaller
# Check if the current version of chromedriver exists
# and if it doesn’t exist, download it automatically,
# then add chromedriver to path
driver = webdriver.Chrome(chromedriver_autoinstaller.install())

Running from Nodejs:

Clone or download and extract :

https://github.com/praveendvd/chromedriver

Now install the library to your npm project as :

C:\Users\test\Downloads\node-v14.17.0-win-x64\npm install "C:\Users\test\Downloads\chromedriver-main\chromedriver-main" --chromedriver_download_to_target_folder="C:\Users\test\Downloads\chromedriver-main\output" --scripts-prepend-node-path

Now you can use this library to autodownload compatible chrome driver version as :

var chromedriver =  require('chromedriver');
process.env.detect_chromedriver_version = true
process.env.chromedriver_force_download = true
process.env.chromedriver_download_to_target_folder="C:/Users/output"
chromedriver.download() //returns a promise

Using external libraries in postman and there by using custom helper in Postman visualization!

Postman allows you to use CDN libraries in the script session,

so here we are using the CDN equivalent of the handlebar and then passing the compiled template to the postman visualizer

**We can initialize CDN in two ways :**

**First Approach:**

By initializing the CDN in global space using “new Function(cdn)()”

You can see the code below , but this will mess up global scope and can be used only once in the entire run*

// you can call any package with min build in postman
pm.sendRequest("https://cdnjs.cloudflare.com/ajax/libs/handlebars.js/4.7.7/handlebars.js", (err, res) => {
pm.environment.set("res", res.text());
// intialize the min.js
new Function(pm.environment.get("res"))();
//create template soource
var source =
`{{#ifCond v1 v2}}
{{v1}} is equal to {{v2}}
{{else}}
{{v1}} is not equal to {{v2}}
{{/ifCond}}`;
//you can call methods in the cdn file using this keyword
//here we are registring handlebar
Handlebars.registerHelper('ifCond', function (v1, v2, options) {
if (v1 === v2) {
return options.fn(this);
}
return options.inverse(this);
});
//compile the template
var template = Handlebars.compile(source);
//data to be passed 
// try v1 and v2 with same and different values and observe the output in visualization tab
var data = {
"v1": "test",
"v2": "test",
};
// passing the data to template
var result = template(data);
//visualizing it 
pm.visualizer.set(result)
})

**Second Approach:**

you can call any package with min build in postman

this initializes the functions in local scope so it won’t mess up the global space and cause issues

pm.sendRequest("https://cdnjs.cloudflare.com/ajax/libs/handlebars.js/4.7.7/handlebars.min.js", (err, res) => {
pm.environment.set("res", res.text());
// intialize the min.js
eval(pm.environment.get("res"));
//create template soource
var source =
`{{#ifCond v1 v2}}
{{v1}} is equal to {{v2}}
{{else}}
{{v1}} is not equal to {{v2}}
{{/ifCond}}`;
//you can call methods in the cdn file using this keyword
//here we are registring handlebar
this.Handlebars.registerHelper('ifCond', function (v1, v2, options) {
if (v1 === v2) {
return options.fn(this);
}
return options.inverse(this);
});
//compile the template
var template = this.Handlebars.compile(source);
//data to be passed
// try v1 and v2 with same and different values and observe the output in visualization tab
var data = {
"v1": "test",
"v2": "test",
};
// passing the data to template
var result = template(data);
//visualizing it
pm.visualizer.set(result)
})

Creating Json Object From string in Java (Rest-assured):

https://mvnrepository.co m/artifact/org.json/json/2 0210307

get org.json from maven

add below dependency to your maven:

<!--
https://mvnrepository.com/artifact/org.json/js
on -->
<dependency>
<groupId>org.json</groupId>
<artifactId>json</artifactId>
<version>20210307</version>
</dependency>

Now import these:

//import these 
import org.json.JSONException;
import org.json.JSONObject;
import org.json.JSONArray;

Now you can convert a json string to json object just like that:

//now you can convert string to array and
object without having complicated maps
and objects
try {
JSONArray jsonArray = new
JSONArray("[1,2,3,4,5]");
//you can give entire jsonObject here
JSONObject jsonObject = new
JSONObject("{\"name\": [\"praveen\"]}");
System.out.println("outputarray: " +
jsonArray.toString(2));
System.out.println("outputObject: " +
jsonObject.toString(2));
} catch (JSONException err) {
System.out.println("Error: " +
err.toString());
}

Sending request as

Response response = RestAssured.given() .header("Content-type", "application/json") .and() .body("{\r\n" + " \"name\": \"praveen\",\r\n" + " \"job\": \"leader\"\r\n" + "}") .when() .post("https://reqres.in/api/users") .then() .extract().response(); System.out.println(response.prettyPrint());

The hidden gem: Postman API and Documentation feature

its easier than you would expect to create a project collection…

Postman API Feature:

you can create collection , mock servers, documentation in click of a button by using postman API definition feature.

Lets see how to do it!!

  1. Get the API definition:

postman supports following API definitions:

so what is API definitions ?

API definitions, or API specifications or description formats (all are the same) defines your API . It is like a live documentation on what is available, what it can do , what is supported etc . In short a comprehensive live documentation that can help consumers understand what your API can do.

in summary:

  • Help internal folks understand the API and agree on its attributes
  • Help external folks understand the API and what it can do
  • Act as live documentation on what all endpoints are available, what methods are supported, what attributes are expected etc.

https://swagger.io/blog/api-development/why-you-should-create-an-api-definition/

How to get API definition ?

Most APIs visualizes the API definition using swagger UI. Which creates a user interactable representation of your API from the API definition file.

In mostcases the swagger UI will have link to the definition file or you can goto network tab and search for it

lets take https://petstore.swagger.io/ as example.

you can see the link to the definition file, it can be in yaml or json format. just click the link and copy the content.

you can see the version is swagger 2.0 meaning its open api v2

2. Create API in Postman

click API section and click the add icon or create new API link

Now give the below details:

name,version , open api v2.0 and json , and click create

Now paste the swagger definition we copied from pet store:

you can press ctrl+b to format the json properly after pasting:

click save

3. Now Generate collection from this swagger definition:

click generate collection:

click show advanced settings:

Give the below settings:

Now click Generate collection and goto your collections once the collection generation completes

4. Mock server , documentation and other things

so the generated collection will have documentation and examples as per the API definition file provided so you don’t have to generate documentation separately!!

And for mockserver, you can directly create a mock server from the collection as the examples are already available.

5. Publishing your documentation and adding animation to your documentation

Click the collection and click view documentation , and click publish

Now you have the documentation available in public domain for your customers.

https://swagger.io/blog/api-development/why-you-should-create-an-api-definition/

To unpublish follow the same procedure and click edit documentation and click unpublish

Adding animation:

you can add gif to your documentation , to do that use :

copy the absolute url of your gif:

provide it in documentation as :

![](https://i.pinimg.com/originals/f0/6a/9b/f06a9b257ba15f4761308ff84ab1ba2b.gif)

so when you click outside it will be resolved as

Appium: Running tests on a device connected to a remote system (uiautomator2)

Architecture:

Credit Github: https://github.com/appium/appium/issues/10503
Credit Github: https://github.com/appium/appium/issues/10503

Here we are running scripts from a remote/local Appium server while the devices are connected to another system in the network.

Setup:

  1. Start adb server in the system in which devices are connected:

Just run the below command in cmd (given adb is available in path)

adb kill-server
adb -a -P 5037 server nodaemon

Appium capabilities:

Now to run appium tests we need to add the below capability:

Try appium inspector from the remote system with below capability (edit all other fields according to your setup except remoteAdbHost,systemport and adbPort)

here remoteadbhost is the remote system which has connected devices, system port is the uiautomator2 server port which is 8021 by default and finally adbport

This will allow you to inspect the remote devices from your local system

{
“platformName”: “Android”,
“appium:platformVersion”: “11”,
“appium:deviceName”: “sr”,
“appium:app”: “C:\\Users\\Downloads\\ApiDemos-debug.apk”,
“appium:automationName”: “UiAutomator2”,
"remoteAdbHost":"192:168.0.7",
"systemPort":8021,
"adbPort":5037
}

Note: when using webdriverio 7 add appium: infront of all the capabilities as its the new syntax for w3c.

Manually testing the remote connectivity:

1. From remote machine run:

Note: Change “192.168.0.7” to the ip address of the system in which devices are connected

on running this you can see the remote devices

adb -L tcp:192.168.0.7:5037 devices

Now you are ready to run the tests:

2. if you run

adb devices

This gives the devices connected local to the system

so to change that you can set the variable ADB_SERVER_SOCKET, to set this from use:

Note: change ip to system in which devices are connected

set ADB_SERVER_SOCKET=192.168.0.7

Now adb devices command gives the remote device details

4.To test if the adb is working remotely run the below command, you will see that call view is opened in the remote device:

just run the below command in cmd of the remote machine

adb shell am start -a android.intent.action.VIEW

Postman trick and tips: How to run a specific iteration data from Newman

There is no inbuilt way to specify a specific iteration row to be executed using Newman, but can be done using Powershell or by using Newman as a library.

The approaches are as below

Powershell:

here we read the actual csv file in the current directory using import csv

Then considers only row 1..2 , if you just want 1 row you can change $a[1..2] to $[1]

$a= Import-Csv .\a.csv
$a[1..2] | Select-Object * | export-csv -Path .\temp.csv -NoTypeInformation
newman run .\test.postman_collection.json -d .\temp.csv

As library:

here we read and split the csv as we want using csv parser and then write it back to a temp file as csv using csv-stringify library.

First, install:

npm install csv
npm install fs
npm i fs-extra
npm install newman

Then use the below code

const newman = require('newman'); // require newman in your project
const stringify = require('csv-stringify')
const parse = require('csv-parse/lib/sync')
const fs = require("fs")
// call newman.run to pass `options` object and wait for callback
let data = fs.readFileSync('./a.csv',
{ encoding: 'utf8', flag: 'r' });
data = parse(data, {
columns: true,
    skip_empty_lines: true
})
console.log(data)
//index doesn't consider header so 0 is first data row
stringify(data.slice(0, 2), {
header: true
}, function (err, output) {

    fs.writeFileSync("temp.csv", output);

    newman.run({
collection: require('./test.postman_collection.json'),
reporters: 'cli',
iterationData: "./temp.csv"
}, function (err) {
if (err) { throw err; }
console.log('collection run complete!');
});
})

How to delete Jenkins jobs using remote access API

This is why proper API documentation and proper status code is important 😀😀😀

Spend half an hour trying to figure out how to delete a #Jenkins #job using “Jenkins remote access API” 😩 😤 😮 😱

You can access all the methods support for an endpoint say for the below job:

https://<Jenkins_url>/job/<job_name>

at : 👇

https://<Jenkins_url>/job/<job_name>/api

The documentation at this endpoint says, to programmatically delete a job, just do DELETE to “This URL”, which on clicking takes us to: 😖 😫 😩👇

https://<Jenkins_url>/job/<job_name>/

so I tried #DELETE method on : 😑 😒 👇

https://<Jenkins_url>/job/<job_name>

And guess what we get status #200 but the job never gets deleted after playing around for half an hour by reading many answers online and inspecting the #network tab when you do delete from UI. I identified the below endpoint to do this: 📌 📍 📌 📍 🚩

https://<Jenkins_url>/job/<job_name>/doDelete

But was still wondering why the documentation was wrong !!! so gave a last try of adding the front slash as per the URL opened up in the browser: 🙏🙏

DELETE https://<Jenkins_url>/job/<job_name>/

Guess what !!!!! 💰 💰 I got a status code #204 and JOB got deleted !!!! 😂 😃 😄 😅

so note: front slash is important if you are playing with Jenkins remote access API 😂 😃 😄 😅

#jenkins #devops #qaengineers #api

What is the difference between BDD and Keyword driven automation framework?

Courtesy: https://app.animaker.com/dashboard#_=_

First understand BDD:

BDD is not just about testing it is a type of development practice, that avoids the difficulty of providing a definition of “Done”.

https://dannorth.net/introducing-bdd/ the author of BDD style states that:

“Behaviour” is a more useful word than “test”

Now I had a tool — agiledox — to remove the word “test” and a template for each test method name. It suddenly occurred to me that people’s misunderstandings about TDD almost always came back to the word “test”.

That’s not to say that testing isn’t intrinsic to TDD — the resulting set of methods is an effective way of ensuring your code works. However, if the methods do not comprehensively describe the behaviour of your system, then they are lulling you into a false sense of security.

I started using the word “behaviour” in place of “test” in my dealings with TDD and found that not only did it seem to fit but also that a whole category of coaching questions magically dissolved. I now had answers to some of those TDD questions. What to call your test is easy — it’s a sentence describing the next behaviour in which you are interested. How much to test becomes moot — you can only describe so much behaviour in a single sentence. When a test fails, simply work through the process described above — either you introduced a bug, the behaviour moved, or the test is no longer relevant.

I found the shift from thinking in tests to thinking in behaviour so profound that I started to refer to TDD as BDD, or behaviour- driven development.

It shows that focusing on the behavior of the system than on functionality we get more clear understanding on:

  1. What to implement
  2. What to test
  3. What to consider as acceptance criteria for marking a story “done
  4. Where to start: keep story as the starting point , it defines what functionality and what behavior the system should have. It avoids the cases of having misunderstanding about the system and ensures there is no “absence of error fallacy” (absence of error fallacy mean there is no errors but the system itself is not usable as it was not build the way it was required to be )
  5. What changes to make and what are its effect on the system behavior
  6. Provides more traceability and visibility of system functionality and features
  7. More test coverage traceability as you know what all scenarios or behaviors to be covered

Difference between BDD and KDT

BDD:

https://dannorth.net/whats-in-a-story/

Behaviour-driven development uses a story as the basic unit of
 functionality, and therefore of delivery. The acceptance criteria are
 an intrinsic part of the story — in effect they define the scope of
 its behaviour, and give us a shared definition of “done”. They are
 also used as the basis for estimation when we come to do our planning.
 
 Most importantly, the stories are the result of conversations between
 the project stakeholders, business analysts, testers and developers.
 BDD is as much about the interactions between the various people in
 the project as it is about the outputs of the development process.

KDT

Keyword driven testing is a way of achieving the BDD approach in testing , you can define the behavior of a system using **keyword**.

you can also do this using **Gherkin** as in cucumber

Framework like **Robotframework** supports both keyword abstraction and use of Gherkin(Given,when,then)

Summary:

BDD is a development practice where user stories are defined by defining the expected behavior of the system towards a specific action. It allows more clarity on what to develop, test and deliver.

and KDT is the approach of implementing BDD in test framework so that we get test coverage and one to one traceability with our test scripts and user stories

Gherkin is another syntactical way of defining BDD in test,

so you can use both Gherkin, Keyword driven , or simple page object method abstractions that defines behavior than functionality in your BDD test framework.

example:

As a [X]
I want [Y]
so that [Z]

where Y is some feature, Z is the benefit or value of the feature, and X is the person (or role) who will benefit. Its strength is that it forces you to identify the value of delivering a story when you first define it.

Another form of explaining behavior would be :

Given some initial context (the givens),
When an event occurs,
then ensure some outcomes.

in both forms there is an action by the actor/user and an expected behavior from the system.

Important:

Unless the keywords or Gherkin or test methods defines the behavior of the system , non of Gherkin , Keyword driven or simple test methods cannot be considered as Behavior driven, they become just Gherkin , KDT or plain low test methods.

so not all keywords or Gherkin or test methods are BDD compliant

Data driven testing per request without using data file

Create a environment variable called “csv” and copy the below content and paste it as value:

username,password,error_message,error_code
username1,password1,errormessage1,errorcode1
username1,password1,errormessage1,errorcode1

Now in pre-request add :

//we use variables as it will get destroyed after the run . So if index returns null then it means it’s the first iteration
if (!pm.variables.get("index")) {
    const parse = require('csv-parse/lib/sync')
//Environmental variable where we copy-pasted the csv content
const input = pm.environment.get("csv");
const records = parse(input, {
columns: true,
skip_empty_lines: true
})
    pm.variables.set("index", 0)
pm.variables.set("records", records)
}
records = pm.variables.get("records")
index = pm.variables.get("index")
//we are setting variable 
if (index !== records.length) {
for (let i of Object.entries(records[index])) {
pm.variables.set(i[0], i[1])
}
pm.variables.set("index", ++index)
pm.variables.get("index")===records.length?null:postman.setNextRequest(pm.info.requestName)
}

Now you can run data driven for that one particular request:

Note that here we used pm.variables. this is because it creates local variable that get destroyed after the collection run completes .

So for each run , first value will be null and then will be set to 0

Eg collection:

https://www.postman.com/collections/eb144d613b7becb22482

use the same data as environment variable content , now run the collection using collection runner or newman.

You can import this collection by clicking file >import >and then choosing links

Output: