diff --git a/lib/zin/func.php b/lib/zin/func.php index 3ceae7bd3f..249f94b419 100644 --- a/lib/zin/func.php +++ b/lib/zin/func.php @@ -187,3 +187,6 @@ function taskBasicInfo(): taskBasicInfo {return createWg('taskBasicInfo', func_g function taskEffortInfo(): taskEffortInfo {return createWg('taskEffortInfo', func_get_args());} function taskLifeInfo(): taskLifeInfo {return createWg('taskLifeInfo', func_get_args());} function taskMiscInfo(): taskMiscInfo {return createWg('taskMiscInfo', func_get_args());} +function caseBasicInfo(): caseBasicInfo {return createWg('caseBasicInfo', func_get_args());} +function caseTimeInfo(): caseTimeInfo {return createWg('caseTimeInfo', func_get_args());} +function caseRelatedList(): caseRelatedList {return createWg('caseRelatedList', func_get_args());} diff --git a/lib/zin/wg/casebasicinfo/v1.php b/lib/zin/wg/casebasicinfo/v1.php new file mode 100644 index 0000000000..3d47ae9903 --- /dev/null +++ b/lib/zin/wg/casebasicinfo/v1.php @@ -0,0 +1,209 @@ + '?object' // 当前用例。 + ); + + protected function getStage($case) + { + global $lang; + + $caseStage = array(); + if($case->stage) + { + foreach(explode(',', $case->stage) as $stage) + { + if(empty($stage)) continue; + $caseStage[] = div(zget($lang->testcase->stageList, $stage)); + } + } + return div($caseStage); + } + + protected function getModule($case): array + { + global $app; + + $modulePath = $this->prop('modulePath', data('modulePath')); + $caseModule = $this->prop('caseModule', data('caseModule')); + $isLibCase = $this->prop('isLibCase', data('isLibCase')); + $tab = $app->tab; + $moduleItems = array(); + if(!empty($modulePath)) + { + $canBrowseCaselib = hasPriv('caselib', 'browse'); + $canBrowseTestCase = hasPriv('testcase', 'browse'); + $canBrowseProjectTestCase = hasPriv('testcase', 'browse'); + + if($caseModule->branch && isset($branches[$caseModule->branch])) + { + $moduleItems[] = $branches[$caseModule->branch]; + $moduleItems[] = icon('angle-right'); + } + + foreach($modulePath as $key => $module) + { + if($tab == 'qa' || $tab == 'ops') + { + if($isLibCase) + { + $moduleItems[] = $canBrowseCaselib ? a(set::href(createLink('caselib', 'browse', "libID={$case->lib}&browseType=byModule¶m={$module->id}")), $module->name) : $module->name; + } + else + { + $moduleItems[] = $canBrowseTestCase ? a(set::href(createLink('testcase', 'browse', "productID={$case->product}&branch={$module->branch}&browseType=byModule¶m={$module->id}")), $module->name) : $module->name; + } + } + else if($tab == 'project') + { + $moduleItems[] = $canBrowseProjectTestCase ? a(set::href(createLink('project', 'testcase', "projectID={$this->session->project}&productID=$case->product&branch=$module->branch&browseType=byModule¶m=$module->id")), $module->name) : $module->name; + } + else + { + $moduleItems[] = $module->name; + } + if(isset($modulePath[$key + 1])) $moduleItems[] = icon('angle-right'); + } + + } + return empty($modulePath) ? array('/') : $moduleItems; + } + + protected function getFromCase($case): array + { + if(!isset($case->linkCaseTitles)) $case->linkCaseTitles = array(); + + $linkCaseTitles = array(); + foreach($case->linkCaseTitles as $linkCaseID => $linkCaseTitle) + { + $linkCaseTitles[] = a + ( + set::href(createLink('testcase', 'view', "caseID={$linkCaseID}", '', true)), + setData(array('toggle' => 'modal')), + "#{$linkCaseID} {$linkCaseTitle}" + ); + } + return $linkCaseTitles; + } + + protected function getStory($case): array + { + global $app, $lang; + + $tab = $app->tab; + $storyText = isset($case->storyTitle) ? "#{$case->story}:{$case->storyTitle}" : ''; + $param = $tab == 'project' ? "&version=0&projectID={$this->session->project}" : ''; + $story = array(); + $story[] = hasPriv('story', 'view') ? a(set::href(createLink('story', 'view', "storyID={$case->story}{$param}")), setData(array('toggle' => 'modal', 'size' => 'lg')), $storyText) : $storyText; + if($case->story && $case->storyStatus == 'active' && $case->latestStoryVersion > $case->storyVersion) + { + $story[] = span + ( + '(', + $lang->story->changed, + common::hasPriv('testcase', 'confirmStoryChange', $case) ? a + ( + set::href(createLink('testcase', 'confirmStoryChange', "caseID={$case->id}")), + setData('app', $tab), + setClass('mx-1 px-1 primary-pale'), + $lang->confirm + ) : '', + ')' + ); + } + return $story; + } + + protected function getStatus($case): array + { + global $app, $lang; + + $from = $this->prop('from', data('from')); + $taskID = $this->prop('taskID', data('taskID')); + + $status = array(); + $status[] = $app->control->processStatus('testcase', $case); + + if($from == 'testtask' && $case->version > $case->currentVersion) + { + $status[] = span + ( + set('title', $lang->testcase->fromTesttask), + ' (', + $lang->testcase->changed, + hasPriv('testcase', 'confirmchange') ? a(setClass('btn size-xs primary-pale mx-1 ajax-submit'), set::href(createLink('testcase', 'confirmchange', "caseID=$case->id&taskID=$taskID")), $lang->testcase->sync) : '', + ')' + ); + } + + if(isset($case->fromCaseVersion) && $case->fromCaseVersion > $case->version && $from != 'testtask' && !empty($case->product)) + { + $status[] = span + ( + set('title', $lang->testcase->fromCaselib), + ' (', + $lang->testcase->changed, + hasPriv('testcase', 'confirmLibcaseChange') ? a(setClass('btn size-xs primary-pale mx-1 ajax-submit'), set::href(createLink('testcase', 'confirmLibcaseChange', "caseID={$case->id}&libcaseID={$case->fromCaseID}")), $lang->testcase->sync) : '', + hasPriv('testcase', 'ignoreLibcaseChange') ? a(setClass('btn size-xs primary-pale mx-1 ajax-submit'), set::href(createLink('testcase', 'ignoreLibcaseChange', "caseID={$case->id}")), $lang->testcase->ignore) : '', + ')' + ); + } + return $status; + } + + protected function getItems(): array + { + global $lang; + + $case = $this->prop('case', data('case')); + if(!$case) return array(); + + $product = $this->prop('product', data('product')); + $branchName = $this->prop('branchName', data('branchName')); + $libName = $this->prop('libName', data('libName')); + $branchLabel = sprintf($lang->product->branch, $lang->product->branchName[$product->type]); + + $items = array(); + if($isLibCase) + { + $items[$lang->testcase->fromCase] = array('children' => wg($this->getFromCase($case))); + $items[$lang->testcase->lib] = hasPriv('caselib', 'browse') ? array('control' => 'link', 'url' => createLink('caselib', 'browse', "libID={$case->lib}"), 'text' => $libName) : $libName; + } + else + { + if($case->product && !$product->shadow) $items[$lang->testcase->product] = hasPriv('product', 'view') ? array('control' => 'link', 'url' => createLink('product', 'view', "productID={$case->product}"), 'text' => $product->name) : $product->name; + if($case->branch && $product->type != 'normal') $items[$branchLabel] = hasPriv('testcase', 'browse') ? array('control' => 'link', 'url' => createLink('testcase', 'browse', "productID={$case->product}&branch={$case->branch}"), 'text' => $branchName) : $branchName; + $items[$lang->testcase->module] = array('children' => wg($this->getModule($case))); + $items[$lang->testcase->story] = array('children' => wg($this->getStory($case))); + } + + $items[$lang->testcase->type] = zget($lang->case->typeList, $case->type); + $items[$lang->testcase->stage] = array('children' => wg($this->getStage($case))); + $items[$lang->testcase->pri] = array('control' => 'pri', 'pri' => $case->pri, 'text' => $lang->case->priList); + $items[$lang->testcase->status] = array('children' => wg($this->getStatus($case))); + if(!$isLibCase) + { + $items[$lang->testcase->lastRunTime] = !helper::isZeroDate($case->lastRunDate) ? $case->lastRunDate : ''; + $items[$lang->testcase->lastRunResult] = $case->lastRunResult ? $lang->testcase->resultList[$case->lastRunResult] : $lang->testcase->unexecuted; + } + $items[$lang->testcase->keywords] = $case->keywords; + + return $items; + } + + protected function build() + { + return new datalist + ( + set::className('case-basec-info'), + set::items($this->getItems()) + ); + } +} diff --git a/lib/zin/wg/caserelatedlist/v1.php b/lib/zin/wg/caserelatedlist/v1.php new file mode 100644 index 0000000000..ae825b5751 --- /dev/null +++ b/lib/zin/wg/caserelatedlist/v1.php @@ -0,0 +1,48 @@ + '?object' // 当前用例。 + ); + + protected function created() + { + global $app, $lang; + + $case = $this->prop('case', data('case')); + if(!$case) return array(); + + $data = array(); + $data['linkBugs'] = array + ( + 'title' => $lang->testcase->legendLinkBugs, + 'items' => array_filter(array_merge($case->toBugs, array($case->fromBug))), + 'url' => hasPriv('bug', 'view') ? createLink('bug', 'view', 'bugID={id}') : false + ); + + $linkCases = array(); + foreach($case->linkCaseTitles as $caseID => $linkCaseTitle) + { + $linkCase = new \stdclass(); + $linkCase->id = $caseID; + $linkCase->title = $linkCaseTitle; + + $linkCases[] = $linkCase; + } + + $data['linkCases'] = array + ( + 'title' => $lang->testcase->linkCase, + 'items' => $linkCases, + 'url' => hasPriv('testcase', 'view') ? createLink('testcase', 'view', 'caseID={id}') : false + ); + + $this->setProp('data', $data); + } +} diff --git a/lib/zin/wg/casetimeinfo/v1.php b/lib/zin/wg/casetimeinfo/v1.php new file mode 100644 index 0000000000..d4ddbf938f --- /dev/null +++ b/lib/zin/wg/casetimeinfo/v1.php @@ -0,0 +1,52 @@ + '?object', // 当前用例。 + 'users' => '?array' // 用户列表。 + ); + + protected function getReviewedBy($users, $case): string + { + $reviewedBy = ''; + foreach(explode(',', $case->reviewedBy) as $account) + { + $reviewedBy .= ' ' . zget($users, trim($account)); + } + $reviewedBy = trim($reviewedBy); + return $reviewedBy; + } + + protected function getItems(): array + { + global $lang; + + $case = $this->prop('case', data('case')); + if(!$case) return array(); + + $users = $this->prop('users', data('users')); + + $items = array(); + $items[$lang->testcase->openedBy] = zget($users, $case->openedBy) . $lang->at . $case->openedDate; + $items[$lang->testcase->reviewedBy] = $this->getReviewedBy($users, $case); + $items[$lang->testcase->reviewedDate] = !empty($case->reviewedBy) ? $case->reviewedDate : ''; + $items[$lang->testcase->lblLastEdited] = !empty($case->lastEditedBy) ? zget($users, $case->lastEditedBy) . $lang->at . $case->lastEditedDate : ''; + + return $items; + } + + protected function build() + { + return new datalist + ( + set::className('case-time-info'), + set::items($this->getItems()) + ); + } +} diff --git a/module/task/ui/view.html.php b/module/task/ui/view.html.php index 681bc49e7e..ee192cdb20 100644 --- a/module/task/ui/view.html.php +++ b/module/task/ui/view.html.php @@ -12,7 +12,7 @@ namespace zin; include($this->app->getModuleRoot() . 'ai/ui/promptmenu.html.php'); -$isInModal = isInModal(); +$isInModal = isInModal(); /* 初始化头部右上方工具栏。Init detail toolbar. */ $toolbar = array(); diff --git a/module/testcase/config.php b/module/testcase/config.php index f1c4de619a..3f6b0b9b47 100644 --- a/module/testcase/config.php +++ b/module/testcase/config.php @@ -20,8 +20,8 @@ $config->testcase->export->listFields = array('type', 'stage', 'pri', 'status' $config->testcase->actions = new stdclass(); $config->testcase->actions->view = array(); -$config->testcase->actions->view['mainActions'] = array('runResult', 'runCase', 'review', 'importToLib', 'createBug', 'showScript'); -$config->testcase->actions->view['suffixActions'] = array('edit', 'create', 'delete'); +$config->testcase->actions->view['mainActions'] = array('runResult', 'runCase', 'confirmStoryChange', 'review', 'importToLib', 'createBug', 'showScript'); +$config->testcase->actions->view['suffixActions'] = array('edit', 'create', 'createCase', 'delete'); $config->testcase->exportFields = ' id, product, branch, module, story, scene, diff --git a/module/testcase/config/action.php b/module/testcase/config/action.php index a0e40d0e6b..f3b6a4599f 100644 --- a/module/testcase/config/action.php +++ b/module/testcase/config/action.php @@ -91,6 +91,17 @@ $config->testcase->actionList['deleteScene']['hint'] = $lang->testcase->del $config->testcase->actionList['deleteScene']['url'] = array('module' => 'testcase', 'method' => 'deleteScene', 'params' => 'sceneID={id}'); $config->testcase->actionList['deleteScene']['className'] = 'ajax-submit'; +$config->testcase->actionList['confirmStoryChange']['icon'] = 'view'; +$config->testcase->actionList['confirmStoryChange']['text'] = $lang->confirm; +$config->testcase->actionList['confirmStoryChange']['hint'] = $lang->confirm; +$config->testcase->actionList['confirmStoryChange']['url'] = array('module' => 'testcase', 'method' => 'confirmstorychange', 'params' => 'caseID={id}'); +$config->testcase->actionList['confirmStoryChange']['className'] = 'ajax-submit'; + +$config->testcase->actionList['createCase']['icon'] = 'copy'; +$config->testcase->actionList['createCase']['text'] = $lang->testcase->copy; +$config->testcase->actionList['createCase']['hint'] = $lang->testcase->copy; +$config->testcase->actionList['createCase']['url'] = array('module' => 'caselib', 'method' => 'createCase', 'params' => 'libID={lib}&moduleID={module}¶m={id}'); + $config->testcase->actionList['delete']['icon'] = 'trash'; $config->testcase->actionList['delete']['text'] = $lang->testcase->deleteAction; $config->testcase->actionList['delete']['hint'] = $lang->testcase->deleteAction; diff --git a/module/testcase/control.php b/module/testcase/control.php index 37ee8baab1..f33e99a1f6 100644 --- a/module/testcase/control.php +++ b/module/testcase/control.php @@ -447,6 +447,7 @@ class testcase extends control $this->view->branchName = $product->type == 'normal' ? '' : zget($branches, $case->branch, ''); } + $this->view->testcase = $case; $this->view->stepsType = $stepsType; $this->view->version = $version ? $version : $case->version; $this->view->isLibCase = $isLibCase; diff --git a/module/testcase/model.php b/module/testcase/model.php index d886af7578..abe2a565dc 100755 --- a/module/testcase/model.php +++ b/module/testcase/model.php @@ -1133,13 +1133,17 @@ class testcaseModel extends model $action = strtolower($action); - if($action == 'runcase') return $case->auto == 'no' && $case->status != 'wait'; + if($action == 'runcase') return (!$case->lib || !empty($case->product)) && $case->auto == 'no' && $case->status != 'wait'; + if($action == 'runresult') return !$case->lib || !empty($case->product); + if($action == 'importtolib') return !$case->lib || !empty($case->product); if($action == 'ztfrun') return $case->auto == 'auto'; if($action == 'confirmchange') return isset($case->caseStatus) && isset($case->caseVersion) && $case->caseStatus != 'wait' && $case->version < $case->caseVersion; if($action == 'confirmstorychange') return !empty($case->needconfirm) || (isset($case->browseType) && $case->browseType == 'needconfirm'); if($action == 'createbug') return isset($case->caseFails) && $case->caseFails > 0; + if($action == 'create') return !$case->lib || !empty($case->product); if($action == 'review') return ($config->testcase->needReview || !empty($config->testcase->forceReview)) && (isset($case->caseStatus) ? $case->caseStatus == 'wait' : $case->status == 'wait'); if($action == 'showscript') return $case->auto == 'auto'; + if($action == 'createcase') return $case->lib && empty($case->product); return true; } diff --git a/module/testcase/ui/view.html.php b/module/testcase/ui/view.html.php index c0f6a1eee8..d8dc9799d9 100644 --- a/module/testcase/ui/view.html.php +++ b/module/testcase/ui/view.html.php @@ -12,12 +12,34 @@ namespace zin; include($this->app->getModuleRoot() . 'ai/ui/promptmenu.html.php'); -$canCreateCase = hasPriv('testcase', 'create'); - jsVar('viewParams', "caseID={$case->id}&version={$version}&from={$from}&taskID={$taskID}&stepsType="); +$isInModal = isInModal(); + +/* 初始化头部右上方工具栏。Init detail toolbar. */ +$toolbar = array(); +if(!$isInModal && hasPriv('testcase', 'create', $case)) +{ + $toolbar[] = array + ( + 'icon' => 'plus', + 'type' => 'primary', + 'text' => $lang->case->create, + 'url' => createLink('testcase', 'create', "productID={$case->product}&branch={$case->branch}&moduleID={$case->module}") + ); +} + +/* 初始化底部操作栏。Init bottom actions. */ +$actions = $this->loadModel('common')->buildOperateMenu($case); +$actions = array_merge($actions['mainActions'], $actions['suffixActions']); +foreach($actions as $index => $action) +{ + if(!isset($action['url'])) continue; + $actions[$index]['url'] = str_replace('%executionID%', (string)$this->session->execution, $action['url']); +} + $steps = array(); -if($stepsType == 'table') +if(!empty($case->steps) && $stepsType == 'table') { foreach($case->steps as $step) { @@ -25,573 +47,123 @@ if($stepsType == 'table') $stepClass .= count($steps) > 0 && $step->grade == 1 ? ' mt-2' : ' border-t-0'; $steps[] = cell + ( + setClass("step {$stepClass} border align-top flex"), + cell ( - setClass("step {$stepClass} border align-top flex"), - cell + setClass('text-left flex border-r step-id'), + width('1/2'), + span ( - setClass('text-left flex border-r step-id'), - width('1/2'), - span - ( - setClass('pr-2 pl-' . (($step->grade - 1) * 2)), - $step->name - ), - html(nl2br(str_replace(' ', ' ', $step->desc))) + setClass('pr-2 pl-' . (($step->grade - 1) * 2)), + $step->name ), - cell - ( - setClass('text-left flex'), - width('1/2'), - html(nl2br(str_replace(' ', ' ', $step->expect))) - ) - ); - } -} - -/* Get data in legend of basic information. */ -$app->loadLang('product'); -$productLink = $case->product && hasPriv('product', 'view') ? $this->createLink('product', 'view', "productID={$case->product}") : ''; -$branchLink = $case->branch && hasPriv('testcase', 'browse') ? $this->createLink('testcase', 'browse', "productID={$case->product}&branch={$case->branch}") : ''; - -$fromCaseItem = array(); -$libItem = array(); -$productItem = array(); -$branchItem = array(); -$moduleItem = array(); -$storyItem = array(); -$lastRunTimeItem = array(); -$lastRunResultItem = array(); -$linkCaseItem = array(); -$caseStage = array(); -$caseChange = array(); -if($isLibCase) -{ - if(!isset($case->linkCaseTitles)) $case->linkCaseTitles = array(); - - $linkCaseTitles = array(); - foreach($case->linkCaseTitles as $linkCaseID => $linkCaseTitle) - { - $linkCaseTitles[] = a - ( - set::href($this->createLink('testcase', 'view', "caseID={$linkCaseID}", '', true)), - setData(array('toggle' => 'modal')), - "#{$linkCaseID} {$linkCaseTitle}" - ); - } - $fromCaseItem = item - ( - set::name($lang->testcase->fromCase), - $linkCaseTitles - ); - - $libItem = item - ( - set::name($lang->testcase->lib), - hasPriv('caselib', 'browse') ? a(set::href($this->createLink('caselib', 'browse', "libID={$case->lib}")), $libName) : $libName - ); - - $mainActions = array(); - $suffixActions = array(); - if($case->needconfirm) - { - if(hasPriv('testcase', 'confirmstorychange')) $mainActions[] = array('icon' => 'view', 'text' => $lang->confirm, 'hint' => $lang->confirm, 'url' => createLink('testcase', 'confirmstorychange', "caseID={$case->id}"), 'className' => 'ajax-submit'); - } - else - { - if(($this->config->testcase->needReview || !empty($this->config->testcase->forceReview)) && hasPriv('testcase', 'review') && $case->status == 'wait') $mainActions[] = array('icon' => 'glasses', 'text' => $lang->testcase->reviewAB, 'hint' => $lang->testcase->reviewAB, 'url' => createLink('testcase', 'review', "caseID={$case->id}"), 'data-toggle' => 'modal'); - if(!isAjaxRequest('modal')) - { - if(hasPriv('testcase', 'edit')) - { - $editParams = "caseID={$case->id}"; - if($this->app->tab == 'project') $editParams .= "&comment=false&projectID={$this->session->project}"; - if($this->app->tab == 'execution') $editParams .= "&comment=false&executionID={$this->session->execution}"; - $suffixActions[] = array('icon' => 'edit', 'text' => '', 'hint' => $lang->testcase->edit, 'url' => createLink('testcase', 'edit', $editParams)); - } - } - if(hasPriv('caselib', 'createCase')) $mainActions[] = array('icon' => 'copy', 'text' => '', 'hint' => $lang->testcase->copy, 'url' => createLink('caselib', 'createCase', "libID={$case->lib}&moduleID={$case->module}¶m={$case->id}")); - $suffixActions[] = array('icon' => 'trash', 'text' => '', 'hint' => $lang->testcase->delete, 'url' => createLink('testcase', 'delete', "caseID={$case->id}"), 'className' => 'ajax-submit', 'data-confirm' => $lang->testcase->confirmDelete); - } - $actions = array('mainActions' => $mainActions, 'suffixActions' => $suffixActions); -} -else -{ - - $productItem = !$product->shadow ? item - ( - set::name($lang->testcase->product), - a - ( - set::href($productLink), - $product->name - ), - set::collapse($product->name) - ) : null; - - if($product->type != 'normal') - { - $branchItem = item - ( - set::name(sprintf($lang->product->branch, $lang->product->branchName[$product->type])), - a - ( - set::href($branchLink), - $branchName + html(nl2br(str_replace(' ', ' ', $step->desc))) ), - set::collapse($branchName) - ); - } - - $tab = $app->tab; - $moduleItems = array(); - if(!empty($modulePath)) - { - $canBrowseCaselib = hasPriv('caselib', 'browse'); - $canBrowseTestCase = hasPriv('testcase', 'browse'); - $canBrowseProjectTestCase = hasPriv('testcase', 'browse'); - - if($caseModule->branch && isset($branches[$caseModule->branch])) - { - $moduleItems[] = $branches[$caseModule->branch]; - $moduleItems[] = icon('angle-right'); - } - - foreach($modulePath as $key => $module) - { - if($tab == 'qa' || $tab == 'ops') - { - if($isLibCase) - { - $moduleItems[] = $canBrowseCaselib ? a(set::href($this->createLink('caselib', 'browse', "libID={$case->lib}&browseType=byModule¶m={$module->id}")), $module->name) : $module->name; - } - else - { - $moduleItems[] = $canBrowseTestCase ? a(set::href($this->createLink('testcase', 'browse', "productID={$case->product}&branch={$module->branch}&browseType=byModule¶m={$module->id}")), $module->name) : $module->name; - } - } - else if($tab == 'project') - { - $moduleItems[] = $canBrowseProjectTestCase ? a(set::href($this->createLink('project', 'testcase', "projectID={$this->session->project}&productID=$case->product&branch=$module->branch&browseType=byModule¶m=$module->id")), $module->name) : $module->name; - } - else - { - $moduleItems[] = $module->name; - } - if(isset($modulePath[$key + 1])) $moduleItems[] = icon('angle-right'); - } - - } - $moduleItem = item - ( - set::name($lang->testcase->module), - empty($modulePath) ? '/' : $moduleItems - ); - - $param = $tab == 'project' ? "&version=0&projectID={$this->session->project}" : ''; - $confirmStatusChange = ''; - if($case->story && $case->storyStatus == 'active' && $case->latestStoryVersion > $case->storyVersion) - { - $confirmStatusChange = span - ( - '(', - $lang->story->changed, - common::hasPriv('testcase', 'confirmStoryChange', $case) ? a( - set::href($this->createLink('testcase', 'confirmStoryChange', "caseID={$case->id}")), - setData('app', $app->tab), - setClass('mx-1 px-1 primary-pale'), - $lang->confirm - ) : '', - ')' - ); - } - $storyItem = item - ( - set::name($lang->testcase->story), - isset($case->storyTitle) && hasPriv('story', 'view') ? a(set::href($this->createLink('story', 'view', "storyID={$case->story}{$param}")), setData(array('toggle' => 'modal', 'size' => 'lg')), "#{$case->story}:{$case->storyTitle}") : (isset($case->storyTitle) ? "#{$case->story}:{$case->storyTitle}" : ''), set::labelProps(array('data-toggle' => 'modal', 'data-size' => 'lg')), - $confirmStatusChange - ); - - $app->loadLang('testtask'); - $lastRunTimeItem = item - ( - set::name($lang->testtask->lastRunTime), - !helper::isZeroDate($case->lastRunDate) ? $case->lastRunDate : '' - ); - - $lastRunResultItem = item - ( - set::tdClass("result-testcase status-{$case->lastRunResult}"), - set::name($lang->testtask->lastRunResult), - $case->lastRunResult ? $lang->testcase->resultList[$case->lastRunResult] : $lang->testcase->unexecuted - ); - - $linkBugs = array(); - if($case->fromBug) - { - $linkBugs[] = entityLabel - ( - set::href($this->createLink('bug', 'view', "bugID={$case->fromBug}")), - set::level(4), - set::entityID($case->fromBug), - set::text($case->fromBugData->title), - set::labelProps(array('data-toggle' => 'modal', 'data-size' => 'lg')), - set('title', $case->fromBugData->title) - ); - } - if($case->toBugs) - { - foreach($case->toBugs as $bugID => $bug) - { - $linkBugs[] = entityLabel + cell ( - set::href($this->createLink('bug', 'view', "bugID={$bugID}")), - set::level(4), - set::entityID($bugID), - set::text($bug->title), - set::labelProps(array('data-toggle' => 'modal', 'data-size' => 'lg')), - set('title', $bug->title) - ); - } - } - $linkBugItem = item - ( - set::tdClass('linkBugTitles'), - !empty($linkBugs) ? set::collapse(true) : '', - set::name($lang->testcase->legendLinkBugs), - $linkBugs - ); - - $linkCases = array(); - if(isset($case->linkCaseTitles)) - { - foreach($case->linkCaseTitles as $linkCaseID => $linkCaseTitle) - { - $linkCases[] = entityLabel - ( - set::href($this->createLink('testcase', 'view', "caseID={$linkCaseID}")), - set::level(4), - set::entityID($linkCaseID), - set::text($linkCaseTitle), - set::labelProps(array('data-toggle' => 'modal', 'data-size' => 'lg')), - set('title', $linkCaseTitle) - ); - } - } - $linkCaseItem = item - ( - set::tdClass('linkCaseTitles'), - set::name($lang->testcase->linkCase), - !empty($linkCases) ? set::collapse(true) : '', - $linkCases - ); - - $actions = $this->loadModel('common')->buildOperateMenu($case); - foreach($actions as $actionType => $typeActions) - { - foreach($typeActions as $index => $action) - { - if(!isset($action['url'])) continue; - $actions[$actionType][$index]['url'] = str_replace('%executionID%', (string)$this->session->execution, $action['url']); - } + setClass('text-left flex'), + width('1/2'), + html(nl2br(str_replace(' ', ' ', $step->expect))) + ) + ); } } - -if($case->stage) -{ - foreach(explode(',', $case->stage) as $stage) - { - if(empty($stage)) continue; - $caseStage[] = div(zget($lang->testcase->stageList, $stage)); - } - -} - -if($from == 'testtask' && $case->version > $case->currentVersion) -{ - $caseChange[] = span - ( - set('title', $lang->testcase->fromTesttask), - ' (', - $lang->testcase->changed, - hasPriv('testcase', 'confirmchange') ? a(setClass('btn size-xs primary-pale mx-1 ajax-submit'), set::href($this->createLink('testcase', 'confirmchange', "caseID=$case->id&taskID=$taskID")), $lang->testcase->sync) : '', - ')' - ); -} - -if(isset($case->fromCaseVersion) && $case->fromCaseVersion > $case->version && $from != 'testtask' && !empty($case->product)) -{ - $caseChange[] = span - ( - set('title', $lang->testcase->fromCaselib), - ' (', - $lang->testcase->changed, - hasPriv('testcase', 'confirmLibcaseChange') ? a(setClass('btn size-xs primary-pale mx-1 ajax-submit'), set::href($this->createLink('testcase', 'confirmLibcaseChange', "caseID={$case->id}&libcaseID={$case->fromCaseID}")), $lang->testcase->sync) : '', - hasPriv('testcase', 'ignoreLibcaseChange') ? a(setClass('btn size-xs primary-pale mx-1 ajax-submit'), set::href($this->createLink('testcase', 'ignoreLibcaseChange', "caseID={$case->id}")), $lang->testcase->ignore) : '', - ')' - ); -} - -/* Get data in legend of open and edit. */ -$reviewedBy = ''; -foreach(explode(',', $case->reviewedBy) as $account) -{ - $reviewedBy .= ' ' . zget($users, trim($account)); -} -$reviewedBy = trim($reviewedBy); - -$isInModal = isAjaxRequest('modal'); -$versions = array(); -for($i = $case->version; $i >= 1; $i --) -{ - $versionItem = array('text' => "#{$i}", 'url' => inlink('view', "caseID={$case->id}&version={$i}"), 'active' => $i == $version); - if($isInModal) - { - $versionItem['data-load'] = 'modal'; - $versionItem['data-target'] = '.modal-content'; - } - $versions[] = $versionItem; -} -detailHeader +$stepsTable = !empty($case->steps) ? div ( - to::title + $stepsType == 'table' ? div ( - entityLabel + setID('stepsTable'), + div ( - set::entityID($case->id), - set::level(1), - span(setStyle('color', $case->color), $case->title) + setClass('steps-header'), + div + ( + setClass('text-left inline-block steps border'), + width('1/2'), + $lang->testcase->stepDesc + ), + div + ( + setClass('text-left inline-block border border-l-0'), + width('1/2'), + $lang->testcase->stepExpect + ) ), - count($versions) > 1 ? dropdown + div ( - btn(setClass('btn-link'), "#{$version}"), - set::items($versions) - ) : null, - $case->deleted ? span(setClass('label danger'), $lang->case->deleted) : null - ), - $isInModal ? to::prefix('') : null, - !$isInModal ? to::suffix - ( - btn - ( - set::icon('plus'), - set::type('primary'), - set::text($lang->case->create), - $canCreateCase ? set::url($this->createLink('testcase', 'create', "productID={$case->product}&branch={$case->branch}&moduleID={$case->module}")) : null + setClass('steps-body'), + $steps ) - ) : null -); - -detailBody + ) : div + ( + setID('stepsView'), + mindmap + ( + set::data($case->mindMapSteps), + set::readonly(true) + ) + ) +) : div ( - sectionList + setClass('canvas text-center py-2'), + p ( - on::click('.steps-section .step-change-view-btn .icon', 'toggleStepsView'), - section + setClass('py-2 my-2'), + span ( - setClass(empty($case->precondition) ? 'hidden' : ''), - set::title($lang->testcase->precondition), - set::content(nl2br($case->precondition)), - set::useHtml(true) - ), - section - ( - setClass('steps-section'), - set::title($lang->testcase->steps), - !empty($case->steps) ? to::actions - ( - row - ( - setClass('step-change-view ml-2 py-1 border'), - width('fit'), - cell - ( - setClass('px-1.5 leading-4 border-r step-change-view-btn'), - $stepsType == 'table' ? setClass('text-primary') : '', - icon - ( - set::size('9'), - 'table-large' - ) - ), - cell - ( - setClass('px-1.5 leading-4 step-change-view-btn'), - $stepsType != 'table' ? setClass('text-primary') : '', - icon - ( - set::size('9'), - 'tree' - ) - ) - ) - ) : null, - !empty($case->steps) ? div - ( - $stepsType == 'table' ? div - ( - setID('stepsTable'), - div - ( - setClass('steps-header'), - div - ( - setClass('text-left inline-block steps border'), - width('1/2'), - $lang->testcase->stepDesc - ), - div - ( - setClass('text-left inline-block border border-l-0'), - width('1/2'), - $lang->testcase->stepExpect - ) - ), - div - ( - setClass('steps-body'), - $steps - ) - ) : div - ( - setID('stepsView'), - mindmap - ( - set::data($case->mindMapSteps), - set::readonly(true) - ) - ) - ) : div - ( - setClass('canvas text-center py-2'), - p - ( - setClass('py-2 my-2'), - span - ( - setClass('text-gray'), - $lang->noData - ) - ) - ), - set::useHtml(true) - ), - $case->files ? fileList - ( - set::files($case->files), - set::padding(false) - ) : null - ), - history(set::objectID($case->id)), - floatToolbar - ( - set::object($case), - $isInModal ? null : to::prefix(backBtn(set::icon('back'), setClass('ghost text-white'), $lang->goback)), - set::main($actions['mainActions']), - set::suffix($actions['suffixActions']) - ), - detailSide - ( - tabs - ( - set::collapse(true), - tabPane - ( - set::key('legendBasicInfo' . $case->id), - set::title($lang->testcase->legendBasicInfo), - set::active(true), - tableData - ( - $fromCaseItem, - $libItem, - $productItem, - $branchItem, - $moduleItem, - $storyItem, - item - ( - set::name($lang->testcase->type), - zget($lang->case->typeList, $case->type) - ), - item - ( - set::name($lang->testcase->stage), - $caseStage - ), - item - ( - set::name($lang->testcase->pri), - priLabel($case->pri, set::text($lang->case->priList)) - ), - item - ( - set::name($lang->testcase->status), - $this->processStatus('testcase', $case), - $caseChange - ), - $lastRunTimeItem, - $lastRunResultItem, - item - ( - set::name($lang->testcase->keywords), - $case->keywords - ) - ) - ), - tabPane - ( - set::key('legendOpenAndEdit' . $case->id), - set::title($lang->testcase->legendOpenAndEdit), - tableData - ( - item - ( - set::name($lang->testcase->openedBy), - zget($users, $case->openedBy) . $lang->at . $case->openedDate - ), - item - ( - set::name($lang->testcase->reviewedBy), - $reviewedBy - ), - item - ( - set::name($lang->testcase->reviewedDate), - !empty($case->reviewedBy) ? $case->reviewedDate : '' - ), - item - ( - set::name($lang->testcase->lblLastEdited), - !empty($case->lastEditedBy) ? zget($users, $case->lastEditedBy) . $lang->at . $case->lastEditedDate : '' - ) - ) - ) - ), - tabs - ( - isset($linkBugItem) || $linkCaseItem ? set::collapse(true) : true, - tabPane - ( - set::key('otherReleted' . $case->id), - set::title($lang->testcase->legendOther), - set::active(true), - tableData - ( - set::useTable(false), - isset($linkBugItem) ? $linkBugItem : null, - $linkCaseItem - ) - ) + setClass('text-gray'), + $lang->noData ) ) ); -if(!isInModal()) -{ - floatPreNextBtn - ( - !empty($preAndNext->pre) ? set::preLink(createLink('testcase', 'view', "testcaseID={$preAndNext->pre->id}")) : null, - !empty($preAndNext->next) ? set::nextLink(createLink('testcase', 'view', "testcaseID={$preAndNext->next->id}")) : null - ); -} +$stepsActions = array(); +$stepsActions['items'][] = array('icon' => 'table-large', 'size' => 'xs', 'type' => $stepsType == 'table' ? 'primary' : 'ghost', 'class' => 'mr-2', 'url' => createLink('testcase', 'view', "caseID={$case->id}&version={$case->version}&from={$from}&taskID={$taskID}&stepsType=table")); +$stepsActions['items'][] = array('icon' => 'tree', 'size' => 'xs', 'type' => $stepsType == 'mindmap' ? 'primary' : 'ghost', 'url' => createLink('testcase', 'view', "caseID={$case->id}&version={$case->version}&from={$from}&taskID={$taskID}&stepsType=mindmap")); -render(); +/* 初始化主栏内容。Init sections in main column. */ +$sections = array(); +$sections[] = setting() + ->title($lang->testcase->precondition) + ->control('html') + ->content($case->precondition); + +$sections[] = setting() + ->title($lang->testcase->steps) + ->titleActions($stepsActions) + ->children(wg($stepsTable)); + +$sections[] = setting() + ->control('fileList') + ->files($case->files) + ->showDelete(false) + ->padding(false) + ->object($case); + +/* 初始化侧边栏标签页。Init sidebar tabs. */ +$tabs = array(); + +$tabs[] = setting() + ->group('basic') + ->title($lang->testcase->legendBasicInfo) + ->control('caseBasicInfo'); + +$tabs[] = setting() + ->group('basic') + ->title($lang->testcase->legendOpenAndEdit) + ->control('caseTimeInfo'); + +$tabs[] = setting() + ->group('relatives') + ->title($lang->testcase->legendOther) + ->control('caseRelatedList'); + +detail +( + set::urlFormatter(array('{caseID}' => $case->caseID, '{version}' => $case->version, '{product}' => $case->product, '{branch}' => $case->branch, '{module}' => $case->module, '{id}' => $case->id, '{lib}' => $case->lib)), + set::toolbar($toolbar), + set::sections($sections), + set::tabs($tabs), + set::actions($actions) +);