Combining multiple collections in postman - postman-collection-runner

I am trying to run multiple postman collections at one run in jenkins
Below is my scenario
collection1.json with data1.csv and environment variable as environment1.json
collection1.json with data1.csv and environment variable as environment1.json
I want to run the above to collection in one go
-Please let me know how to combine 2 collections and run at once?
-Is there a way to download each collection in postman at once rather than doing 1 by 1?
-Example of using postman-collection-combine

I know that Postman collection runner has some limitations, but I was able to achieve it using this article
This is the script that lets you run multiple Postman collections in parallel:
const path = require('path')
const async = require('async')
const newman = require('newman')
const PARALLEL_RUN_COUNT = 2
const parametersForTestRun = {
collection: path.join(__dirname, 'postman/postman_collection.json'), // your collection
environment: path.join(__dirname, 'postman/localhost.postman_environment.json'), //your env
reporters: 'cli'
};
parallelCollectionRun = function (done) {
newman.run(parametersForTestRun, done);
};
let commands = []
for (let index = 0; index < PARALLEL_RUN_COUNT; index++) {
commands.push(parallelCollectionRun);
}
// Runs the Postman sample collection thrice, in parallel.
async.parallel(
commands,
(err, results) => {
err && console.error(err);
results.forEach(function (result) {
var failures = result.run.failures;
console.info(failures.length ? JSON.stringify(failures.failures, null, 2) :
`${result.collection.name} ran successfully.`);
});
});

Related

Pre-scan web page for dynamic tests

