src/main/webapp/parallelRunnerEnvironment.js
/*
* Certain versions of software accessible here may contain branding from Hewlett-Packard Company (now HP Inc.) and Hewlett Packard Enterprise Company.
* This software was acquired by Micro Focus on September 1, 2017, and is now offered by OpenText.
* Any reference to the HP and Hewlett Packard Enterprise/HPE marks is historical in nature, and the HP and Hewlett Packard Enterprise/HPE marks are the property of their respective owners.
* __________________________________________________________________
* MIT License
*
* Copyright 2012-2024 Open Text
*
* The only warranties for products and services of Open Text and
* its affiliates and licensors ("Open Text") are as may be set forth
* in the express warranty statements accompanying such products and services.
* Nothing herein should be construed as constituting an additional warranty.
* Open Text shall not be liable for technical or editorial errors or
* omissions contained herein. The information contained herein is subject
* to change without notice.
*
* Except as specifically indicated otherwise, this document contains
* confidential information and a valid license is required for possession,
* use or copying. If this work is provided to the U.S. Government,
* consistent with FAR 12.211 and 12.212, Commercial Computer Software,
* Computer Software Documentation, and Technical Data for Commercial Items are
* licensed to the U.S. Government under vendor's standard commercial license.
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
* ___________________________________________________________________
*/
if (typeof RUN_FROM_FS_BUILDER_SELECTOR == "undefined") {
RUN_FROM_FS_BUILDER_SELECTOR = 'div[name="builder"][descriptorid="com.microfocus.application.automation.tools.run.RunFromFileBuilder"]';
}
/**
* Prototype that represents the modal dialog.
* @constructor
*/
function ModalDialog() {}
/**
* Add the style sheet to the provided modal node.
* @param modalCSS the modal style sheet link
*/
ModalDialog.addStyleSheet = function(modalCSS) {
const ss = document.styleSheets;
for (let i = 0, max = ss.length; i < max; i++) {
if (ss[i].href == modalCSS)
return;
}
var link = document.createElement("link");
Utils.addStyleSheet(link,modalCSS);
document.head.appendChild(link);
};
/**
* Generate the browser item for the browsers dialog.
* @param iconSrc the icons source
* @param browserId the id of the browser
* @param labelText the label text
* @param checked true if the radio should be checked, false otherwise
* @returns {*}
*/
ModalDialog.generateBrowserItem = function(iconSrc,browserId,labelText,checked) {
var browserRadio = document.createElement("input");
browserRadio.setAttribute("class","browser-item-child customRadio");
browserRadio.setAttribute("type","radio");
browserRadio.setAttribute("name","browser-radio");
if(checked) {
browserRadio.setAttribute("checked", "true");
}
browserRadio.setAttribute("id",browserId);
var browserImage = document.createElement("img");
browserImage.setAttribute("class","browser-item-child");
browserImage.setAttribute("src",iconSrc);
var browserItem = document.createElement("div");
browserItem.setAttribute("class","browser-item");
var browserLabel = document.createElement("div");
browserLabel.setAttribute("class","browser-item-child browser-name");
browserLabel.innerHTML = labelText;
browserItem.appendChild(browserRadio);
browserItem.appendChild(browserImage);
browserItem.appendChild(browserLabel);
return browserItem;
};
ModalDialog.saveBrowserSettings = function(modalNode) {
// retrieve the button that was used to open the env wizard
const parent = ModalDialog.currentEnv;
let radioButtons = modalNode.querySelectorAll('input[type="radio"]');
for(let i =0; i < radioButtons.length;i++) {
if(radioButtons[i].checked) {
ParallelRunnerEnv.setUpBrowserEnvironment(parent,radioButtons[i],modalNode);
}
}
};
ModalDialog.setSelectedBrowser = function(modal,browserId){
if(ModalDialog.browsers == null) return false;
// check if the provided browser id matches any of the available browsers
var selectedBrowser = null;
for(var i = 0; i < ModalDialog.browsers.length; i++) {
if(ModalDialog.browsers[i].toLowerCase() === browserId.toLowerCase()) {
selectedBrowser = ModalDialog.browsers[i];
}
}
// no match, select the default one
if(selectedBrowser == null) return false;
var radioButton = modal.querySelector("input[id=" + selectedBrowser + "]");
if(radioButton == null) return false;
// set the corresponding radio button to be checked
radioButton.checked = true;
return true;
};
/**
* Hide the modal with the given modalId.
* @param modalId the modal id
* @returns {boolean} true if the modal was hidden, false otherwise
*/
ModalDialog.hide = function(modalId) {
var modal = document.getElementById(modalId);
if(modal == null) return false;
modal.style.display = "none";
};
/**
* Generate the modal dialog
* @constructor
*/
ModalDialog.generate = function(path) {
var modalCSS = path + "plugin/hp-application-automation-tools-plugin/css/modal_dialog.css";
// add the css style
ModalDialog.addStyleSheet(modalCSS);
var browsersModal = document.createElement("div");
browsersModal.setAttribute("id","browsersModal");
browsersModal.setAttribute("class","modal");
var modalContent = document.createElement("div");
modalContent.setAttribute("class","modal-content");
var modalHeader = document.createElement("div");
modalHeader.setAttribute("class","modal-header");
var span = document.createElement("div");
span.innerHTML = "x";
span.setAttribute("class","close");
span.setAttribute("onclick","ModalDialog.hide('browsersModal')");
var title = document.createElement("div");
title.innerHTML = "Choose a browser";
title.setAttribute("class","modal-title");
var modalBody = document.createElement("div");
modalBody.setAttribute("class","modal-body");
var iconsSRC = path + "plugin/hp-application-automation-tools-plugin/ParallelRunner/icons/";
var chromeBrowserItem = ModalDialog.generateBrowserItem(iconsSRC + 'svg/chrome.svg','Chrome','Chrome',true);
var firefoxBrowserItem = ModalDialog.generateBrowserItem(iconsSRC + 'svg/firefox.svg','Firefox','Firefox',false);
var firefox64BrowserItem = ModalDialog.generateBrowserItem(iconsSRC + 'svg/firefox.svg','Firefox64','Firefox 64',false);
var ieBrowserItem = ModalDialog.generateBrowserItem(iconsSRC + 'svg/explorer.svg','IE','IE',false);
var ie64BrowserItem = ModalDialog.generateBrowserItem(iconsSRC + 'svg/explorer.svg','IE64','IE 64',false);
// retain the available browsers
ModalDialog.browsers = ["Chrome","Firefox","Firefox64","IE","IE64"];
// add the browser items to the modal body
modalBody.appendChild(chromeBrowserItem);
modalBody.appendChild(firefoxBrowserItem);
modalBody.appendChild(firefox64BrowserItem);
modalBody.appendChild(ieBrowserItem);
modalBody.appendChild(ie64BrowserItem);
var saveText = document.createElement('div');
saveText.innerHTML = "SAVE";
saveText.setAttribute("class","save-text");
saveText.setAttribute("onclick",'ModalDialog.saveBrowserSettings(browsersModal)');
var modalFooter = document.createElement("div");
modalFooter.setAttribute("class","modal-footer");
modalFooter.appendChild(saveText);
modalHeader.appendChild(span);
modalHeader.appendChild(title);
modalContent.appendChild(modalHeader);
modalContent.appendChild(modalBody);
modalContent.appendChild(modalFooter);
browsersModal.appendChild(modalContent);
return browsersModal;
};
/**
* Multi-purpose utils class.
* @constructor
*/
function Utils() {}
/**
* Check if a string is empty.
* @param str the string to check
* @returns {boolean}
*/
Utils.isEmptyString = function(str) {
return (!str || str.length === 0);
};
Utils.addStyleSheet = function(elem,sheetHref) {
elem.setAttribute("rel","stylesheet");
elem.setAttribute("type","text/css");
elem.setAttribute("href",sheetHref);
};
/**
* Parse the information received for MobileCenter.
* @param deviceId - the deviceId string
* @param os - the os string
* @param manufacturerAndModel - the manufacturer and model string
* @returns {string} the parsed information.
*/
Utils.parseMCInformation = function(deviceId, os, manufacturerAndModel) {
var mc_params = [];
var os_types = {android : 'Android', ios: 'ios', wp : 'Windows Phone'};
if(!Utils.isEmptyString(deviceId))
mc_params.push({name: 'deviceId', value: deviceId});
else {
if (!Utils.isEmptyString(os)) {
// regex for the os string
// example: ios<=10.2.2 => we need to extract ios,<=,10.2.2
var regex = /([a-z]+)(>=|>|<|<=)*([0-9].*[0-9])+/gi;
var match = regex.exec(os);
if (match == null) { // string does not contain a version
mc_params.push({name: 'osType', value: os_types[os]});
mc_params.push({name: 'osVersion', value: 'any'});
}
else { // version was given to us
mc_params.push({name: 'osType', value: os_types[match[1]]});
mc_params.push({name: 'osVersion', value: match[2] == null ? match[3] : match[2] + match[3]});
}
}
}
if(!Utils.isEmptyString(manufacturerAndModel))
mc_params.push({name: 'manufacturerAndModel', value: manufacturerAndModel});
var result_str = "";
for(var i = 0; i < mc_params.length; i++) {
result_str = result_str.concat(mc_params[i].name, " : ", mc_params[i].value, (i===mc_params.length - 1) ? "" : ",");
}
return result_str;
};
/**
* Set the provided jenkins element visibility.
* @param element - the jenkins element to be hidden
* @param isVisible - the visible boolean state to be aquired
*/
Utils.setJenkinsElementVisibility = function(element,isVisible) {
let parent = element.parentElement.closest(".jenkins-form-item");
parent.style.display = isVisible ? "" : "none";
};
/**
* Load the mobile center wizard.
* @param a descriptor
* @param b the environment wizard button
*/
Utils.loadMC = function(a, b, prEnv){
b.disabled = true;
const divMain = prEnv.parentElement.closest(RUN_FROM_FS_BUILDER_SELECTOR);
const dl = divMain.querySelector("#mobileSpecificSection");
var mcUserName = dl.querySelector('input[name="mcUserName"]')?.value;
var mcPassword = dl.querySelector('input[name="mcPassword"]')?.value;
var mcTenantId = dl.querySelector('input[name="mcTenantId"]')?.value;
var mcExecToken = dl.querySelector('input[name="mcExecToken"]')?.value;
var mcAuthType = dl.querySelector('input[name$="authModel"]:checked')?.value;
var mcUrl = dl.querySelector('select[name="mcServerName"]')?.value;
var useProxy = dl.querySelector('input[name="proxySettings"]')?.checked;
var proxyAddress = dl.querySelector('input[name="fsProxyAddress"]')?.value;
var useProxyAuth = dl.querySelector('input[name="fsUseAuthentication"]')?.checked;
var proxyUserName = dl.querySelector('input[name="fsProxyUserName"]')?.value;
var proxyPassword = dl.querySelector('input[name="fsProxyPassword"]')?.value;
var isMcCredentialMissing;
if ('base' == mcAuthType) {
isMcCredentialMissing = !mcUserName || !mcPassword || mcUserName.trim() == "" || mcPassword.trim() == "";
} else {
isMcCredentialMissing = !mcExecToken || mcExecToken.trim() == "";
}
const isProxyAddressRequiredButMissing = useProxy && (proxyAddress == null || proxyAddress.trim() == "");
const isProxyCredentialRequiredButMissing = useProxyAuth && (!proxyUserName || !proxyPassword || proxyUserName.trim() == "" || proxyPassword.trim() == "");
if(isMcCredentialMissing || isProxyAddressRequiredButMissing || isProxyCredentialRequiredButMissing){
ParallelRunnerEnv.setEnvironmentError(prEnv,true);
b.disabled = false;
return;
}
var previousJobId = dl.querySelector('[name="fsJobId"]')?.value;
a.getMcServerUrl(mcUrl, function(r){
let baseUrl = r.responseObject();
if(baseUrl){
baseUrl = baseUrl.trim().replace(/[\/]+$/, "");
} else {
ParallelRunnerEnv.setEnvironmentError(prEnv,true);
b.disabled = false;
return;
}
a.getJobId(baseUrl, mcUserName, mcPassword, mcTenantId, mcExecToken, mcAuthType, useProxyAuth, proxyAddress, proxyUserName, proxyPassword, previousJobId, function (response) {
let map = response.responseObject();
let jobId = '';
let tenantId = '';
let isSaaS = false;
//First need to check the return type(not sure if java map can automatically covert to js map)
if(map.hasOwnProperty("jobId")) {
jobId = map["jobId"];
}
if(map.hasOwnProperty("TENANT_ID_COOKIE")) {
tenantId = map["TENANT_ID_COOKIE"];
}
if(map.hasOwnProperty("isSaaS")) {
isSaaS = map["isSaaS"];
}
// var jobId = response.responseObject();
if(jobId == null) {
ParallelRunnerEnv.setEnvironmentError(prEnv,true);
b.disabled = false;
return;
}
var openedWindow = window.open('/','test parameters','height=820,width=1130');
openedWindow.location.href = 'about:blank';
if (isSaaS) {
openedWindow.location.href = baseUrl + "/integration8/en/#/main/wizard?TENANTID=" + tenantId + "&jobId=" + jobId + "&displayUFTMode=true&deviceOnly=true";
} else {
openedWindow.location.href = baseUrl+"/integration8/en/#/login?jobId="+jobId+"&displayUFTMode=true&deviceOnly=true";
}
var messageCallBack = function (event) {
if (event?.data=="mcCloseWizard") {
a.populateAppAndDevice(baseUrl, mcUserName, mcPassword, mcTenantId, mcExecToken, mcAuthType, useProxyAuth, proxyAddress, proxyUserName, proxyPassword, jobId, function (app) {
var jobInfo = app.responseObject();
let deviceId = "", OS = "", manufacturerAndModel = "";
if(jobInfo['deviceJSON']){
if(jobInfo['deviceJSON']['deviceId']){
deviceId = jobInfo['deviceJSON']['deviceId'];
}
if(jobInfo['deviceJSON']['OS']){
OS = jobInfo['deviceJSON']['OS'];
}
if(jobInfo['deviceJSON']['manufacturerAndModel']){
manufacturerAndModel = jobInfo['deviceJSON']['manufacturerAndModel'];
}
}
if(jobInfo['deviceCapability']){
if(jobInfo['deviceCapability']['OS']){
OS = jobInfo['deviceCapability']['OS'];
}
if(jobInfo['deviceCapability']['manufacturerAndModel']){
manufacturerAndModel = jobInfo['deviceCapability']['manufacturerAndModel'];
}
}
console.log(deviceId);
ParallelRunnerEnv.setEnvironmentSettingsInput(prEnv,Utils.parseMCInformation(deviceId,OS,manufacturerAndModel));
b.disabled = false;
ParallelRunnerEnv.setEnvironmentError(prEnv,false);
window.removeEventListener("message",messageCallBack, false);
openedWindow.close();
});
}
};
window.addEventListener("message", messageCallBack ,false);
function checkChild() {
if (openedWindow && openedWindow.closed) {
clearInterval(timer);
b.disabled = false;
}
}
var timer = setInterval(checkChild, 500);
});
});
};
/**
* Prototype that represents the ParallelRunner environment.
* @constructor
*/
function ParallelRunnerEnv() {}
/**
*
* @param parent
* @returns {*}
*/
ParallelRunnerEnv.getEnvironmentSettingsInputNode = function (parent) {
return parent?.querySelector(".jenkins-input,.setting-input") ;
};
/**
* Set the environment text box input to the given input value,
* the input corresponds to the clicked environment wizard button.
* @param parent the parent container of the clicked environment wizard button, like "parallelRunnerEnvironments"
* @param inputValue the input value to be set
* @returns {boolean} true if it succeeded, false otherwise.
*/
ParallelRunnerEnv.setEnvironmentSettingsInput = function(parent,inputValue) {
let settingInput = ParallelRunnerEnv.getEnvironmentSettingsInputNode(parent);
settingInput && (settingInput.value = inputValue);
};
/**
* Enable the error div that corresponds to the clicked
* environment wizard button.
* @param parent the current environment container
* @param enable the div visibility state(true - visible, false - hidden)
* @returns {boolean} true if it succeeded, false otherwise.
*/
ParallelRunnerEnv.setEnvironmentError = function(parent, enable) {
const errorDiv = parent?.querySelector('div[name="mcSettingsError"]');
errorDiv && (errorDiv.style.display = enable ? "block" : "none");
};
/**
* Set the browser selection modal visibility.
* @param parent - the current environment container
* @param modalId - the browser modal id
* @param visible - should the modal be visible?(true / false)
* @param path - the patch to the root of the plugin
*/
//TODO refactor ModalDialog to be appended to a parent divMain instead of document, with divMain computed from button
ParallelRunnerEnv.setBrowsersModalVisibility = function(parent,modalId,visible,path) {
var modal = document.getElementById(modalId);
// it wasn't generated, so we need to generate it
if(modal == null) {
// generate it
modal = ModalDialog.generate(path);
// add it to the DOM
document.body.appendChild(modal);
}
ModalDialog.currentEnv = parent;
modal = document.getElementById(modalId);
let settingInput = ParallelRunnerEnv.getEnvironmentSettingsInputNode(parent);
const environmentInputValue = settingInput?.value;
// set the selected browser to match the one in the input
if(environmentInputValue != null) {
var browser = environmentInputValue.split(":");
// should be of the form browser: BrowserName
if(browser?.length === 2)
{
ModalDialog.setSelectedBrowser (modal,browser[1].trim());
}
}
if(visible) {
modal.style.display = "block";
// also allow the user to hide it by clicking anywhere on the window
window.onclick = function(event) {
if (event.target === modal) {
modal.style.display = "none";
}
};
}
else
modal.style.display = "none";
};
/**
* Determine the currently selected environment type.
* @param parent - the current environment container
* @returns {*}
*/
ParallelRunnerEnv.getCurrentEnvironmentType = function(parent) {
let input = parent?.querySelector('input[type="radio"][name$="environmentType"]:checked');
return input?.defaultValue;
};
/**
* Set the environment text based on the browser selection.
* @param parent - the current environment container
* @param radio - the selected radio
* @param modal - the browser selection modal
*/
ParallelRunnerEnv.setUpBrowserEnvironment = function(parent,radio,modal) {
// we can close the modal now
modal.style.display = "none";
// based on the browser chosen we will prepare the environment
ParallelRunnerEnv.setEnvironmentSettingsInput(parent,"browser : " + radio['id']);
};
/**
* Sets the environment and test set visibility based on the parallel runner checkBox state.
* @param panel - the current build step container
* @param visible - the visible boolean state to be aquired
*/
ParallelRunnerEnv.setEnvironmentsVisibility = function(panel, visible) {
var environments = panel.querySelectorAll("div[name='fileSystemTestSet']");
if (environments == null || environments.length == 0) return;
[...environments].forEach(env => {
Utils.setJenkinsElementVisibility(env,visible);
});
};
/**
* Click handler for the environment wizard button.
* @param btn the environment wizard button
* @param a first mc argument
* @param modalId the browser modalId to be shown
* @param visibility of the modal
* @param pluginPath the ${root} path
* @returns {boolean}
*/
ParallelRunnerEnv.onEnvironmentWizardClick = function(a,btn,modalId,visibility,pluginPath) {
// get the environment type for the current env, it could be: 'web' or 'mobile'
const prEnv = btn.parentElement.closest('div[name="parallelRunnerEnvironments"]');
let type = ParallelRunnerEnv.getCurrentEnvironmentType(prEnv);
if(type == null) return false;
// if the type is web we need to show the browsers modal
if(type.toLowerCase() === 'web') {
ParallelRunnerEnv.setBrowsersModalVisibility(prEnv,modalId,visibility,pluginPath);
return true;
}
// open the mobile center wizard
if(type.toLowerCase() === 'mobile') {
try {
Utils.loadMC(a, btn, prEnv);
} catch(e) {
console.error(e);
} finally {
btn.disabled = false;
}
return true;
}
return false;
};
/**
* Utility class for the RunFromFileSystem model.
* @constructor
*/
function RunFromFileSystemEnv() {}
/**
* Sets the visibility of a given multi line text box.
* @param panel - the current build step container
* @param name the textbox name
* @param visible - the visible boolean state to be aquired
*/
RunFromFileSystemEnv.setMultiLineTextBoxVisibility = function(panel, name, visible) {
var textBox = panel.querySelector(`input[name="${name}"], textarea[name="${name}"]`);
let parent = textBox.parentElement.closest(".jenkins-form-item");
parent.style.display = visible ? "" : "none";
};
RunFromFileSystemEnv.setInputVisibility = function(panel, name, visible) {
var textBox = panel.querySelector(`input[name="${name}"]`);
Utils.setJenkinsElementVisibility(textBox,visible);
};
RunFromFileSystemEnv.setFsTestsVisibility = function(panel, visible) {
this.setMultiLineTextBoxVisibility(panel, "fsTests", visible);
};
RunFromFileSystemEnv.setFsReportPathVisibility = function(panel, visible) {
this.setInputVisibility(panel, "fsReportPath", visible);
};
RunFromFileSystemEnv.setTimeoutVisibility = function (panel, visible) {
this.setInputVisibility(panel, "fsTimeout", visible);
};
RunFromFileSystemEnv.setParamsVisibility = function(panel, visible) {
this.setInputVisibility(panel, "areParametersEnabled", visible);
};
/**
* Hide/Show the corresponding controls based on the parallel runner checkBox state.
*/
function setViewVisibility(panel) {
const chkParallelRunner = panel.querySelector("input[type=checkbox][name=isParallelRunnerEnabled]");
updateFsView(panel, chkParallelRunner);
chkParallelRunner.addEventListener('click', () => {
updateFsView(panel, chkParallelRunner);
}, false);
}
function updateFsView(panel, chkParallelRunner) {
const isParallelRun = chkParallelRunner.checked;
RunFromFileSystemEnv.setFsTestsVisibility(panel, !isParallelRun);
RunFromFileSystemEnv.setTimeoutVisibility(panel, !isParallelRun);
RunFromFileSystemEnv.setParamsVisibility(panel, !isParallelRun);
//this panel should be automatically shown/hidden, so comment-out it for now to see if all works fine
//ParallelRunnerEnv.setEnvironmentsVisibility(panel, isParallelRun);
}
function setupFsTask(hasConfigPermission) {
let divMain = null;
if (document.location.href.indexOf("pipeline-syntax")>0) { // we are on pipeline-syntax page, where runFromFileBuilder step can be selected only once
divMain = document;
} else if (document.currentScript) { // this block is used for non-IE browsers, for the first FS build step only, it finds very fast the parent DIV
divMain = document.currentScript.parentElement.closest(RUN_FROM_FS_BUILDER_SELECTOR);
}
setTimeout(function() {
prepareFsTask(divMain, hasConfigPermission)}, 100);
}
function prepareFsTask(divMain, hasConfigPermission) {
if (divMain == null) { // this block is needed for IE, but also for non-IE browsers when adding more than one FS build step
let divs = document.querySelectorAll(RUN_FROM_FS_BUILDER_SELECTOR);
divMain = divs[divs.length - 1];
}
if (hasConfigPermission) {
setViewVisibility(divMain);
} else {
const chkParallelRunner = divMain.querySelector("input[type=checkbox][name=isParallelRunnerEnabled]");
if (chkParallelRunner.checked) {
const buttons = chkParallelRunner.closest("div.optionalBlock-container").querySelectorAll("input[type=button], button");
buttons.forEach(function (btn) {
if (!btn.disabled) {
btn.disabled = true;
btn.style.cursor = "not-allowed";
btn.style.pointerEvents = "auto";
}
});
}
}
}