Name

global.ReportUITestProgress

Description

Used from the Automated Testing Framework plugin

Script

function TestResultResponse(status, message) {
  this.status = status;
  this.message = message;
}
var GLIDE_SYSTEM_FORMAT_FOR_ATF = "yyyy-MM-dd HH:mm:ss";

var ReportUITestProgress = Class.create();
ReportUITestProgress.prototype = Object.extendsObject(AbstractAjaxProcessor, {

  reportStepProgress: function() {
  	var batch_tracker_sys_id = this.getParameter('sysparm_batch_execution_tracker_sys_id');
  	var batch_length = this.getParameter('sysparm_batch_length');
  	var next_step_index = this.getParameter('sysparm_next_step_index');
  	var step_result = this.getParameter('sysparm_step_result');
  	var test_result_id = this.getParameter('sysparm_test_result_sys_id');
  	var atfAgentSysId = this.getParameter('sysparm_atf_agent_sys_id');
  	var stepToCheckForPausing = this.getParameter('sysparm_step_to_check_for_pausing');

  	gs.debug("Reporting step progress");
  	gs.debug("batch_tracker_sys_id " + batch_tracker_sys_id);
  	gs.debug("next_step_index " + next_step_index);
  	gs.debug("batch_length " + batch_length);

  	new ClientTestRunnerAjax().updateHeartbeatForATFAgent(atfAgentSysId);

  	// Init response
  	var response = {
  		cancel_request_received: false,
  		report_step_progress_success: false,
  		should_pause: false
  	};

  	// Check if the batch tracker received a cancel request
  	if (sn_atf.ATFTrackerUtil.batchTrackerReceivesCancelRequest(batch_tracker_sys_id)) {
  		response.cancel_request_received = true;
  		return JSON.stringify(response);
  	}

  	// Check if the test should pause or not. If stepToCheckForPausing is null then it's the end of the batch and only
  	// the step_over flag is checked. This allows the user to step over the last step and pause at the end of the batch
  	response.should_pause = sn_atf.ATFBreakpoint.shouldPauseAtStep(test_result_id, stepToCheckForPausing);

  	// Parse the step result
  	var stepResult = this._parseJSON(step_result);

  	// Find the step result record and make sure it's valid
  	var stepResultGR = this._findResultItemRecord(test_result_id, stepResult.sys_atf_step_sys_id);
  	if (gs.nil(stepResultGR) || !stepResultGR.isValidRecord()) {
  		gs.warn("Unable to find item record with: result sys_id: " + test_result_id + ", step sys_id: " + stepResult.sys_atf_step_sys_id);
  		return JSON.stringify(response);
  	}

  	// Record is found, populate it and update
  	this._populateTestResultItem(stepResultGR, stepResult, test_result_id, "step_result", stepResult.message);
  	if (!stepResultGR.update()) {
  		gs.warn("failed to update item record with: sys_id: " + stepResultGR.sys_id);
  		return JSON.stringify(response);
  	}

  	// If this point is reached, the step result update was successful
  	response.report_step_progress_success = true;

  	// A step is done, update the tracker to say it is running the next one
  	if (next_step_index <= batch_length)
  		response.report_step_progress_success = sn_atf.ATFTrackerUtil.reportStepProgress(batch_tracker_sys_id, next_step_index, batch_length);

  	return JSON.stringify(response);
  },

  _logAndFormatError: function (message){
  	gs.error("Returning due to error: {0}", message);
  	return JSON.stringify(new TestResultResponse("error", message));
  },

  reportBatchResult: function() {
  	var sysAtfTestResultSysId = this.getParameter('sysparm_test_result_sys_id');
  	var test_result = this.getParameter('sysparm_test_result');
  	var batch_tracker_sys_id = this.getParameter('sysparm_batch_tracker_sys_id');
  	var stepThatTimedOut = this._parseJSON(this.getParameter('sysparm_failed_to_report_step'));
  	var isTestDebugEnabled = sn_atf.AutomatedTestingFramework.isDebugEnabled();

  	var atfAgentSysId = this.getParameter('sysparm_atf_agent_sys_id');
  	new ClientTestRunnerAjax().updateHeartbeatForATFAgent(atfAgentSysId);

  	gs.debug("Processing Batch Result for Test Result sys_id: " + sysAtfTestResultSysId);
  	gs.debug("batch_tracker_sys_id " + batch_tracker_sys_id);

  	if (!sysAtfTestResultSysId)
  		return this._logAndFormatError("missing test_result_sys_id");

  	if (!test_result)
  		return this._logAndFormatError("missing test_result data");

  	// sys_atf_test_result record to update
  	var gr = new GlideRecord("sys_atf_test_result");
  	if (!gr.get(sysAtfTestResultSysId))
  		return this._logAndFormatError("ReportUITestProgress: failed to find sys_atf_test_result record by id: " + sysAtfTestResultSysId);

  	// set test result payload, if debug is enabled.
  	if(isTestDebugEnabled){
  		// This is just a batch so append to the existing value if there is one
  		if (gs.nil(gr.test_result_json))
  			gr.test_result_json = '"frontendTest" : ' + test_result;
  		else
  			gr.test_result_json = gr.test_result_json + ', \n "frontendTest" : ' + test_result;
  	}

  	var testResultObject = this._parseJSON(test_result);

  	// append user agent string if it's unique
  	if (gs.nil(gr.user_agents))
  		gr.user_agents = testResultObject.userAgent;
  	else if (gr.user_agents.indexOf(testResultObject.userAgent) === -1)
  		gr.user_agents = gr.user_agents + ',\n' + testResultObject.userAgent;

  	// clear session_id between batches to indicate this test result is no longer running in this session
  	gr.session_id = '';

  	if (!gr.update())
  		return this._logAndFormatError("failed to update test result");

  	if(!this._saveStepEvents(sysAtfTestResultSysId, testResultObject.stepEvents))
  		return this._logAndFormatError("Failed to create or update one or more result item records.");

  	var isSuccess = (!testResultObject.hasFailure && !testResultObject.hasWarning);
  	var isSuccessWithWarnings = (!testResultObject.hasFailure && testResultObject.hasWarning);

  	gs.debug("testResultObject.isCanceled: " + testResultObject.isCanceled);
  	gs.debug("testResultObject.hasFailure: " + testResultObject.hasFailure);
  	gs.debug("test result is success: " + isSuccess);

  	if (testResultObject.isCanceled)
  		sn_atf.ATFTrackerUtil.cancelTrackerIfIncomplete(batch_tracker_sys_id);
  	else if (isSuccess)
  		sn_atf.ATFTrackerUtil.successTracker(batch_tracker_sys_id);
  	else if (isSuccessWithWarnings)
  		sn_atf.ATFTrackerUtil.successWithWarningsTracker(batch_tracker_sys_id);
  	else
  		sn_atf.ATFTrackerUtil.failTracker(batch_tracker_sys_id);

  	if (stepThatTimedOut !== null)
  		this._updateStepThatFailedToReportToFailure(sysAtfTestResultSysId, stepThatTimedOut);

  	return JSON.stringify(new TestResultResponse("success", sysAtfTestResultSysId));
  },

  reportBatchConsoleLogs: function() {
  	var sysAtfTestResultSysId = this.getParameter('sysparm_test_result_sys_id');
  	var console_logs = this.getParameter('sysparm_console_logs');
  	var atfAgentSysId = this.getParameter('sysparm_atf_agent_sys_id');
  	var batchTrackerSysId = this.getParameter('sysparm_batch_tracker_sys_id');
  	var isTestDebugEnabled = sn_atf.AutomatedTestingFramework.isDebugEnabled();

  	new ClientTestRunnerAjax().updateHeartbeatForATFAgent(atfAgentSysId);

  	gs.debug("Processing Batch Console Logs for Test Result sys_id: " + sysAtfTestResultSysId);
  	gs.debug("batch_tracker_sys_id " + batchTrackerSysId);

  	if (!sysAtfTestResultSysId)
  		return this._logAndFormatError("missing test_result_sys_id");

  	if (!console_logs)
  		return this._logAndFormatError("missing console_logs data");

  	var gr = new GlideRecord("sys_atf_test_result");
  	if (!gr.get(sysAtfTestResultSysId))
  		return this._logAndFormatError("ReportUITestProgress: failed to find sys_atf_test_result record by id: " + sysAtfTestResultSysId);

  	if (isTestDebugEnabled) {
  		if (gs.nil(gr.test_result_json))
  			gr.test_result_json = '"consoleLogs" : ' + console_logs;
  		else
  			gr.test_result_json = gr.test_result_json + ', \n "consoleLogs" : ' + console_logs;
  	}

  	if (!gr.update())
  		return this._logAndFormatError("failed to update test result");

  	var consoleLogsObject = this._parseJSON(console_logs);
  	if (!this._saveStepEvents(sysAtfTestResultSysId, consoleLogsObject))
  		return this._logAndFormatError("Failed to create or update one or more result item records.");

  	return JSON.stringify(new TestResultResponse("success", sysAtfTestResultSysId));
  },

  _findResultItemRecord: function(sysAtfTestResultSysId, stepSysId) {
  	var testResultItemGR = new GlideRecord("sys_atf_test_result_step");
  	testResultItemGR.addQuery("test_result", sysAtfTestResultSysId);
  	testResultItemGR.addQuery("step", stepSysId);
  	testResultItemGR.query();
  	if(!testResultItemGR.next())
  		return null;

  	return testResultItemGR;
  },

  _updateStepThatFailedToReportToFailure: function(testResultSysId, step) {
  	var itemGR = this._findResultItemRecord(testResultSysId, step.sys_atf_step_sys_id);
  	itemGR.summary = step.message;
  	itemGR.status = "failure";
  	itemGR.recorded_at = GlideCounter.next("sys_atf_step_result");

  	var gdtStartTimeEvent = new GlideDateTime();
  	var gdtEndTimeEvent = new GlideDateTime();

  	gdtStartTimeEvent.setValueUTC(step.start_time, GLIDE_SYSTEM_FORMAT_FOR_ATF);
  	itemGR.setValue("start_time", gdtStartTimeEvent.getValue());
  	itemGR.setValue("end_time", gdtEndTimeEvent.getValue());

  	var duration = GlideDateTime.subtract(gdtStartTimeEvent, gdtEndTimeEvent);
  	itemGR.setValue("run_time", duration.getDurationValue());

  	if (!itemGR.update())
  		gs.warn("Failed to update item record with: sys_id: " + itemGR.sys_id);
  },

  /**
   * persist all events of an event type that occurred during the current step
   */
  _saveStepEvents: function(sysAtfTestResultSysId, /*StepEvent*/ items) {
  	var success = true;
  	for(var i=0; i < items.length; i++){
  		var item = items[i];
  		// If we're just logging a step completion, only update the recorded_at for the step result
  		if("step_completion".equals(item.type)) {
  			this._updateRecordedAtForStepResult(sysAtfTestResultSysId, item.sys_atf_step_sys_id);
  			continue;
  		}

  		var itemEventGR = new GlideRecord("sys_atf_test_result_item");
  		this._populateTestResultItem(itemEventGR, item, sysAtfTestResultSysId, item.type, item.object, item.whitelisted_client_error, item.step_id, item.browser);

  		if(!itemEventGR.insert()){
  			gs.warn("failed to update item record for alert: " + item.object);
  			success = false;
  		}
  	}
  	return success;
  },
  
  
  _updateRecordedAtForStepResult: function(sysAtfTestResultSysId, sysAtfStepSysId){
  	if(GlideStringUtil.nil(sysAtfTestResultSysId) || GlideStringUtil.nil(sysAtfStepSysId) )
  		return;
  	
  	var stepResultGR = this._findResultItemRecord(sysAtfTestResultSysId, sysAtfStepSysId);
  	if(stepResultGR == null)
  		return;
  	
  	stepResultGR.recorded_at = GlideCounter.next("sys_atf_step_result");
  	stepResultGR.update();
  },

  /**
   * populate the test result item record
   * if the record is a client error, include browser information
   */
  _populateTestResultItem: function(itemGR, item, sysAtfTestResultSysId, type, output, whitelistedClientErrorSysId, stepId, userAgent) {
        itemGR.test_result = sysAtfTestResultSysId;
        itemGR.type = type;
        itemGR.output = output;
        itemGR.whitelisted_client_error = whitelistedClientErrorSysId;
        if (stepId)
            itemGR.step = stepId;

  	  if (type == "client_error")
  		 itemGR.description = this._getClientErrorLogDescription(userAgent, item.status);

        if (itemGR.getRecordClassName() == 'sys_atf_test_result_step')
        	this._updateOutputVars(itemGR, item);

        if (GlideStringUtil.notNil(item.status))
            itemGR.status = item.status;

        // set start and end times in UTC (incoming values are UTC, store as UTC)
        var gdtStartTimeEvent = new GlideDateTime();
        var gdtEndTimeEvent = new GlideDateTime();

  	if (!gs.nil(item.start_time)) {
        gdtStartTimeEvent.setValueUTC(item.start_time, GLIDE_SYSTEM_FORMAT_FOR_ATF);
        itemGR.setValue("start_time", gdtStartTimeEvent.getValue());
  	}
  
  	if (!gs.nil(item.end_time)) {
        gdtEndTimeEvent.setValueUTC(item.end_time, GLIDE_SYSTEM_FORMAT_FOR_ATF);
        itemGR.setValue("end_time", gdtEndTimeEvent.getValue());
  	}

        var duration = GlideDateTime.subtract(gdtStartTimeEvent, gdtEndTimeEvent);
        itemGR.setValue("run_time", duration.getDurationValue());

  	// start_time_millis & end_time_millis format is yyyy-MM-dd HH:mm:ss.SSS
  	if (!gs.nil(item.start_time_millis)) {
        itemGR.setValue("start_time_millis", item.start_time_millis);
  	}
  	if (!gs.nil(item.end_time_millis)) {
        itemGR.setValue("end_time_millis", item.end_time_millis);
  	}
  	if (!gs.nil(item.run_time_millis) ) {
        itemGR.setValue("run_time_millis", item.run_time_millis);
  	}

  	itemGR.recorded_at = GlideCounter.next("sys_atf_step_result");
  },

  _parseJSON: function(objectToParse) {
  	var result = {};
  	try {
  		result = JSON.parse(objectToParse);
  	} catch(e) {
  		gs.error("ReportUITestResult: Error parsing JSON string: " + e);
  		gs.log(e.stack);
  	}

  	return result;
  },

  /**
   * for test logs of type client error, include browser and version information about where the error occurred
   */
  _getClientErrorLogDescription: function(userAgent, status) {
  	var helper = new PickABrowserHelper();
  	var browserInfo = helper.parseUserAgent(userAgent);

  	return gs.getMessage("This client error occurred on the page in {0} {1}", [browserInfo.browserName, browserInfo.version]);
  },

  _updateOutputVars: function(stepResultGR, item) {
  	var outputs = stepResultGR.outputs;
  	var names = outputs.getVariableNames();
      for (var i=0; i<names.length; i++) {
      	if (names[i] in item.outputs) {
      		outputs[names[i]] = item.outputs[names[i]];
      	}
      }
  },

  /**
  * Updates sys_atf_test_result_item record with new parameters
  */
  updateServerErrorRecord: function() {
  	var testResultItem = this.getParameter('sysparm_result_item_sys_id');
  	var status = this.getParameter("sysparm_status");
  	var allowedError = this.getParameter("sysparm_allowed_error");
  	var gr = new GlideRecord("sys_atf_test_result_item");
  	if (gr.get(testResultItem)) {
  		gr.status = status;
  		gr.whitelisted_client_error = allowedError;
  		gr.update();
  	}

  },
  
  type: 'ReportUITestProgress'
});

Sys ID

d144df3097221200abe4bb7503ac4a81

Offical Documentation

Official Docs: