It is a way to define the Jenkins workflow as code or in simple words instead of creating freestyle jobs and chaining them through post-build action. We can create the entire pipeline using code written in groovy
What is groovy?
Groovy is powerful, optionally typed and dynamic language to develop an application on Java Platform where its syntax is Java-like. Its typing discipline is strong, static and dynamic. The best things about Groovy are that since it extends JDK, it accepts Java code. Groovy can be used as both programming and scripting Language. Groovy is a superset of Java which means Java program will run in Groovy environment but vice-versa may or may not be possible. Whereas Java is strongly and statically typed programming language. Reference : geeksforgeeks.org
Difference between the declarative pipeline and scripted pipeline?
Though both these pipelines are based on the groovy DSL, the scripted pipeline uses stricter groovy based syntaxes because it was the first pipeline to be built on the groovy foundation. Since this Groovy script was not typically desirable to all the users, the declarative pipeline was introduced to offer a simpler and more optioned Groovy syntax. Declarative pipeline is a new introduction to Jenkins world.
Jenkins is built on java and provides methods to access the Jenkins objects from the execution JVM, one of such method is .getItemByFullName()
This will return the full object of the Item with the name provided
So if we need to get java object (which will have all attributes including result) of a job called “Test Job” . We can get it as jenkins.model.Jenkins.instance.getItemByFullName(“Test Job”)
Now from this object we have to find the job.getLastBuild().getResult().toString()
This will give the status of the last build of that particular Job
But how to run this is Declarative pipeline?
In the scripted pipeline, you can write the commands directly but in the declarative pipeline, you have to run strict groovy commands inside the script block
The result could be accessed from other stages by storing it as environment variable as env.variable and can be accessed inside the step section of any other stage as ${variable}
In VMware workstation click ‘File> Create VM’ and choose custom option.
Select ESXi 6.5 from dropdwon to ensure compatibility with previous versions.
Choose ‘ i will select OS later’ to avoid the situation of VMWare detecting incorrect OS version and doing wrong configurations, that might cause compatibility issues.
Note: you can choose installer image directly, and in most cases it works fine.
Select the settings as in the screenshots below, and leave the settings in it’s defaults if screenshots are not provided.
Choose different location and name if required, else leave it as default.
Select appropriate RAM size for your VM, more RAM size = Smooth system!!!
Select NAT or Bridged for internet connection , use Host only for private network between VM and host.
Suggested: We need to install packages and hence requires internet connectivity. For this purpose, choose NAT.
Note: Bridge network will behave as a new device connected to the real network and so is not recommended in protected networks. Read more at : Here
Leave all other setting as default and click finish.
Go to VM setting and select the downloaded ISO file and reboot the system.
select install centos 7.
Press Enter.
Select Keyboard and other settings.
Click on ‘installation Destination’ (Or where ever such a mark is there ).
Don’t need to do anything, just click Done button on the top left corner.
Start installation.
Set root password and create password by clicking the respective icons.
login as root.
Installing GUI. Let’s startttttttttttt……………..
Set up network:
Open network management UI through below command:
nmtui
Note: use your arrow keys to select different buttons and options.
Go to Edit connection and choose your network, select ‘automatically connect’:
This will ensure that network connection is enabled automatically with every reboot
Click OK and choose ‘activate a connection’. Select the appropriate connection and click activate (if status shows as deactivate then leave it as it is ).
Give the below command to ensure proper dhcp IP (not mandatory, do only if system didn’t got an IP assigned):
sudo dhclient
Install GNOME Desktop UI:
First update all packages in the system to avoid conflicts:
sudo yum update
Give the below command to install GNOME Desktop UI theme.
Here what we actually does is that , we sets runlevel 5 (graphical) to default runlevel.
So on each reboot , system starts up using default runlevel 5 which is the graphical runlevel
The below command shows the available runlevels
ls –al /lib/systemd/system/runlevel*
Note: Just think of runlevels as instructions used by linux to know what all services or capabilities to be loaded during boot-up ( something like secure,full and minimum start up boot in windows) . RunLevel 5 have scripts running for GUI.
The right way is to use custom formatter (Plugins)
So what does this means ?
We are going to create our own report plugin using Extent report.
As in TestNG reporting, where we override the TestNG listeners to tell TestNG what to do for each action or events,
Here, we implements Cucumber listener class to listen to cucumber events and tell it what to do for each event.
Cucumber sends events for each action like scenario started, step started, step finished etc to this instance of the listener. So here we handle these events and tell cucumber what to do with these events (In our case we will tell it to log it to extent report).
There are two listener interfaces:
EventListener
ConcurrentEventListener
So as the name suggests, concurrentEventListener is thread-safe and the other is not. The implementation is exactly the same so we will use only the concurrentEventListner so that our framework is threadsafe when we have to run things in parallel.
The inLine comments will explain most things, but just to iterate.
Here the report will be saved in the current directory as ExtentReportResults.html
Plugin doesn’t allow any input (Thats why empty constructor) . so in runner file , this (plugin = {“cucumberHooks.customReportListener:”}, is wrong and this (plugin = {“cucumberHooks.customReportListener”}, is correct ( means there should be no colon :)
If you want to pass argument you can make the constructor accept the argument and pass it in plugin as (plugin = {“cucumberHooks.customReportListener:<argument>”},
The TestStep event and Hook events implements PickleStepTestStep and HookTestStep ,
These interfaces have additional methods that allows us to access step names type and keywords. Too access these steps we need to widden the object by casting it to PickleStepTestStep and HookTestStep ,
We have seen how to create reports in our previous article, but that report used to break when we have test case names with a special character or long names.
So I came across a better report and let’s see how to implement it.
Note: Drawback of this report is that you cannot send it as an attachment as it is dependent on many files. It won’t work as standalone,
So you can use both this report and the old report together and use this one for debugging and the other one as a summary to send along with emails.
This article helps you in understanding how to start chrome driver on a specific port, connect to an existing browser session and opening chrome browser with debug port opened through selenium scripts.
You should use ChromeDriverService for starting chrome in a different port:
it('Validate that error message in all fields given uploaded {Regression} {Smoke} {Sanity}', async function () { await stage1.goto() await stage1.sendValue('hi') await browser.sleep(5000) });
Note:
This works even if the page object is defined as a function
For protractor-cucumber add the same in before hook :
you should add the hook in the step definition file itself and not in separate hook.js:
"use strict";
let {Given,Before} = require('cucumber'); let decache = require('decache'); let stage1 = require('../pageobjects/stage1.js');
Before(async function (scenario) { decache('../pageobjects/stage1.js'); stage1 = require('../pageobjects/stage1.js'); await browser.sleep(4000)
});
Given('I navigates to google', async() => { await stage1.goto() });
let parser = require('csv-parser-sync-plus-promise')
Use as sync:
let a=parser.readCsvSync('')
Use as Promise:
let b=parser.readCsvPromise('') it('test {Regression} {Sanity} {Sanity}', async function () { console.log(await b); });
Protractor test:
Use the demo csv ‘1.csv’
'use strict'; let parser = require('csv-parser-sync-plus-promise') let testdata=parser.readCsvSync('<full_path>/1.csv'); describe('Validate dfsfdsf 1 behaviour', function () {
for(let i of testdata){ it('test {Regression} {Sanity} {Sanity}', async function () { console.log(i.a); console.log(i.b); console.log(i.c); expect(Number(i.a)+Number(i.b)).toBe(Number(i.c)) }); } });
cucumber :npm install cucumber (If protractor was installed locally else use npm install -g cucumber). Both protractor and cucumber should be in same scope.
//set cucumber options cucumberOpts: { require: ['./testsuites/*.js','./commons/chaiAssertions.js','./commons/hooks.js'], strict: true, format: [], //don't put 'Pretty' as it is depreciated 'dry-run': false, compiler: [], format: 'json:results.json', //make sure you are not using multi-capabilities }, SELENIUM_PROMISE_MANAGER: false, };
Here, i point to the feature file using the property specs: [‘feature/*.feature’],
and glues it to the step definition using cucumberopts> require:
There is no one to one mapping between feature and step definition, the framework automatically finds the step definition that contains the definition for the step from provided step definitions(.js files) in the require field.
Now write feature file:
test.feature
Feature: Google search Scenario Outline: Log in with given API Given I navigates to google And searches for ' ' Then I should see '' Examples: |input|this| |test|pass| |test2|fail|
Now write step definition:
step.js:
var { Given } = require('cucumber'); Given('I navigates to google', async function () { await browser.get('https://www.google.com/'); }); Given('searches for {string}', async function (searchValue) { await element(by.css('input[role="combobox"]')).sendKeys(searchValue) }); Given('I should see {string}', async function (expectedValue) { expect(expectedValue).to.equal('pass') });
so here we are using just Given as during runtime Given ,when then etc will be ignored and only the string after that will be considered
So, even if our feature file has And searches for ‘input’ , we can write step definition as Given(‘searches for {string}’.
Note that we are not using regular expressions to get parameters but the data type.
you might have seen in other tutorials , Given( /^searches for (\w+)$/ ). Its simpler to use the format i have used Given(‘searches for {string}’. Both the approaches works works fine.
Click inspect and click F8 and click run. The exection stops at ‘debugger’ line and now add manual break points ow goto the file using the tabs and add manual break points.