Making attachment mandatory in Test Management 2.0
- Mark as New
- Bookmark
- Subscribe
- Mute
- Subscribe to RSS Feed
- Permalink
- Report Inappropriate Content
08-09-2024 04:32 AM
Hi everyone,
I have a requirement to make attachments mandatory in Test Management 2.0 for test steps that are in state 'Blocked' or 'Failed'.
I have tried various customizations to the macro bellow (test_step_list), but none of them are working:
<?xml version="1.0" encoding="utf-8" ?>
<j:jelly trim="false" xmlns:j="jelly:core" xmlns:g="glide" xmlns:j2="null" xmlns:g2="null">
<g2:doctype name="html" />
<g:inline template="dir_checker.xml" />
<html ng-app="sn.testManagement" lang="${jvar_text_language}" class="${jvar_text_direction}" style="overflow-y:auto;" data-doctype="true" dir="${jvar_text_direction}">
<head>
<meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=no" />
<meta name="apple-mobile-web-app-capable" content="yes" />
<g:inline template="html_page_meta.xml" />
<link href="${gs.getProperty('glide.product.icon')}" rel="shortcut icon" />
<!-- JS -->
<g:inline template="ng_head_inline_script.xml" />
<g:requires name="scripts/js_includes_test_management.js" includes="true" params="r=$[new global.AgileGlobalUtils().getFlushStamp('jsjsjscache')]"/>
<g2:evaluate var="jvar_test_result_count">
var gr = new GlideRecord('sn_test_management_test_run');
gr.get('$[sysparm_test_run_id]');
var testCount = gr.getValue('total_tests');
// Update the run by field to the current user
gr.setValue('run_by', gs.getUserID());
gr.update();
testCount;
</g2:evaluate>
<g2:evaluate var="jvar_test_index">
var index = 0;
var indexValid = false;
var totalCount = parseInt('$[jvar_test_result_count]');
if (!gs.nil('$[sysparm_test_index]')) {
index = parseInt('$[sysparm_test_index]');
indexValid = index ${AMP}lt; totalCount;
}
if (!indexValid) {
var lastVisitedTestResult = TestRun.getLastRunTestByCurrentUser('$[sysparm_test_run_id]');
if (!gs.nil(lastVisitedTestResult))
index = parseInt(lastVisitedTestResult);
indexValid = index ${AMP}lt; totalCount;
if (!indexValid)
index = 0;
}
TestRun.setLastRunTestByCurrentUser('$[sysparm_test_run_id]', index);
index;
</g2:evaluate>
<g2:evaluate var="jvar_test_result" object="true">
var index = parseInt('$[jvar_test_index]');
var testResult;
var gr = new GlideRecord('sn_test_management_test_result');
gr.addQuery('test_run', '$[sysparm_test_run_id]');
gr.orderBy('test_version.test.number');
gr.chooseWindow(index, index + 1, false);
gr.query();
if (gr.next())
testResult = gr;
testResult;
</g2:evaluate>
<g2:evaluate var="jvar_test_steps" object="true">
new TestStepResultService().getStepResults('$[jvar_test_result.getValue('sys_id')]');
</g2:evaluate>
<g2:evaluate var="jvar_test" object="true">
var test = new GlideRecord('sn_test_management_test_version');
test.get('$[jvar_test_result.getValue('test_version')]');
test;
</g2:evaluate>
<g2:evaluate var="jvar_test_position_text">
var position = String(parseInt('$[jvar_test_index]') + 1);
var count = '$[jvar_test_result_count]';
var text = gs.getMessage('{0} out of {1}', [position, count]);
text;
</g2:evaluate>
<g2:evaluate var="jvar_test_case_migrated">
var migrated = false;
var testVersionGr = new GlideRecord('sn_test_management_test_version');
var testCaseGr = new GlideRecord('tm_test_case');
if(testVersionGr.isValidField('tm_test_case') && testCaseGr.get('$[jvar_test.getValue('tm_test_case')]')) {
migrated = testCaseGr.getValue('migrated') === "1" ? true : false;
}
migrated;
</g2:evaluate>
</head>
<body>
<now-message key="File not downloaded" value="${gs.getMessage('The file {0} did not pass security scan and cannot be downloaded.')}"></now-message>
<j2:if test="$[jvar_test_result_count <= 0]">
<div class="test-run-error">
<span class="icon icon-ellipsis"></span>
<span class="test-run-error-message">
$[gs.getMessage('This run contains no tests')]
</span>
</div>
</j2:if>
<j2:if test="$[jvar_test_result_count > 0]">
<script type="text/ng-template" id="pause_test_dialog.html">
<g:inline template="sn_test_management_pause_test_dialog.xml" />
</script>
<script>
(function(){
document.on('click', 'a[data-type="list2_popup"]', function(evt, element) {
var showOpenButton = false;
var trapFocus = false;
var view = "migration";
popListDiv(evt, "tm_test_case", "$[jvar_test.getValue('tm_test_case')]", "migration", 450, showOpenButton, trapFocus);
evt.stop();
});
})();
</script>
<div ng-controller="testExecutionCtrl as txc" ng-cloak="">
<script>
angular.module("sn.testManagement").run(function(testManagementData){
testManagementData.load("testSteps", JSON.parse("$[JS:jvar_test_steps]"));
testManagementData.load("testIndex", "$[JS:jvar_test_index]");
testManagementData.load("testResultCount", "$[JS:jvar_test_result_count]");
});
</script>
<input type="hidden" id="test_run_id" name="test_run_id" value="$[jvar_test_run_id]" />
<nav class="nav navbar-default" >
<a href="#" ng-if="$[jvar_test_case_migrated]" class="btn btn-icon table-btn-lg icon-info list_popup" data-type="list2_popup" data-list_id="tm_test_case" aria-label="${gs.getMessage('Preview record')}" data-use-href="true" data-popover-title-is-html="false" title="" aria-haspop="true" role="button" aria-expanded="false" data-original-title="${gs.getMessage('Preview record')}"></a>
<span uib-tooltip="$[jvar_test.getValue('short_description')]" tooltip-placement="bottom-left" tooltip-append-to-body="true" class="test-description text-ellipsis">$[jvar_test.getValue('short_description')]
</span>
</nav>
<form name="step_list" novalidate="">
<div ng-if="txc.testSteps.length === 0" class="test-run-error">
<span class="icon icon-ellipsis"></span>
<span class="test-run-error-message">
$[gs.getMessage('This test is not available')]
</span>
</div>
<ul ng-if="txc.testSteps.length > 0" class="test_steps">
<li ng-repeat = "step in txc.testSteps">
<div class="flex-box flex-box-v-center padding_test_step">
<div class="step-desc">{{step.step}}</div>
<div class="step-icon-container" ng-if="step.needs_verification == 1" ng-init="txc.setInitialStepResultStatus(step)">
<i role="button" class="step-icon icon-workflow-complete" ng-class="step.status === 'passed' ? 'test-passed' : 'text-light'" alt="" ng-click="txc.setStepStatus(step, 'PASSED')" tabindex="0" uib-tooltip="${HTML: gs.getMessage('Passed')}" tooltip-append-to-body="true" aria-label="${gs.getMessage('Mark as passed')}"/>
<i role="button" class="step-icon icon-workflow-rejected" ng-class="step.status === 'failed' ? 'test-failed' : 'text-light'" alt="" ng-click="txc.setStepStatus(step, 'FAILED')" tabindex="0" uib-tooltip="${HTML: gs.getMessage('Failed')}" tooltip-append-to-body="true" aria-label="${gs.getMessage('Mark as failed')}"/>
<i role="button" class="step-icon icon-workflow-late" ng-class="step.status === 'blocked' ? 'test-blocked' : 'text-light'" alt="" ng-click="txc.setStepStatus(step, 'BLOCKED')" tabindex="0" uib-tooltip="${HTML: gs.getMessage('Blocked')}" tooltip-append-to-body="true" aria-label="${gs.getMessage('Mark as blocked')}"/>
</div>
</div>
<div class="step_comment_container padding_test_step">
<div class="step_comment" uib-collapse="!(step.status == 'blocked')">
<label>${gs.getMessage('Comment')}</label>
<textarea class="form-control" name="{{step.id}}" id="{{step.id}}" ng-if="step.status == 'blocked'" ng-model-options="{allowInvalid: true}" resize-content="" ng-change="txc.updateComment(step)" ng-model="step.comment" aria-label="${gs.getMessage('Comment')}"></textarea>
</div>
<div class="step_comment" uib-collapse="!(step.status == 'failed')">
<label>${gs.getMessage('Comment')}</label>
<textarea class="form-control" name="{{step.id}}" id="{{step.id}}" ng-if="step.status == 'failed'" ng-model-options="{allowInvalid: true}" resize-content="" ng-change="txc.updateComment(step)" ng-model="step.comment" aria-label="${gs.getMessage('Comment')}"></textarea>
</div>
<div class="step_side_line" ng-class="{'blocked': step.status == 'blocked', 'failed': step.status == 'failed'}"></div>
</div>
<div class="step_attachment_container" ng-if="step.status == 'blocked' || step.status == 'failed'">
<div class="add_attachment flex-box flex-box-v-center padding_test_step pull-left" role="button" tabindex="0" upload-file="">
<span class="icon-add-circle-empty flex-box flex-box-v-center flex-box-h-center"></span>
<span>${gs.getMessage('Add Attachment')}</span>
</div>
<div class="clearfix"></div>
<input class="upload" type="file" multiple="" ng-file-select="txc.uploadFile(step, $files)" style="display: none;" />
<ul class="step_attachment_list">
<li ng-repeat="file in step.files track by file.sys_id" class="flex-box flex-box-v-center step_attachment_item_container" ng-click="txc.handleAttachmentClick($event,file)" ng-class="{'infected_file': file.state === 'not_available'}">
<span class="step_attachment_image" ng-if="file.image $[AMP]$[AMP] !file.progress" ng-style="::{'background-image': 'url(' + file.thumbSrc + ')'}" />
<span class="step_attachment_icon" ng-if="!file.image $[AMP]$[AMP] !file.progress" ng-switch="" on="file.ext">
<span ng-switch-when="pdf" class="{{::txc.fileIcons.pdf}}"></span>
<span ng-switch-when="doc" class="{{::txc.fileIcons.doc}}"></span>
<span ng-switch-when="docx" class="{{::txc.fileIcons.doc}}"></span>
<span ng-switch-when="ppt" class="{{::txc.fileIcons.ppt}}"></span>
<span ng-switch-when="txt" class="{{::txc.fileIcons.txt}}"></span>
<span ng-switch-when="xls" class="{{::txc.fileIcons.xls}}"></span>
<span ng-switch-when="zip" class="{{::txc.fileIcons.zip}}"></span>
<span ng-switch-default="" class="icon-document"></span>
</span>
<div class="step_attachment_detail_container flex-box flex-box-v-center" ng-if="!file.progress">
<div class="step_attachment_item_detail flex-box">
<div class="file_name_text flex-ellipsis">
<span title="{{::file.file_name}}">{{::file.file_name}}</span>
<span ng-if="::file.state === 'not_available'">$[SP][${gs.getMessage('Unavailable')}]</span>
</div>
<div class="file_size_text">{{::file.size}}</div>
</div>
<div class="step_attachment_actions">
<div class="step_attachment_icon_container flex-box">
<a ng-if="::file.state !== 'not_available'" ng-click="$event.stopPropagation();" class="btn btn-icon icon-download flex-box flex-box-v-center flex-box-h-center" ng-href="{{txc.getDownloadLink(file)}}" title="${HTML: gs.getMessage('Download')}" role="button"></a>
<span class="btn btn-icon icon-delete flex-box flex-box-v-center flex-box-h-center" title="${gs.getMessage('Delete')}" role="button" ng-click="txc.deleteAttachment($event, step,file,$index)"></span>
</div>
</div>
</div>
<uib-progressbar ng-if="file.progress" value="file.progress"></uib-progressbar>
</li>
</ul>
</div>
</li>
</ul>
<div class='test_step_actions flex-box flex-box-v-center' ng-class="(txc.testResultCount > 1) ? 'flex-box-content-space-between' : 'flex-box-content-flex-end'">
<div class="test_step_position_text" ng-if="txc.testResultCount > 1">
$[jvar_test_position_text]
</div>
<div class='test_step_form_button'>
<button type="button" class="btn btn-default pause-button" ng-click="txc.pauseTestExecution()">${gs.getMessage('Pause')}</button>
<button type="button" class="btn" ng-class="txc.doneButton.classes" ng-click="txc.completeTestExecution($event,step_list, '$[sysparm_test_run_id]')" data-error="${gs.getMessage('Please add comments to failed or blocked steps')}">${gs.getMessage('Done')}</button>
</div>
<div class='test_step_navigate_buttons' ng-if="txc.testResultCount > 1">
<a class="btn btn-default btn-previous-test icon-chevron-left" tabindex="0" role="button" uib-tooltip="${gs.getMessage('Previous Test')}" tooltip-append-to-body="true" tooltip-placement="auto" aria-label="${gs.getMessage('Go to previous test')}" ng-href="{{txc.prevButton.href}}" ng-disabled="txc.prevButton.disabled"></a>
<a class="btn btn-primary btn-next-test icon-chevron-right" tabindex="0" role="button" uib-tooltip="${gs.getMessage('Next Test')}" tooltip-append-to-body="true" tooltip-placement="auto" aria-label="${gs.getMessage('Go to next test')}" ng-href="{{txc.nextButton.href}}" ng-disabled="txc.nextButton.disabled"></a>
</div>
</div>
</form>
</div>
</j2:if>
</body>
</html>
</j:jelly>
Any help or tips will be appreciated. Thanks
- Mark as New
- Bookmark
- Subscribe
- Mute
- Subscribe to RSS Feed
- Permalink
- Report Inappropriate Content
02-25-2025 10:10 PM
Hi,
Did you manage to accomplish this? I am interested to know the solution.
Thanks.
- Mark as New
- Bookmark
- Subscribe
- Mute
- Subscribe to RSS Feed
- Permalink
- Report Inappropriate Content
02-28-2025 06:26 AM
Hi,
Yes I did. I also did some other modifications (to make the comments mandatory for Passed steps) but this is the final version (most of the new logic is the <script> tag in the body (in the div ng-controller="testExecutionCtrl as txc"):
<?xml version="1.0" encoding="UTF-8"?>
<j:jelly xmlns:j="jelly:core" xmlns:g="glide" xmlns:g2="null" xmlns:j2="null" trim="false">
<g2:doctype name="html" />
<g:inline template="dir_checker.xml" />
<html ng-app="sn.testManagement" lang="${jvar_text_language}" class="${jvar_text_direction}" style="overflow-y:auto;" data-doctype="true" dir="${jvar_text_direction}">
<head>
<meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=no" />
<meta name="apple-mobile-web-app-capable" content="yes" />
<g:inline template="html_page_meta.xml" />
<link href="${gs.getProperty('glide.product.icon')}" rel="shortcut icon" />
<!-- JS -->
<g:inline template="ng_head_inline_script.xml" />
<g:requires name="scripts/js_includes_test_management.js" includes="true" params="r=$[new global.AgileGlobalUtils().getFlushStamp('jsjsjscache')]" />
<g2:evaluate var="jvar_test_result_count">var gr = new GlideRecord('sn_test_management_test_run');
gr.get('$[sysparm_test_run_id]');
var testCount = gr.getValue('total_tests');
// Update the run by field to the current user
gr.setValue('run_by', gs.getUserID());
gr.update();
testCount;</g2:evaluate>
<g2:evaluate var="jvar_test_index">var index = 0;
var indexValid = false;
var totalCount = parseInt('$[jvar_test_result_count]');
if (!gs.nil('$[sysparm_test_index]')) {
index = parseInt('$[sysparm_test_index]');
indexValid = index ${AMP}lt; totalCount;
}
if (!indexValid) {
var lastVisitedTestResult = TestRun.getLastRunTestByCurrentUser('$[sysparm_test_run_id]');
if (!gs.nil(lastVisitedTestResult))
index = parseInt(lastVisitedTestResult);
indexValid = index ${AMP}lt; totalCount;
if (!indexValid)
index = 0;
}
TestRun.setLastRunTestByCurrentUser('$[sysparm_test_run_id]', index);
index;</g2:evaluate>
<g2:evaluate var="jvar_test_result" object="true">var index = parseInt('$[jvar_test_index]');
var testResult;
var gr = new GlideRecord('sn_test_management_test_result');
gr.addQuery('test_run', '$[sysparm_test_run_id]');
gr.orderBy('test_version.test.number');
gr.chooseWindow(index, index + 1, false);
gr.query();
if (gr.next())
testResult = gr;
testResult;</g2:evaluate>
<g2:evaluate var="jvar_test_steps" object="true">new TestStepResultService().getStepResults('$[jvar_test_result.getValue('sys_id')]');</g2:evaluate>
<g2:evaluate var="jvar_test" object="true">var test = new GlideRecord('sn_test_management_test_version');
test.get('$[jvar_test_result.getValue('test_version')]');
test;</g2:evaluate>
<g2:evaluate var="jvar_test_position_text">var position = String(parseInt('$[jvar_test_index]') + 1);
var count = '$[jvar_test_result_count]';
var text = gs.getMessage('{0} out of {1}', [position, count]);
text;</g2:evaluate>
<g2:evaluate var="jvar_test_case_migrated">var migrated = false;
var testVersionGr = new GlideRecord('sn_test_management_test_version');
var testCaseGr = new GlideRecord('tm_test_case');
if(testVersionGr.isValidField('tm_test_case') && testCaseGr.get('$[jvar_test.getValue('tm_test_case')]')) {
migrated = testCaseGr.getValue('migrated') === "1" ? true : false;
}
migrated;</g2:evaluate>
</head>
<body>
<now-message key="File not downloaded" value="${gs.getMessage('The file {0} did not pass security scan and cannot be downloaded.')}" />
<j2:if test="$[jvar_test_result_count <= 0]">
<div class="test-run-error">
<span class="icon icon-ellipsis" />
<span class="test-run-error-message">$[gs.getMessage('This run contains no tests')]</span>
</div>
</j2:if>
<j2:if test="$[jvar_test_result_count > 0]">
<script type="text/ng-template" id="pause_test_dialog.html">
<g:inline template="sn_test_management_pause_test_dialog.xml" />
</script>
<script>(function(){
document.on('click', 'a[data-type="list2_popup"]', function(evt, element) {
var showOpenButton = false;
var trapFocus = false;
var view = "migration";
popListDiv(evt, "tm_test_case", "$[jvar_test.getValue('tm_test_case')]", "migration", 450, showOpenButton, trapFocus);
evt.stop();
});
})();</script>
<div ng-controller="testExecutionCtrl as txc" ng-cloak="">
<script>angular.module("sn.testManagement").run(function(testManagementData){
testManagementData.load("testSteps", JSON.parse("$[JS:jvar_test_steps]"));
testManagementData.load("testIndex", "$[JS:jvar_test_index]");
testManagementData.load("testResultCount", "$[JS:jvar_test_result_count]");
checkAttachment = function(event){
var counter = 0;
var stepsLimit = testManagementData.get("testSteps").length;
testManagementData.get("testSteps").forEach(function(step) {
if(step.status === 'passed'){
counter += 1;
if (counter == stepsLimit){
var div = document.getElementById('doneButton');
div.style.display ="inline-block";
var div2 = document.getElementById('testButton');
div2.style.display ="none";
}
}
if (step.status === 'blocked' || step.status === 'failed'){
console.log(typeof(step.files.length));
if (step.files.length > 0) {
var div = document.getElementById('doneButton');
div.style.display ="inline-block";
var div2 = document.getElementById('testButton');
div2.style.display ="none";
}
}
})
}
checkStatus = function(event){
var div = document.getElementById('doneButton');
console.log('CheckStatus style ' + div.style.display);
if (div.style.display == "inline-block")
{
div = document.getElementById('doneButton');
div.style.display ="none";
var div2 = document.getElementById('testButton');
div2.style.display ="inline-block";
}
}
document.getElementById('testButton').addEventListener('click', checkAttachment);
});</script>
<input type="hidden" id="test_run_id" name="test_run_id" value="$[jvar_test_run_id]" />
<nav class="nav navbar-default">
<a href="#" ng-if="$[jvar_test_case_migrated]" class="btn btn-icon table-btn-lg icon-info list_popup" data-type="list2_popup" data-list_id="tm_test_case" aria-label="${gs.getMessage('Preview record')}" data-use-href="true" data-popover-title-is-html="false" title="" aria-haspop="true" role="button" aria-expanded="false" data-original-title="${gs.getMessage('Preview record')}" />
<span uib-tooltip="$[jvar_test.getValue('short_description')]" tooltip-placement="bottom-left" tooltip-append-to-body="true" class="test-description text-ellipsis">$[jvar_test.getValue('short_description')]</span>
</nav>
<form name="step_list" novalidate="">
<div ng-if="txc.testSteps.length === 0" class="test-run-error">
<span class="icon icon-ellipsis" />
<span class="test-run-error-message">$[gs.getMessage('This test is not available')]</span>
</div>
<ul ng-if="txc.testSteps.length > 0" class="test_steps">
<li ng-repeat="step in txc.testSteps">
<div class="flex-box flex-box-v-center padding_test_step">
<div class="step-desc">{{step.step}}</div>
<div class="step-icon-container" ng-if="step.needs_verification == 1" ng-init="txc.setInitialStepResultStatus(step)">
<i role="button" class="step-icon icon-workflow-complete" ng-class="step.status === 'passed' ? 'test-passed' : 'text-light'" alt="" ng-click="txc.setStepStatus(step, 'PASSED')" tabindex="0" uib-tooltip="${HTML: gs.getMessage('Passed')}" tooltip-append-to-body="true" aria-label="${gs.getMessage('Mark as passed')}" />
<i onclick="checkStatus()" role="button" class="step-icon icon-workflow-rejected" ng-class="step.status === 'failed' ? 'test-failed' : 'text-light'" alt="" ng-click="txc.setStepStatus(step, 'FAILED')" tabindex="0" uib-tooltip="${HTML: gs.getMessage('Failed')}" tooltip-append-to-body="true" aria-label="${gs.getMessage('Mark as failed')}" />
<i onclick="checkStatus()" role="button" class="step-icon icon-workflow-late" ng-class="step.status === 'blocked' ? 'test-blocked' : 'text-light'" alt="" ng-click="txc.setStepStatus(step, 'BLOCKED')" tabindex="0" uib-tooltip="${HTML: gs.getMessage('Blocked')}" tooltip-append-to-body="true" aria-label="${gs.getMessage('Mark as blocked')}" />
</div>
</div>
<div class="step_comment_container padding_test_step">
<div class="step_comment" uib-collapse="!(step.status == 'blocked')">
<label>${gs.getMessage('Comment')}</label>
<textarea class="form-control" name="{{step.id}}" id="{{step.id}}" ng-if="step.status == 'blocked'" ng-model-options="{allowInvalid: true}" resize-content="" ng-change="txc.updateComment(step)" ng-model="step.comment" aria-label="${gs.getMessage('Comment')}" />
</div>
<div class="step_comment" uib-collapse="!(step.status == 'failed')">
<label>${gs.getMessage('Comment')}</label>
<textarea class="form-control" name="{{step.id}}" id="{{step.id}}" ng-if="step.status == 'failed'" ng-model-options="{allowInvalid: true}" resize-content="" ng-change="txc.updateComment(step)" ng-model="step.comment" aria-label="${gs.getMessage('Comment')}" />
</div>
<div class="step_side_line" ng-class="{'blocked': step.status == 'blocked', 'failed': step.status == 'failed'}" />
</div>
<div class="step_attachment_container" ng-if="step.status == 'blocked' || step.status == 'failed'">
<div class="add_attachment flex-box flex-box-v-center padding_test_step pull-left" role="button" tabindex="0" upload-file="">
<span class="icon-add-circle-empty flex-box flex-box-v-center flex-box-h-center" />
<span>${gs.getMessage('Add Attachment')}</span>
</div>
<div class="clearfix" />
<input class="upload" type="file" multiple="" ng-file-select="txc.uploadFile(step, $files)" style="display: none;" />
<ul class="step_attachment_list">
<li ng-repeat="file in step.files track by file.sys_id" class="flex-box flex-box-v-center step_attachment_item_container" ng-click="txc.handleAttachmentClick($event,file)" ng-class="{'infected_file': file.state === 'not_available'}">
<span class="step_attachment_image" ng-if="file.image && !file.progress" ng-style="::{'background-image': 'url(' + file.thumbSrc + ')'}" />
<span class="step_attachment_icon" ng-if="!file.image && !file.progress" ng-switch="" on="file.ext">
<span ng-switch-when="pdf" class="{{::txc.fileIcons.pdf}}" />
<span ng-switch-when="doc" class="{{::txc.fileIcons.doc}}" />
<span ng-switch-when="docx" class="{{::txc.fileIcons.doc}}" />
<span ng-switch-when="ppt" class="{{::txc.fileIcons.ppt}}" />
<span ng-switch-when="txt" class="{{::txc.fileIcons.txt}}" />
<span ng-switch-when="xls" class="{{::txc.fileIcons.xls}}" />
<span ng-switch-when="zip" class="{{::txc.fileIcons.zip}}" />
<span ng-switch-default="" class="icon-document" />
</span>
<div class="step_attachment_detail_container flex-box flex-box-v-center" ng-if="!file.progress">
<div class="step_attachment_item_detail flex-box">
<div class="file_name_text flex-ellipsis">
<span title="{{::file.file_name}}">{{::file.file_name}}</span>
<span ng-if="::file.state === 'not_available'">${gs.getMessage('Unavailable')}</span>
</div>
<div class="file_size_text">{{::file.size}}</div>
</div>
<div class="step_attachment_actions">
<div class="step_attachment_icon_container flex-box">
<a ng-if="::file.state !== 'not_available'" ng-click="$event.stopPropagation();" class="btn btn-icon icon-download flex-box flex-box-v-center flex-box-h-center" ng-href="{{txc.getDownloadLink(file)}}" title="${HTML: gs.getMessage('Download')}" role="button" />
<span class="btn btn-icon icon-delete flex-box flex-box-v-center flex-box-h-center" title="${gs.getMessage('Delete')}" role="button" ng-click="txc.deleteAttachment($event, step, file, $index)" />
</div>
</div>
</div>
<uib-progressbar ng-if="file.progress" value="file.progress" />
</li>
</ul>
<span ng-if="step.status == 'blocked' || step.status == 'failed'" class="error-message" style="color: red;" ng-show="!step.files || step.files.length === 0">${gs.getMessage(' Attachment is required for blocked or failed steps.')}</span>
</div>
</li>
</ul>
<div style="margin-top: 20px;">
<p>Sample message</p>
</div>
<div class="test_step_actions flex-box flex-box-v-center" ng-class="(txc.testResultCount > 1) ? 'flex-box-content-space-between' : 'flex-box-content-flex-end'">
<div class="test_step_position_text" ng-if="txc.testResultCount > 1">$[jvar_test_position_text]</div>
<div class="test_step_form_button">
<button type="button" class="btn btn-default pause-button" ng-click="txc.pauseTestExecution()">${gs.getMessage('Pause')}</button>
<button id="doneButton" style="display:none" type="button" class="btn" ng-class="txc.doneButton.classes" ng-click="txc.completeTestExecution($event,step_list, '$[sysparm_test_run_id]')" data-error="${gs.getMessage('Please add comments to failed or blocked steps')}">${gs.getMessage('Done')}</button>
<button id="testButton" type="button" ng-class="txc.doneButton.classes" class="btn">${gs.getMessage('Validate')}</button>
</div>
<div class="test_step_navigate_buttons" ng-if="txc.testResultCount > 1">
<a class="btn btn-default btn-previous-test icon-chevron-left" tabindex="0" role="button" uib-tooltip="${gs.getMessage('Previous Test')}" tooltip-append-to-body="true" tooltip-placement="auto" aria-label="${gs.getMessage('Go to previous test')}" ng-href="{{txc.prevButton.href}}" ng-disabled="txc.prevButton.disabled" />
<a class="btn btn-primary btn-next-test icon-chevron-right" tabindex="0" role="button" uib-tooltip="${gs.getMessage('Next Test')}" tooltip-append-to-body="true" tooltip-placement="auto" aria-label="${gs.getMessage('Go to next test')}" ng-href="{{txc.nextButton.href}}" ng-disabled="txc.nextButton.disabled" />
</div>
</div>
</form>
</div>
</j2:if>
</body>
</html>
</j:jelly>
, line 88):
- Mark as New
- Bookmark
- Subscribe
- Mute
- Subscribe to RSS Feed
- Permalink
- Report Inappropriate Content
02-28-2025 06:34 AM
Hoooollllyyy... talk about over engineering a problem.
Why not just do a BR to check for attachments at that state and return an error message aborting the action if the user has not added an attachment?
- Mark as New
- Bookmark
- Subscribe
- Mute
- Subscribe to RSS Feed
- Permalink
- Report Inappropriate Content
02-28-2025 06:43 AM
We tried but you cannot stop the submission of the form which is done by the angular function txc.completeTestExecution which is not accessible on ServiceNow.