Looking for a definitive answer to the question posed by #JeffTanner here about generating dynamic tests. From that question and the Cypress samples, it's clear that we need to know the number of tests required before generating them.
Problem
We have a web page containing a table of Healthcare analytic data that is refreshed many times during the day. Each refresh the team must check the data, and to divvy up the work we run each row as a separate test. But the number of rows varies every time which means I must count the rows and update the system on each run. Looking for a way to programmatically get the row count.
The HTML is a table of <tbody><tr></tr></tbody>, so the following is enough to get the count but I can't run it in a beforeEach(), the error thrown is "No tests found"
let rowCount;
beforeEach(() => {
cy.visit('/analytics')
cy.get('tbody tr').then($els => rowCount = $els.length)
})
Cypress._.times(rowCount => {
it('process row', () => {
...
})
})
The before:run event fires before the tests start, you can scan the web page there.
Set the event listener in setupNodeEvents(). Cypress commands won't run here, but you can use equivalent Node commands.
const { defineConfig } = require("cypress");
module.exports = defineConfig({
e2e: {
setupNodeEvents(on, config) {
on('before:run', async (details) => {
try {
const fetch = require('node-fetch');
const fs = require('fs-extra');
const jsdom = require("jsdom");
const { JSDOM } = jsdom;
const response = await fetch(config.env.prescan); // default below
const body = await response.text(); // or pass in command line
const dom = new JSDOM(body);
const rows = dom.window.document.body.querySelectorAll('tr') // query
// save results
fs.writeJson('./cypress/fixtures/analytics-rows.json', {rows:rows.length})
} catch (error) {
console.log('error:', error)
}
})
},
},
env: {
prefetch: 'url-for-analytics-page'
}
})
Test
import {rows} from './cypress/fixtures/analytics-rows.json' // read row count
Cypress._.times(rows, (row) => {
it(`tests row ${row}`, () => {
...
})
}
You can add a script scan-for-rows.js to the project scripts folder, like this
const rp = require('request-promise');
const $ = require('cheerio');
const fs = require('fs-extra');
rp('my-url')
.then(function(html) {
const rowCount = $('big > a', html).length
fs.writeJson('row-count.json', {rowCount})
})
.catch(function(err){
//handle error
});
Then in package.json call a pre-test script every time a new version of the web page appears.
One possibility is to run the above Cypress test in a pretest script which will always run before your main test script.
// package.json
{
...
"scripts": {
"pretest": "npx cypress run --spec cypress/e2e/pre-scan.cy.js",
"test": "npx cypress run --spec cypress/e2e/main-test.cy.js",
}
}
// pre-scan.cy.js
it('scans for table row count', () => {
cy.visit('/analytics');
cy.get('tbody tr').then($els => {
const rowCount = $els.length;
cy.writeFile('cypress/fixtures/rowcount.json', rowCount);
});
});
Here's a way to get the row count in the spec file without using extra packages, plugins, test hooks, or npm scripts.
Basically, you can create a separate module that makes a synchronous HTTP request using the XMLHTTPRequest class to the /analytics endpoint and use the browser's DOMParser class to find the return the number of <tr> tags.
// scripts/get-row-count.js
export function getRowCount() {
let request = new XMLHttpRequest();
// Set async to false because Cypress will not wait for async functions to finish before looking for it() statements
request.open('GET', '/analytics', false);
request.send(null);
const document = new DOMParser().parseFromString(request.response, 'text/html');
const trTags = Array.from(document.getElementsByTagName('tr'));
return trTags.length;
};
Then in the spec file, import the new function and now you can get an updated row count whenever you need it.
import { getRowCount } from '../scripts/get-row-count';
Cypress._.times(getRowCount() => {
it('process row', () => {
...
})
})
The reason for XMLHTTPRequest instead of fetch is because it allows synchronous requests to be made. Synchronous requests are needed because Cypress won't wait for async requests to come back before parsing for it() blocks.
With this, you always have the most up to date row count without it going stale.

How to run the single test with different data set in parallel by using cypress on single machine

I just have the below Test.json file in the fixture folder:
[
{
"searchKeyword":"cypress"
},
{
"searchKeyword":"QA automation"
},
{
"searchKeyword":"stackoverflow"
}
]
The above file contains three different dataset.
I just have the below spec file and It contains one It (Test case) and It will run multiple times based on the above input.
Test.spec.js file:
describe("Run the test parallel based on the input data",() =>{
const baseUrl = "https://www.google.com/";
before("Login to consumer account", () => {
cy.fixture('Test').then(function (data) {
this.data = data;
})
});
it("Search the keyword", function () {
this.data.forEach((testData) =>{
cy.visit(baseUrl);
cy.xpath("//input[#name='q']").type(testData.searchKeyword);
cy.xpath("//input[#value='Google Search']").click();
cy.get("//ul/li[2]").should("be.visible");
});
});
});
The above code is working as expected. But I just want to run the above single test parallelly by using different dataset.
Example: Three browser instance open and it should pick three different data from the Test.json file and run the single test which is available in the Test.spec.js file.
I just need logic to implement for one of my project, But I'm not able to share the code which is more complex that's reason just create some dummy test data and test script to achieve my logic.
Can someone please share yours thoughts to achieve this.
One way to run multiple instances of Cypress in parallel is via the Module API, which is basically using a Node script to start up the multiple instances.
Node script
// run-parallel.js
const cypress = require('cypress')
const fixtures = require('./cypress/fixtures/Test.json')
fixture.forEach(fixture => {
cypress.run({
env: {
fixture
},
})
})
Test
describe("Run the test for given env data",() =>{
const testData = Cypress.env('fixture')
...
it("Search the keyword", function () {
cy.visit(baseUrl);
cy.xpath("//input[#name='q']").type(testData.searchKeyword);
...
});
});
Awaiting results
cypress.run() returns a promise, so you can collate the results as follows
Videos and screenshots are troublesome, since it tries to save all under the same name, but you can specify a folder for each fixture
const promises = fixtures.map(fixture => {
return cypress.run({
config: {
video: true,
videosFolder: `cypress/videos/${fixture.searchKeyword}`,
screenshotsFolder: `cypress/screenshots/${fixture.searchKeyword}`,
},
env: {
fixture
},
spec: './cypress/integration/dummy.spec.js',
})
})
Promise.all(promises).then((results) => {
console.log(results.map(result => `${result.config.env.fixture.searchKeyword}: ${result.status}`));
});

Accessing environmental variable in Cypress test

I am trying to call an external script from a Cypress test using cy.exec to create an environmental variable which I then want to access this environmental variable in the test. Is this possible using Cypress.env(<environmental variable>)?
The external script looks like:
const cypress = require('cypress');
const pkg = require('#glib/cypress-secrets');
const { createAuthApiKeyKubeSecret } = pkg;
const username = 'test-consumer';
const apikey = createAuthApiKeyKubeSecret(username);
console.log(apikey);
process.env.apikey = apikey;
This script is called by the before function in the test.
describe("Test to create a capability", function () {
before(() => {
cy.exec('node create-secret.js');
});
after(() => {
cy.exec('node delete-secret.js', {log: true});
});
it('Checks on the Create page', function() {
cy.visit(Cypress.config().baseUrl + "?apikey=" + Cypress.env('apikey'));
// We need to check if we are on the correct page
// We just need to check two elements, a label and a button.
cy.contains('About the capability').should('exist');
cy.contains('button', 'Next Step').should('exist')
});
});
The baseUrl is set correctly but the apikey environmental variable is coming back undefined.
According to the documentation (here) you have to put cypress_ or CYPRESS_ before the variable name:
process.env.cypress_apikey = apikey;

Parallel requests in cypress

I want to make parallel requests in cypress. I define a command for that:
const resetDb = () => {
const apiUrl = `${Cypress.config().baseUrl}/api`;
Cypress.Promise.all([
cy.request(`${apiUrl}/group/seed/resetDb`),
cy.request(`${apiUrl}/auth/seed/resetDb`),
cy.request(`${apiUrl}/email/seed/resetDb`),
]);
};
Cypress.Commands.add('resetDb', resetDb);
However, it is still making those requests in sequence. What am I doing wrong?
I was able to solve this problem using task in Cypress, which allows you to use nodejs API.
In the plugins index file, I define a task as follows:
const fetch = require('isomorphic-unfetch');
module.exports = on => {
on('task', {
resetDb() {
const apiUrl = `http://my.com/api`;
return Promise.all([
fetch(`${apiUrl}/group/seed/resetDb`),
fetch(`${apiUrl}/auth/seed/resetDb`),
fetch(`${apiUrl}/email/seed/resetDb`),
]);
},
});
};
The it can be used as follows:
before(() => {
return cy.task('resetDb');
});

How do you dynamically load launch_url and append a relative path to it

I am trying to use the property launch_url in my tests and append a relative path to it before sending the browser to that url.
I know that browser.init(); send the browser to the address in launch_url but I was wondering if it is possible to append some string to the retrieved url before executing the redirect.
Assuming my launch_url is www.xxx.com, I'd like to do something like:
this.test = function (client) {
client.init("/a/b/c"); // >> should result in the browser going to www.xxx.com/a/b/c
};
Any idea?
Thanks
If you have specified a "launch_url" in your nightwatch.json file, you can access it on the 'client' object passed into your tests. For example:
module.exports = {
'Demo test Google' : function (client) {
client
.url(client.launch_url)
.waitForElementVisible('body', 1000)
.setValue('input[type=text]', 'nightwatch')
.waitForElementVisible('button[name=btnG]', 1000)
.click('button[name=btnG]')
.pause(1000)
.assert.containsText('#main', 'Night Watch')
.end();
}
};
So, for your example code:
this.test = function (client) {
client.url(client.launch_url + "/a/b/c"); // >> should result in the browser going to www.xxx.com/a/b/c
};
An alternative would be to create a config file with some base items in it; then be able to append that config file through a custom init method you use.
IE:
var configDefaults = module.exports = {
baseUrl: 'http://www.example.com/',
local: false,
};
Then in your custom command write something like:
"use strict";
var config = require('./config')
exports.command = function(urlModifier) {
var _self = this;
_self
.url(config.baseUrl+urlModifier)
.waitForElementVisible('body', 4000)
if( typeof callback === "function"){
callback.call(_self);
}
return this;
};

Resources