In this article, I am going the discuss the script I have created to automate the execution of test case and to update the status on the basis of results , for related test case in TestCollab application using API.

What we are up to?

We are automating the process of verification of the login feature offered by TestCollab.

How it is being done?

Assumptions

It is being assumed that

  • The reader has been through the TestCollab API guide accessible at http://developers.testcollab.com

  • The reader has basic knowledge of using API endpoints (TestCollab or any other REST API)

The key test steps

  • The application should not allow login through invalid credentials and should show a proper message to user

  • If correct credentials are provided on login page, user should be allowed access to the application by showing him/her the tasks page (this is the page shown by default after login)

Pre-requisites

  • A testcase created in TestCollab that needs to be automated

  • One or more test plans that are created to test the functionality with concerned testcase assigned to a user

Values expected

  • Id of the project that has the testcase to be automated

  • Id of the concerned testcase

  • A valid API token of the TestCollab user who has been assigned the testcase

The code

login is the function that does the main job of testing.

While login does the main task of verification, other sections of code take care of

  • Fetching the correct testplantestcases that are created for testcase whose id is provided

  • Looping through testplantestcases, it fetches the testplanregressions (test plan RUNs)

  • Then for each RUN list of concerned executedtestcases records are fetched

  • With all required information from the application the login function is then invoked

  • If the status returned by login is same as the status already set for executedtestcase record then the executedtestcase status is set to unexecuted

  • The executedtestcase record is then updated to have status as per the results of automation

The snippet below uses javascript. Selenium and XHR libraries are mainly required.

Moreover, I used Chrome to run the automated script, you can use other browser and that may require some changes in part of code that is used to find UI element and perform actions on it.

const { Builder, By, Key, until } = require('selenium-webdriver')
const assert = require('assert');
const { elementIsVisible } = require('selenium-webdriver/lib/until');
const { WebDriver } = require('selenium-webdriver');
const XMLHttpRequest = require("xmlhttprequest").XMLHttpRequest;
const console = require('console');

async function login() {
let driver = await new Builder().forBrowser("chrome").build();

try {
const TIMEOUT = 10000
await driver.manage().setTimeouts({ implicit: TIMEOUT, pageLoad: TIMEOUT, script: TIMEOUT })
await driver.get("https://testcollab.io/login")
await driver.manage().window().setRect({ width: 750, height: 750 })
await driver.findElement(By.id("normal_login_userName")).click()
await driver.findElement(By.id("normal_login_userName")).sendKeys("vishal+st2022ent@gigapromoters.com")
await driver.findElement(By.id("normal_login_password")).sendKeys("abcd1234")
await driver.findElement(By.css(".btn-success")).click()
await driver.wait(until.elementIsVisible(driver.findElement(By.css(".ant-notification-notice-message"))), 10000);
await driver.findElement(By.css(".ant-notification-notice-message")).click()
assert(await driver.findElement(By.css(".ant-notification-notice-message")).getText() == "Identifier or password invalid.")
await driver.findElement(By.css(".anticon-close > svg")).click()
await driver.findElement(By.id("normal_login_password")).click()
await driver.findElement(By.id("normal_login_password")).clear()
await driver.findElement(By.id("normal_login_password")).sendKeys("abcd12345")
await driver.findElement(By.css(".btn-success")).click()
await driver.executeScript("window.scrollTo(0,0)")
await driver.findElement(By.css(".page-title")).click()
await assert(await driver.findElement(By.css(".page-title")).getText() == "My Tasks")
await driver.findElement(By.css(".white_wrapper")).click()
await driver.findElement(By.css(".UserAvatar--inner")).click()
await driver.findElement(By.partialLinkText("Logout")).click()
driver.close()
return 1;
}
catch (e) {
console.log(e)
driver.close()
return 2;
}
}

let project_id = 8;
let testcase_id = 1;
let api_token = 'sOm3D4mM4491';

let xhr = new XMLHttpRequest();

let url = new URL('https://api.testcollab.io/users/me');
url.searchParams.set('token', api_token);

let xhrBody = null;
let assignee_id = null;

