diff --git a/module/testtask/control.php b/module/testtask/control.php
index 6057dc98b6..6ecd39c93b 100644
--- a/module/testtask/control.php
+++ b/module/testtask/control.php
@@ -1288,6 +1288,7 @@ class testtask extends control
$nextCase['version'] = $preAndNext->next->version;
}
+ $this->view->title = $this->lang->testtask->lblRunCase;
$this->view->run = $run;
$this->view->preCase = $preCase;
$this->view->nextCase = $nextCase;
@@ -1441,6 +1442,7 @@ class testtask extends control
$results = $this->testtask->getResults(0, $caseID, $status);
}
+ if(empty($this->testcase)) $this->app->loadLang('testcase');
$this->view->case = $case;
$this->view->runID = $runID;
$this->view->results = $results;
diff --git a/module/testtask/js/common.ui.js b/module/testtask/js/common.ui.js
index c9d8cd6618..87fb8df9d2 100644
--- a/module/testtask/js/common.ui.js
+++ b/module/testtask/js/common.ui.js
@@ -64,3 +64,89 @@ function suitEndDate()
endDate = $.zui.formatDate(convertStringToDate(beginDate).addDays(1), 'yyyy-MM-dd');
$('#end').val(endDate);
}
+
+/**
+ * Create bug from fail case.
+ *
+ * @param event $event
+ * @access public
+ * @return void
+ */
+function createBug(event)
+{
+ var $form = $(event.target).closest('form');
+ var stepIdList = '';
+ $form.find('.step .step-id input[type="checkbox"]:checked').each(function()
+ {
+ if($(this).prop('checked')) stepIdList += $(this).val() + '_';
+ });
+
+ $form.attr('action', $.createLink('bug', 'create', bugCreateParams + ',stepIdList=' + stepIdList));
+}
+
+/**
+ * Toggle results show or hide.
+ *
+ * @param event $event
+ * @access public
+ * @return void
+ */
+function toggleShowResults(event)
+{
+ var $target = $(event.target).closest('tr');
+ if($target.data('status') == 'running') return;
+ $target.toggleClass('show-detail');
+ var show = $target.hasClass('show-detail');
+ $target.next('.result-detail').toggleClass('hidden', !show);
+ $target.find('.collapse-handle').toggleClass('icon-angle-down', !show).toggleClass('icon-angle-top', show);;
+}
+
+/**
+ * Toggle check all.
+ *
+ * @param event $event
+ * @access public
+ * @return void
+ */
+function toggleCheckAll(event)
+{
+ var $checkAll = $(event.target).closest('td').find("input[type='checkbox']");
+ var isChecked = !$checkAll.prop('checked');
+ $checkAll.prop('checked', isChecked);
+
+ $checkAll.closest('tbody').children('tr').find('input[type=checkbox]').prop('checked', isChecked);
+}
+
+/**
+ * Toggle child item check status.
+ *
+ * @param event $event
+ * @access public
+ * @return void
+ */
+function toggleCheckChildItem(event)
+{
+ var $target = $(event.target).closest('td').find("input[type='checkbox']");
+ var isChecked = !$target.prop('checked');
+ $target.prop('checked', isChecked);
+
+ var $next = $target.closest('tr').next();
+
+ while($target.closest('tr').hasClass('step-group') && $next.length && $next.hasClass('step-item'))
+ {
+ $next.find("input[type='checkbox']").prop('checked', isChecked);
+ $next = $next.next();
+ }
+
+ if($target.closest('tr').hasClass('step-item'))
+ {
+ var parentStepKey = $target.closest('tr').data('parent');
+ var allSiblings = $target.closest('tbody').find('.step-item.group-' + parentStepKey).length
+ var checkedSiblings = $target.closest('tbody').find('.step-item.group-' + parentStepKey + ' input[type=checkbox]:checked').length
+
+ $target.closest('tr').prevAll('.step-group').first().find('input[type=checkbox]').prop('checked', allSiblings == checkedSiblings);
+ }
+
+ var $tbody = $target.closest('tbody');
+ $tbody.find('.check-all input[type=checkbox]').prop('checked', $tbody.find('.check-item input[type=checkbox]').length == $tbody.find('.check-item input[type=checkbox]:checked').length);
+}
diff --git a/module/testtask/js/results.ui.js b/module/testtask/js/results.ui.js
new file mode 100644
index 0000000000..f50e423c31
--- /dev/null
+++ b/module/testtask/js/results.ui.js
@@ -0,0 +1,34 @@
+$(function()
+{
+ if($('tr').length == 0) return false;
+
+ if($('tr').first().data('status') == 'ready')
+ {
+ $('tr').first().trigger('click');
+ }
+ else
+ {
+ var times = 0;
+ var id = $('tr').first().data('id')
+ var link = $.createLink('testtask', 'ajaxGetResult', 'resultID=' + id);
+
+ var resultInterval = setInterval(() => {
+ times++;
+ if(times > 600)
+ {
+ clearInterval(resultInterval);
+ }
+
+ $.get(link, function(task)
+ {
+ task = JSON.parse(task);
+ task = task.data;
+ if(task.ZTFResult != '')
+ {
+ clearInterval(resultInterval);
+ loadPage();
+ }
+ });
+ }, 1000);
+ }
+});
diff --git a/module/testtask/lang/de.php b/module/testtask/lang/de.php
index 07cf75743f..a60f4edc00 100644
--- a/module/testtask/lang/de.php
+++ b/module/testtask/lang/de.php
@@ -140,7 +140,7 @@ $lang->testtask->browseBySuite = 'Browse by Suite';
$lang->testtask->passAll = 'Alle erfolgreich';
$lang->testtask->pass = 'Erfolgreich';
$lang->testtask->fail = 'Fehlgeschlagen';
-$lang->testtask->showResult = 'Ausführungen %s';
+$lang->testtask->showResult = 'Ausführungen %s';
$lang->testtask->showFail = 'Fehlgeschlagen %s';
$lang->testtask->confirmDelete = 'Möchten Sie dieses Build löschen?';
diff --git a/module/testtask/lang/en.php b/module/testtask/lang/en.php
index 6b49eb1388..f8eba755e4 100644
--- a/module/testtask/lang/en.php
+++ b/module/testtask/lang/en.php
@@ -140,7 +140,7 @@ $lang->testtask->browseBySuite = 'Browse by Suite';
$lang->testtask->passAll = 'Pass All';
$lang->testtask->pass = 'Pass';
$lang->testtask->fail = 'Failed';
-$lang->testtask->showResult = 'Run %s times';
+$lang->testtask->showResult = 'Run %s times';
$lang->testtask->showFail = 'Failed %s times';
$lang->testtask->confirmDelete = 'Do you want to delete this build?';
diff --git a/module/testtask/lang/fr.php b/module/testtask/lang/fr.php
index a052dbf563..86a147de48 100644
--- a/module/testtask/lang/fr.php
+++ b/module/testtask/lang/fr.php
@@ -140,7 +140,7 @@ $lang->testtask->browseBySuite = 'Browse by Suite';
$lang->testtask->passAll = 'Tout Réussir';
$lang->testtask->pass = 'Réussite';
$lang->testtask->fail = 'Echec';
-$lang->testtask->showResult = 'Jouée %s fois';
+$lang->testtask->showResult = 'Jouée %s fois';
$lang->testtask->showFail = 'Echouée %s fois';
$lang->testtask->confirmDelete = 'Voulez-vous supprimer ce build ?';
diff --git a/module/testtask/lang/zh-cn.php b/module/testtask/lang/zh-cn.php
index b0988f2808..8112270692 100644
--- a/module/testtask/lang/zh-cn.php
+++ b/module/testtask/lang/zh-cn.php
@@ -140,7 +140,7 @@ $lang->testtask->browseBySuite = '按套件查看';
$lang->testtask->passAll = '全部通过';
$lang->testtask->pass = '通过';
$lang->testtask->fail = '失败';
-$lang->testtask->showResult = '共执行%s次';
+$lang->testtask->showResult = '共执行%s次';
$lang->testtask->showFail = '失败%s次';
$lang->testtask->confirmDelete = '您确认要删除该测试单吗?';
@@ -162,7 +162,7 @@ $lang->testtask->allCases = '全部用例';
$lang->testtask->lblCases = '用例列表';
$lang->testtask->lblUnlinkCase = '移除用例';
$lang->testtask->lblRunCase = '执行用例';
-$lang->testtask->lblResults = '执行结果';
+$lang->testtask->lblResults = '用例执行结果';
$lang->testtask->placeholder = new stdclass();
$lang->testtask->placeholder->begin = '开始日期';
diff --git a/module/testtask/ui/results.html.php b/module/testtask/ui/results.html.php
new file mode 100644
index 0000000000..cfc86d5bb5
--- /dev/null
+++ b/module/testtask/ui/results.html.php
@@ -0,0 +1,364 @@
+
+ * @package testcase
+ * @link https://www.zentao.net
+ */
+namespace zin;
+
+jsVar('tab', $app->tab);
+
+modalHeader
+(
+ set::title($lang->testtask->lblResults),
+ set::entityText($case->title),
+ set::entityID($case->id),
+);
+
+div
+(
+ setClass('border-b'),
+ div
+ (
+ h::strong($lang->testcase->precondition),
+ ),
+ div
+ (
+ setClass('leading-6 h-6 my-2'),
+ nl2br($case->precondition),
+ ),
+);
+
+$count = count($results);
+$trs = array();
+$trCount = 1;
+$failCount = 0;
+foreach($results as $i => $result)
+{
+ $class = ($result->caseResult == 'pass' ? 'success' : ($result->caseResult == 'fail' ? 'danger' : ($result->caseResult == 'blocked' ? 'warning' : '')));
+ $fileCount = '(' . count($result->files) . ')';
+ if($class != 'success') $failCount ++;
+ $trs[] = h::tr
+ (
+ setClass("result-item result{$class}"),
+ on::click('toggleShowResults'),
+ set('data-id', $result->id),
+ set('data-status', $result->node > 0 && empty($result->ZTFResult) ? 'running': 'ready'),
+ h::td
+ (
+ width('120px'),
+ "#{$result->id}"
+ ),
+ h::td
+ (
+ width('180px'),
+ $result->date
+ ),
+ $result->node > 0 ? h::td
+ (
+ sprintf($lang->testtask->runNode, zget($users, $result->lastRunner), $result->nodeName, $lang->testtask->runCase),
+ span
+ (
+ setClass('label'),
+ $lang->testtask->auto
+ ),
+ ) : h::td
+ (
+ zget($users, $result->lastRunner) . ' ' . $lang->testtask->runCase,
+ ),
+ h::td
+ (
+ width('150px'),
+ zget($builds, $result->build, ''),
+ ),
+ h::td
+ (
+ setClass('text-right'),
+ width('60px'),
+ $result->node == 0 || !empty($result->ZTFResult) ? h::strong
+ (
+ setClass("result-testcase status-{$result->caseResult}"),
+ $lang->testcase->resultList[$result->caseResult],
+ ) : h::strong
+ (
+ setClass('text-waring'),
+ $lang->testtask->running,
+ ),
+ ),
+ h::td
+ (
+ width('60px'),
+ !empty($result->files) ? h::a
+ (
+ set::href("#caseResult{$result->id}"),
+ set('data-toggle', 'modal'),
+ $lang->files . $fileCount,
+ ) : '',
+ ),
+ h::td
+ (
+ setClass('text-center'),
+ width('50px'),
+ icon
+ (
+ setClass('collapse-handle'),
+ 'angle-down'
+ ),
+ ),
+ );
+
+ $stepID = $childID = 0;
+ $stepResultTrs = array();
+ foreach($result->stepResults as $key => $stepResult)
+ {
+ if(empty($stepResult['type'])) $stepResult['type'] = 'step';
+ if(empty($stepResult['parent'])) $stepResult['parent'] = 0;
+ if($stepResult['type'] == 'group' || $stepResult['type'] == 'step')
+ {
+ $stepID ++;
+ $childID = 0;
+ }
+
+ $stepClass = $stepResult['type'] == 'item' ? "step-item group-{$stepResult['parent']}" : "step-group";
+ $inputName = $stepResult['type'] != 'group' ? 'stepIdList[]' : '';
+
+ $itemTds = array();
+ if($stepResult['type'] != 'group')
+ {
+ $modalID = $result->id . '-' . $key;
+ $fileCount = '(' . count($stepResult['files']) . ')';
+
+ $itemTds[] = h::td
+ (
+ setClass('text-left'),
+ isset($stepResult['expect']) ? html(nl2br($stepResult['expect'])) : '',
+ );
+ $itemTds[] = h::td(isset($result->version) ? html($result->version) : '');
+ $itemTds[] = !empty($stepResult['result']) ? h::td
+ (
+ setClass("status-{$stepResult['result']} text-center"),
+ $lang->testcase->resultList[$stepResult['result']],
+ ) : h::td();
+ $itemTds[] = !empty($stepResult['result']) ? h::td
+ (
+ nl2br($stepResult['real']),
+ ) : h::td();
+ $itemTds[] = !empty($stepResult['result']) ? h::td
+ (
+ setClass('text-center'),
+ !empty($stepResult['files']) ? h::a
+ (
+ set::href("#caseResult{$modalID}"),
+ set('data-toggle', 'modal'),
+ $lang->files . $fileCount,
+ ) : '',
+ ) : h::td();
+ }
+
+ $stepResultTrs[] = h::tr
+ (
+ setClass("step {$stepClass}"),
+ set('data-parent', $stepResult['parent']),
+ set('data-id', $key),
+ h::td
+ (
+ setClass('step-id'),
+ $result->caseResult == 'fail' ? div
+ (
+ setClass('checkbox-primary check-item'),
+ on::click('toggleCheckChildItem'),
+ h::input
+ (
+ set::id($inputName),
+ set('type', 'checkbox'),
+ set('name', $inputName),
+ set('value', $key),
+ ),
+ h::label($stepClass == 'step-group' ? $stepID : ''),
+ ) : $stepID,
+ ),
+ h::td
+ (
+ setClass('text-left'),
+ $stepResult['type'] == 'group' ? set('colspan', '6') : '',
+ div
+ (
+ setClass('inputGroup'),
+ $stepResult['type'] == 'item' ? h::span
+ (
+ setClass('step-item-id mr-2'),
+ "{$stepID}.{$childID}",
+ ) : '',
+ isset($stepResult['desc']) ? html(nl2br($stepResult['desc'])) : '',
+ ),
+ ),
+ $itemTds,
+ );
+ $childID ++;
+ }
+ $stepResultTrs[] = $result->caseResult == 'fail' && common::hasPriv('testcase', 'createBug') ? h::tr
+ (
+ h::td
+ (
+ set('colspan', '2'),
+ div
+ (
+ setClass('checkbox-primary check-all'),
+ on::click('toggleCheckAll'),
+ h::input
+ (
+ set::id("checkAll[{$i}]"),
+ set('type', 'checkbox'),
+ set('name', 'checkAll'),
+ ),
+ h::label($lang->selectAll),
+ ),
+ ),
+ h::td
+ (
+ set('colspan', '5'),
+ ),
+ h::td
+ (
+ setClass('to-bug-button'),
+ btn
+ (
+ setClass('btn'),
+ set::type('primary'),
+ set::btnType('btnType'),
+ on::click('createBug'),
+ set('data-target', '_blank'),
+ set('data-close-modal', true),
+ $lang->testcase->createBug
+ ),
+ ),
+ ) : '';
+ $stepResultTrs[] = !empty($result->ZTFResult) && $result->node > 0 ? h::td
+ (
+ set('colspan', '6'),
+ h::p($lang->testtask->runningLog),
+ h::p($result->ZTFResult),
+ ) : '';
+
+ $projectParam = $this->app->tab == 'project' ? "projectID={$this->session->project}," : '';
+ $executionParam = $this->app->tab == 'execution' ? "executionID={$this->session->execution}" : "";
+ $executionParam = isset($testtask) ? "executionID={$testtask->execution}" : $executionParam;
+ $params = isset($testtask) ? ",testtask={$testtask->id}" : "";
+ $params = $params . ',buildID=' . (isset($testtask->build) ? $testtask->build : $result->build);
+ $linkParams = "product={$case->product}&branch={$case->branch}&extras={$projectParam}caseID={$case->id},version={$case->version},resultID={$result->id},runID={$result->run}{$params}";
+ if($executionParam) $params .= ',' . $executionParam;
+ $trs[] = h::tr
+ (
+ set::id('tr-detail_' . $trCount ++),
+ setClass('result-detail hidden'),
+ h::td
+ (
+ setClass('pd-0'),
+ set('colspan', '7'),
+ h::form
+ (
+ setClass('form load-indicator form-ajax form-grid'),
+ set('data-params', $linkParams),
+ set('action', createLink('bug', 'create', $linkParams)),
+ h::table
+ (
+ setClass('table condensed resultSteps'),
+ h::thead
+ (
+ h::tr
+ (
+ h::td
+ (
+ width('60px'),
+ $lang->testcase->stepID,
+ ),
+ h::td
+ (
+ setClass('text-left'),
+ $lang->testcase->stepDesc,
+ ),
+ h::td
+ (
+ width('25%'),
+ setClass('text-left'),
+ $lang->testcase->stepExpect,
+ ),
+ h::td
+ (
+ width('5%'),
+ setClass('text-left'),
+ $lang->testcase->stepVersion,
+ ),
+ h::td
+ (
+ width('80px'),
+ setClass('text-center'),
+ $lang->testcase->result,
+ ),
+ h::td
+ (
+ width('20%'),
+ setClass('text-left'),
+ $lang->testcase->real,
+ ),
+ h::td
+ (
+ width('80px'),
+ ),
+ ),
+ ),
+ h::tbody
+ (
+ $stepResultTrs,
+ ),
+ ),
+ modal
+ (
+ set::id("stepResult{$result->id}-{$stepID}"),
+ set::title($lang->files),
+ !empty($stepResult['files']) ? fileList(set::files($stepResult['files'])) : '',
+ ),
+ ),
+ ),
+ );
+}
+
+div
+(
+ div
+ (
+ setClass('main'),
+ set::id('casesResults'),
+ h::table
+ (
+ setClass('table condensed table-hover border'),
+ $case->auto != 'unit' ? h::caption
+ (
+ setClass('text-left bg-lighter leading-8 px-3 border'),
+ h::strong
+ (
+ $lang->testcase->result,
+ h::span
+ (
+ setClass('ml-4'),
+ html(sprintf($lang->testtask->showResult, $count)),
+ ),
+ h::span
+ (
+ setClass('ml-2'),
+ html(sprintf($lang->testtask->showFail, $failCount)),
+ ),
+ ),
+ ) : '',
+ $trs,
+ ),
+ ),
+);
+jsVar('bugCreateParams', $linkParams);
+
+render();
+