try {
xhr.open("GET", url);
xhr.timeout = 10000;
xhr.responseType = 'json';
xhr.send(xhrBody);

xhr.onload = function () {
if (xhr.status != 200) {
console.log(`xhr Error ${xhr.status}: ${xhr.statusText}`);
} else {
const obj = JSON.parse(xhr.responseText);
assignee_id = obj["id"];

let xhr2 = new XMLHttpRequest();

url = new URL('https://api.testcollab.io/testplantestcases');
url.searchParams.set('token', api_token);
url.searchParams.set('project', project_id);
url.searchParams.set('test_case', testcase_id);
url.searchParams.set('_sort', 'ID:DESC');

xhrBody2 = null;

try {
xhr2.open("GET", url);
xhr2.timeout = 10000;
xhr2.responseType = 'json';
xhr2.send(xhrBody2);

xhr2.onload = function () {
if (xhr2.status != 200) {
console.log(`xhr2 Error ${xhr2.status}: ${xhr2.statusText}`);
} else {
const obj = JSON.parse(xhr2.responseText);
obj.forEach(testplantestcase => {
if (testplantestcase["testplan"]["id"] !== undefined) {
let testplantestcase_id = testplantestcase["id"];
let testplan_id = testplantestcase["testplan"]["id"];

let xhr3 = new XMLHttpRequest();

url = new URL('https://api.testcollab.io/testplanregressions');
url.searchParams.set('token', api_token);
url.searchParams.set('project', project_id);
url.searchParams.set('testplan', testplan_id);
url.searchParams.set('_limit', 1);
url.searchParams.set('_sort', 'ID:DESC');

xhrBody3 = null;

try {
xhr3.open("GET", url);
xhr3.timeout = 10000;
xhr3.responseType = 'json';
xhr3.send(xhrBody3);

xhr3.onload = function () {
if (xhr3.status != 200) {
console.log(`xhr3 Error ${xhr3.status}: ${xhr3.statusText}`);
} else {
const obj = JSON.parse(xhr3.responseText);
obj.forEach(testplanregressions => {
if (testplanregressions["id"] !== undefined) {
let regression_id = testplanregressions["id"];
let xhr4 = new XMLHttpRequest();

url = new URL('https://api.testcollab.io/executedtestcases');
url.searchParams.set('token', api_token);
url.searchParams.set('project', project_id);
url.searchParams.set('regression', regression_id);
url.searchParams.set('assigned_to', assignee_id);
url.searchParams.set('test_plan_test_case', testplantestcase_id);
url.searchParams.set('_sort', 'ID:DESC');

xhrBody4 = null;

try {
xhr4.open("GET", url);
xhr4.timeout = 10000;
xhr4.responseType = 'json';
xhr4.send(xhrBody4);

xhr4.onload = async function () {
if (xhr4.status != 200) {
console.log(`xhr4 Error ${xhr4.status}: ${xhr4.statusText}`);
} else {
const obj = JSON.parse(xhr4.responseText);
obj.forEach(executedtestcase => {
if (executedtestcase["id"] !== undefined) {
login().then((testcase_status) => {
let executabletestcase_id = executedtestcase["id"];
let testplanconfig_id = ((executedtestcase["test_plan_config"] !== undefined && executedtestcase["test_plan_config"] !== null) ? executedtestcase["test_plan_config"]["id"] : null);
let prev_status = executedtestcase["status"];
if (prev_status == testcase_status) {
//if previous executed test case status is same as current status then in order to write new execution log we need to mark it as unexecuted
let xhr6 = new XMLHttpRequest();
url = new URL('https://api.testcollab.io/executedtestcases/' + executabletestcase_id);
url.searchParams.set('token', api_token);

let xhrBody5 = {
"project": project_id,
"status": 0,
"_comment1": "Resetting the status to unexecuted before adding new execution log",
"test_plan_config": testplanconfig_id
};
try {
xhr6.open("PUT", url);
xhr6.timeout = 10000;
xhr6.responseType = 'json';
xhr6.setRequestHeader("Content-Type", "application/json;charset=UTF-8");
xhr6.send(JSON.stringify(xhrBody5));
xhr6.onload = function () {
if (xhr6.status != 200) {
console.log(`xhr6 Error ${xhr6.status}: ${xhr6.statusText}`);
} else {
const obj = JSON.parse(xhr6.responseText);
console.log("Resetting testcase status");
let xhr5 = new XMLHttpRequest();
url = new URL('https://api.testcollab.io/executedtestcases/' + executabletestcase_id);
url.searchParams.set('token', api_token);

let xhrBody5 = {
"project": project_id,
"status": testcase_status,
"_comment1": "Optional",
"test_plan_config": testplanconfig_id
};
try {
xhr5.open("PUT", url);
xhr5.timeout = 10000;
xhr5.responseType = 'json';
xhr5.setRequestHeader("Content-Type", "application/json;charset=UTF-8");
xhr5.send(JSON.stringify(xhrBody5));
xhr5.onload = function () {
if (xhr5.status != 200) {
console.log(`xhr5 Error ${xhr5.status}: ${xhr5.statusText}`);
} else {
const obj = JSON.parse(xhr5.responseText);
console.log("Done");
}
};

xhr5.onprogress = function (event) {
if (event.lengthComputable) {
console.log(`Received ${event.loaded} of ${event.total} bytes`);
} else {
console.log(`Received ${event.loaded} bytes`);
}
};

xhr5.onerror = function () {
console.log("xhr5 Request failed");
};

}
catch (e) {
console.log("Caught " + e);
}
}
};

xhr6.onprogress = function (event) {
if (event.lengthComputable) {
console.log(`Received ${event.loaded} of ${event.total} bytes`);
} else {
console.log(`Received ${event.loaded} bytes`);
}
};

xhr6.onerror = function () {
console.log("xhr6 Request failed");
};

}
catch (e) {
console.log("Caught " + e);
}
} else {

let xhr5 = new XMLHttpRequest();
url = new URL('https://api.testcollab.io/executedtestcases/' + executabletestcase_id);
url.searchParams.set('token', api_token);

let xhrBody5 = {
"project": project_id,
"status": testcase_status,
"_comment1": "Optional",
"test_plan_config": testplanconfig_id
};
try {
xhr5.open("PUT", url);
xhr5.timeout = 10000;
xhr5.responseType = 'json';
xhr5.setRequestHeader("Content-Type", "application/json;charset=UTF-8");
xhr5.send(JSON.stringify(xhrBody5));
xhr5.onload = function () {
if (xhr5.status != 200) {
console.log(`xhr5 Error ${xhr5.status}: ${xhr5.statusText}`);
} else {
const obj = JSON.parse(xhr5.responseText);
console.log("Done");
}
};

xhr5.onprogress = function (event) {
if (event.lengthComputable) {
console.log(`Received ${event.loaded} of ${event.total} bytes`);
} else {
console.log(`Received ${event.loaded} bytes`);
}
};

xhr5.onerror = function () {
console.log("xhr5 Request failed");
};

}
catch (e) {
console.log("Caught " + e);
}
}
});
}
})
}
};
xhr4.onprogress = function (event) {
if (event.lengthComputable) {
console.log(`Received ${event.loaded} of ${event.total} bytes`);
} else {
console.log(`Received ${event.loaded} bytes`);
}
};

xhr4.onerror = function () {
console.log("xhr4 Request failed");
};
}
catch (e) {
console.log("Caught " + e);
}
}
})
}
};

xhr3.onprogress = function (event) {
if (event.lengthComputable) {
console.log(`Received ${event.loaded} of ${event.total} bytes`);
} else {
console.log(`Received ${event.loaded} bytes`);
}
};

xhr3.onerror = function () {
console.log("xhr3 Request failed");
};
}
catch (e) {
console.log("Caught " + e);
}
}
})
}
};

xhr2.onprogress = function (event) {
if (event.lengthComputable) {
console.log(`Received ${event.loaded} of ${event.total} bytes`);
} else {
console.log(`Received ${event.loaded} bytes`);
}
};

xhr2.onerror = function () {
console.log("xhr2 Request failed");
};
}
catch (e) {
console.log("Caught " + e);
}
}
};

xhr.onprogress = function (event) {
if (event.lengthComputable) {
console.log(`Received ${event.loaded} of ${event.total} bytes`);
} else {
console.log(`Received ${event.loaded} bytes`);
}
};

xhr.onerror = function () {
console.log("xhr Request failed");
};
}
catch (e) {
console.log("Caught " + e);
}

Next step

I have not set the step wise results, this can be done by setting JSON for individual step of test case having step index , step , expected result , status, comment, mentions and attachments etc.

Hope you enjoyed reading this article, do share your concerns and queries here . Till then Happy testing :) ...

Did this answer your question?