* @package testtask * @version $Id: control.php 5114 2013-07-12 06:02:59Z chencongzhi520@gmail.com $ * @link https://www.zentao.net */ class testtask extends control { /** * 产品键值对。 * Product key-value pairs. * * @var array * @access public */ public $products = array(); /** * 项目 ID。 * Project id. * * @var int * @access public */ public $projectID = 0; /** * 扩展构造方法,获取产品键值对。 * Extend the constructor to get product key-value pairs. * * @param string $moduleName * @param string $methodName * @access public * @return void */ public function __construct($moduleName = '', $methodName = '') { parent::__construct($moduleName, $methodName); if(static::class == 'testtask') { $products = $this->testtaskZen->getProducts(); /* 如果没有获取到产品键值对、并且即不是弹窗页面,并且是 zin 或者 fetch 请求,那么跳转到错误提示页面。*/ /* If the product key-value pair is not obtained and it is not a pop-up page, and it is a zin request or a fetch request, then jump to the error page. */ if(empty($products) && !isInModal() && (helper::isAjaxRequest('zin') || helper::isAjaxRequest('fetch'))) { $tab = ($this->app->tab == 'project' || $this->app->tab == 'execution') ? $this->app->tab : 'qa'; $objectID = ($tab == 'project' || $tab == 'execution') ? $this->session->$tab : 0; $this->locate($this->createLink('product', 'showErrorNone', "moduleName={$tab}&activeMenu=testtask&objectID=$objectID")); } $this->products = $products; $this->view->products = $products; } } /** * 测试单的列表。 * Browse testtasks. * * @param int $productID * @param string $branch * @param string $type * @param string $orderBy * @param int $recTotal * @param int $recPerPage * @param int $pageID * @param string $beginTime * @param string $endTime * @access public * @return void */ public function browse(int $productID = 0, string $branch = '0', string $type = 'local,totalStatus', string $orderBy = 'id_desc', int $recTotal = 0, int $recPerPage = 20, int $pageID = 1, string $beginTime = '', string $endTime = '') { /* 检查是否有权限访问测试单所属产品。*/ /* Check if user have permission to access the product to which the testtask belongs. */ $productID = $this->loadModel('product')->checkAccess($productID, $this->products); /* 保存部分内容到 session 中供后面使用。*/ /* Save session. */ $uri = $this->app->getURI(true); $this->session->set('testtaskList', $uri, 'qa'); $this->session->set('reportList', $uri, 'qa'); $this->session->set('buildList', $uri, 'execution'); /* 预处理部分变量供查询使用。*/ /* Prepare variables for query. */ $this->app->loadClass('pager', $static = true); $pager = pager::init($recTotal, $recPerPage, $pageID); $beginTime = $beginTime ? date('Y-m-d', strtotime($beginTime)) : ''; $endTime = $endTime ? date('Y-m-d', strtotime($endTime)) : ''; $sort = common::appendOrder($orderBy); $product = $this->product->getById($productID); /* 处理分支。 */ /* Process branch. */ $branch = $this->testtaskZen->getBrowseBranch($branch, $product->type); /* 设置1.5级菜单。 */ /* Set 1.5 level menu. */ $this->loadModel('qa')->setMenu($productID, $branch); /* 从数据库中查询符合条件的测试单。*/ /* Query the testtasks from the database. */ $testtasks = $this->testtask->getProductTasks($productID, $branch, $type, $beginTime, $endTime, $sort, $pager); /* Process testtask members. */ $users = $this->loadModel('user')->getPairs('noclosed|noletter'); foreach($testtasks as $testtask) { $testtask->rawStatus = $testtask->status; $testtask->status = $this->processStatus('testtask', $testtask); if(empty($testtask->members)) continue; $members = array(); foreach(explode(',', $testtask->members) as $member) $members[] = zget($users, $member); $testtask->members = implode(',', array_unique($members)); } $this->testtaskZen->prepareSummaryForBrowse($testtasks); $this->view->title = $product->name . $this->lang->hyphen . $this->lang->testtask->common; $this->view->users = $users; $this->view->tasks = $testtasks; $this->view->product = $product; $this->view->branch = $branch; $this->view->type = $type; $this->view->orderBy = $orderBy; $this->view->pager = $pager; $this->view->beginTime = $beginTime; $this->view->endTime = $endTime; $this->view->multipleSprints = $this->loadModel('execution')->fetchExecutionList(0, 'all', $productID); $this->display(); } /** * 单元测试列表页面。 * Browse unit tasks. * * @param int $productID * @param string $browseType * @param string $orderBy * @param int $recTotal * @param int $recPerPage * @param int $pageID * @access public * @return void */ public function browseUnits(int $productID = 0, string $browseType = 'newest', string $orderBy = 'id_desc', int $recTotal = 0, int $recPerPage = 20, int $pageID = 1) { /* 检查是否有权限访问测试单所属产品。*/ /* Check if user have permission to access the product to which the testtask belongs. */ $productID = $this->loadModel('product')->checkAccess($productID, $this->products); /* 保存部分内容到 session 中供后面使用。*/ /* Save session. */ $this->session->set('testtaskList', $this->app->getURI(true), 'qa'); $this->session->set('caseList', $this->app->getURI(true), $this->app->tab); $this->session->set('buildList', $this->app->getURI(true) . '#app=' . $this->app->tab, 'execution'); $this->loadModel('qa')->setMenu($productID); /* 确保二级导航高亮的是测试用例。*/ /* Make sure the secondary navigation highlights test cases. */ $this->lang->qa->menu->testcase['subModule'] .= ',testtask'; /* 预处理部分变量供查询使用。*/ /* Prepare variables for query. */ if($browseType == 'newest') $recPerPage = '10'; $this->app->loadClass('pager', true); $pager = pager::init($recTotal, $recPerPage, $pageID); $sort = common::appendOrder($orderBy); $this->view->title = $this->products[$productID] . $this->lang->hyphen . $this->lang->testtask->common; $this->view->tasks = $this->testtask->getProductUnitTasks($productID, $browseType, $sort, $pager); $this->view->users = $this->loadModel('user')->getPairs('noclosed|noletter'); $this->view->product = $this->product->getByID($productID); $this->view->browseType = $browseType; $this->view->orderBy = $orderBy; $this->view->pager = $pager; $this->display(); } /** * 创建测试单页面和创建表单的提交。 * Create a testtask. * * @param int $productID * @param int $executionID * @param int $build * @param int $projectID * @access public * @return void */ public function create(int $productID, int $executionID = 0, int $build = 0, int $projectID = 0) { /* 检查是否有权限访问测试单所属产品。*/ /* Check if user have permission to access the product to which the testtask belongs. */ $productID = $this->loadModel('product')->checkAccess($productID, $this->products); if(!empty($_POST)) { /* 生成表单数据。*/ /* Generate form data. */ $formData = form::data($this->config->testtask->form->create)->add('project', $projectID)->get(); $formData = $this->loadModel('file')->processImgURL($formData, $this->config->testtask->editor->create['id'], $this->post->uid); if($formData->execution) { $execution = $this->loadModel('execution')->fetchByID($formData->execution); $formData->project = $execution->project; } if($formData->build && empty($formData->execution)) { $build = $this->loadModel('build')->getById((int)$this->post->build); $formData->project = $build->project; } $formData->members = trim($formData->members, ','); /* 创建测试单。*/ /* Create a testtask. */ $testtaskID = $this->testtask->create($formData); if(dao::isError()) return $this->send(array('result' => 'fail', 'message' => dao::getError())); /* 处理上传的文件。*/ /* Process uploaded files. */ $this->loadModel('file')->updateObjectID($this->post->uid, $testtaskID, 'testtask'); $this->file->saveUpload('testtask', $testtaskID); /* 执行工作流的扩展动作并返回提示消息。*/ /* Execute the extended action of the workflow and return a prompt message. */ $message = $this->executeHooks($testtaskID) ?: $this->lang->saveSuccess; /* 根据不同的应用生成不同的跳转链接。*/ /* Create different links according to different applications. */ $testtask = $this->testtask->fetchByID($testtaskID); if($this->app->tab == 'project') $link = $this->createLink('project', 'testtask', "projectID={$testtask->project}"); if($this->app->tab == 'execution') $link = $this->createLink('execution', 'testtask', "executionID={$testtask->execution}"); if($this->app->tab == 'qa') $link = $this->createLink('testtask', 'browse', "productID={$this->post->product}"); return $this->send(array('result' => 'success', 'message' => $message, 'load' => $link, 'id' => $testtaskID)); } $this->testtaskZen->setMenu($productID, 0, $projectID, $executionID); $this->testtaskZen->assignForCreate($productID, $projectID, $executionID, $build); $this->display(); } /** * 查看当前测试单的详细信息。 * View detail of a testtask. * * @param int $testtaskID * @access public * @return void */ public function view(int $testtaskID) { /* 查询测试单详细信息。 */ /* Query detail of a testtask. */ $testtask = $this->testtask->getByID($testtaskID, true); if(!$testtask) return $this->send(array('result' => 'fail', 'load' => array('alert' => $this->lang->notFound, 'locate' => $this->createLink('qa', 'index')))); $this->testtaskZen->setMenu($testtask->product, $testtask->branch, $testtask->project, $testtask->execution, $testtask); $this->testtaskZen->setDropMenu($testtask->product, $testtask); /* session 改变时重新查询关联的产品。*/ /* When the session changes, query the related products again. */ $products = $this->products; if($this->session->project != $testtask->project) $products = $this->loadModel('product')->getProductPairsByProject($testtask->project); $this->session->project = $testtask->project; /* 如果测试单所属产品在产品键值对中不存在,将其加入。*/ /* Prepare the product key-value pairs. */ if(!isset($products[$testtask->product])) { $product = $this->loadModel('product')->getByID($testtask->product); $products[$product->id] = $product->name; } /* 执行工作流配置的扩展动作。*/ /* Execute extended actions configured in the workflow. */ $this->executeHooks($testtaskID); if(!empty($testtask->execution)) $this->view->execution = $this->loadModel('project')->getByID((int)$testtask->execution); $this->view->title = "TASK #$testtask->id $testtask->name/" . $products[$testtask->product]; $this->view->users = $this->loadModel('user')->getPairs('noclosed|noletter'); $this->view->actions = $this->loadModel('action')->getList('testtask', $testtaskID); $this->view->testreport = $this->loadModel('testreport')->getById($testtask->testreport); $this->view->buildName = $testtask->build == 'trunk' ? $this->lang->trunk : $testtask->buildName; $this->view->task = $testtask; $this->view->productID = $testtask->product; $this->display(); } /** * 查看单元测试测试单的用例列表。 * Browse cases of a testtask which is unit test. * * @param int $testtaskID * @param string $orderBy * @param int $recTotal * @param int $recPerPage * @param int $pageID * @access public * @return void */ public function unitCases(int $testtaskID, string $orderBy = 't1.id_asc', int $recTotal = 0, int $recPerPage = 20, int $pageID = 1) { $testtask = $this->testtask->getByID($testtaskID); /* 检查是否有权限访问测试单所属产品。*/ /* Check if user have permission to access the product to which the testtask belongs. */ $productID = $this->loadModel('product')->checkAccess($testtask->product, $this->products); $this->loadModel('qa')->setMenu($productID); $this->lang->qa->menu->testcase['subModule'] .= ',testtask'; /* 根据测试套件获取测试用例执行结果。*/ /* Get testrun of test cases by suite. */ $suiteRuns = $this->testtask->getRunsForUnitCases($testtaskID, "t4.suite_asc,$orderBy"); /* 保存部分内容到 session 中供后面使用。*/ /* Save session .*/ $this->session->set('caseList', $this->app->getURI(true), 'qa'); $this->loadModel('common')->saveQueryCondition($this->dao->get(), 'testcase', false); /* 因为套件和测试单是多对多关系,所以要过滤掉相同ID的测试单执行数据。 */ /* Filter out the testrun data with the same ID because the suite and testtask have a many-to-many relationship. */ $runs = array(); foreach($suiteRuns as $run) $runs[$run->id] = $run; $runs = $this->loadModel('testcase')->appendData($runs, 'testrun'); /* Process runs. */ $this->app->loadClass('pager', $static = true); $pager = pager::init(count($runs), $recPerPage, $pageID); $runs = array_chunk($runs, $pager->recPerPage, true); $runs = empty($runs) ? $runs : (isset($runs[$pageID - 1]) ? $runs[$pageID - 1] : current($runs)); $runs = $this->testtaskZen->processRowspanForUnitCases($runs); $this->view->title = $this->products[$productID] . $this->lang->hyphen . $this->lang->testcase->common; $this->view->users = $this->loadModel('user')->getPairs('noletter'); $this->view->runs = $runs; $this->view->productID = $productID; $this->view->taskID = $testtaskID; $this->view->pager = $pager; $this->display(); } /** * 查看非单元测试的测试单的用例列表。 * Browse cases of a testtask which isn't unit test. * * @param int $taskID * @param string $browseType all|assignedtome|bysuite|byModule * @param string $orderBy * @param int $param * @param int $recTotal * @param int $recPerPage * @param int $pageID * @access public * @return void */ public function cases(int $taskID, string $browseType = 'all', int $param = 0, string $orderBy = 'id_desc', int $recTotal = 0, int $recPerPage = 20, int $pageID = 1) { /* 获取测试单信息。*/ /* Get testtask info. */ $testtask = $this->testtask->getByID($taskID); if(!$testtask) return $this->send(array('result' => 'fail', 'load' => array('alert' => $this->lang->testtask->checkLinked, 'locate' => array('back' => true)))); if(!$this->loadModel('common')->checkPrivByObject('project', $testtask->project)) return $this->sendError($this->lang->project->accessDenied, inlink('browse')); if($testtask->execution && !$this->loadModel('common')->checkPrivByObject('execution', $testtask->execution)) return $this->sendError($this->lang->execution->accessDenied, inlink('browse')); /* 检查是否有权限访问测试单所属产品。*/ /* Check if user have permission to access the product to which the testtask belongs. */ $this->testtaskZen->setMenu($testtask->product, (string)$testtask->branch, $testtask->project, $testtask->execution, $testtask); $productID = $this->loadModel('product')->checkAccess($testtask->product, $this->products); /* 预处理部分变量供后面使用。*/ /* Prepare variables. */ $browseType = strtolower($browseType); $param = (int)$param; /* 保存部分内容到 session 中供后面使用。*/ /* Save session. */ $this->session->set('caseList', $this->app->getURI(true), $this->app->tab); if($browseType != 'bymodule') $this->session->set('taskCaseBrowseType', $browseType); /* 保存部分内容到 cookie 中供后面使用。*/ /* Save cookies. */ if($this->cookie->preTaskID != $taskID) helper::setcookie('taskCaseModule', 0, 0); helper::setcookie('preTaskID', $taskID); if($browseType == 'bymodule') helper::setcookie('taskCaseModule', $param, 0); /* 如果测试单所属产品在产品键值对中不存在,将其加入。*/ /* Prepare the product key-value pairs. */ $product = $this->loadModel('product')->getByID($productID); if(!isset($this->products[$productID])) $this->products[$productID] = $product->name; /* 预处理部分变量供查询使用。*/ /* Prepare variables for query. */ $this->app->loadClass('pager', $static = true); $pager = pager::init($recTotal, $recPerPage, $pageID); $queryID = ($browseType == 'bysearch' or $browseType == 'bysuite') ? $param : 0; $moduleID = ($browseType == 'bymodule') ? $param : ($browseType == 'bysearch' ? 0 : ((int)$this->cookie->taskCaseModule ?: 0)); $sort = common::appendOrder($orderBy, 't2.id'); $sort = str_replace('case_', 'id_', $sort); /* 从数据库中查询一个测试单下关联的测试用例。*/ /* Query the cases associated with a testtask from the database. */ $runs = $this->testtask->getTaskCases($productID, $browseType, $queryID, $moduleID, $sort, $pager, $testtask); $this->loadModel('common')->saveQueryCondition($this->dao->get(), 'testcase', false); list($runs, $scenes) = $this->testtask->getSceneCases($productID, $runs); $this->testtaskZen->setSearchParamsForCases($product, $moduleID, $testtask, $queryID); $this->testtaskZen->assignForCases($product, $testtask, $runs, $scenes, $moduleID, $browseType, $param, $orderBy, $pager); $this->display(); } /** * 查看一个测试单的报表。 * The report page of a testtask. * * @param int $productID * @param int $taskID * @param string $browseType * @param int $branchID * @param int $moduleID * @param string $chartType * @access public * @return void */ public function report(int $productID, int $taskID, string $browseType, int $branchID, int $moduleID = 0, string $chartType = 'pie') { $this->loadModel('report'); $charts = $datas = array(); if(!empty($_POST)) { $this->app->loadLang('testcase'); foreach($this->post->charts as $chart) { $chartFunc = 'getDataOf' . $chart; $chartData = $this->testtask->$chartFunc($taskID); $chartOption = $this->config->testtask->report->options; if(!empty($chartType)) { $chartOption->type = $chartType; $chartOption->graph->caption = $this->lang->testtask->report->charts[$chart]; } $charts[$chart] = $chartOption; $datas[$chart] = $this->report->computePercent($chartData); } } $task = $this->testtask->getByID($taskID); $this->testtaskZen->setMenu($task->product, $branchID, $task->project, $task->execution, $task); $this->testtaskZen->setDropMenu($task->product, $task); /* 如果测试单所属产品在产品键值对中不存在,将其加入。*/ /* Prepare the product key-value pairs. */ if(!isset($this->products[$task->product])) { $product = $this->loadModel('product')->getByID($task->product); $this->products[$product->id] = $product->name; } $this->view->title = $this->products[$task->product] . $this->lang->hyphen . $this->lang->testtask->common . $this->lang->hyphen . $this->lang->testtask->reportChart; $this->view->productID = $task->product; $this->view->taskID = $taskID; $this->view->browseType = $browseType; $this->view->moduleID = $moduleID; $this->view->branchID = $branchID; $this->view->chartType = $chartType; $this->view->charts = $charts; $this->view->datas = $datas; $this->display(); } /** * 分组浏览一个测试单关联的用例。 * Browse the cases associated with a testtask in groups. * * @param int $taskID * @param string $browseType * @access public * @return void */ public function groupCase(int $taskID, string $browseType = 'all') { /* 检查测试单是否存在。*/ /* Check if the testtask exists. */ $task = $this->testtask->getByID($taskID); if(!$task) return $this->send(array('result' => 'fail', 'load' => array('alert' => $this->lang->notFound, 'locate' => $this->createLink('qa', 'index')))); /* 检查是否有权限访问测试单所属产品。*/ /* Check if user have permission to access the product to which the testtask belongs. */ $this->testtaskZen->setMenu($task->product, $task->branch, $task->project, $task->execution, $task); $this->testtaskZen->setDropMenu($task->product, $task); $productID = $this->loadModel('product')->checkAccess($task->product, $this->products); /* 保存部分内容到 session 和 cookie 中供后面使用。*/ /* Save session and cookie. */ $this->session->set('caseList', $this->app->getURI(true), 'qa'); helper::setcookie('taskCaseModule', 0, 0); /* 如果测试单所属产品在产品键值对中不存在,将其加入。*/ /* Prepare the product key-value pairs. */ if(!isset($this->products[$productID])) { $product = $this->loadModel('product')->getByID($productID); $this->products[$productID] = $product->name; } /* 从数据库中查询一个测试单下关联的测试用例。*/ /* Query the cases associated with a testtask from the database. */ if(!isset($this->lang->testtask->featureBar['groupcase'][$browseType])) $browseType = 'all'; $cases = $this->testtask->getTaskCases($productID, $browseType, 0, 0, 'story', null, $task); $this->loadModel('common')->saveQueryCondition($this->dao->get(), 'testcase', false); $cases = $this->loadModel('testcase')->appendData($cases, 'run'); /* 处理测试用例的跨行合并属性供前端组件分组使用。*/ /* Process the rowspan property of cases for use by front-end component groupings. */ $cases = $this->testtaskZen->processRowspanForGroupCase($cases, $task->build); $this->view->title = $this->products[$productID] . $this->lang->hyphen . $this->lang->testtask->cases; $this->view->users = $this->loadModel('user')->getPairs('noletter'); $this->view->canBeChanged = common::canBeChanged('testtask', $task); $this->view->productID = $productID; $this->view->cases = $cases; $this->view->task = $task; $this->view->browseType = $browseType; $this->display(); } /** * 编辑一个测试单。 * Edit a testtask. * * @param int $taskID * @access public * @return void */ public function edit(int $taskID) { if(!empty($_POST)) { $oldTask = $this->testtask->getByID($taskID); $task = $this->testtaskZen->buildTaskForEdit($taskID, $oldTask->product); $this->testtaskZen->checkTaskForEdit($task); if(dao::isError()) return $this->send(array('result' => 'fail', 'message' => dao::getError())); $changes = $this->testtask->update($task, $oldTask); if(dao::isError()) return $this->send(array('result' => 'fail', 'message' => dao::getError())); if($changes || $this->post->comment) { $actionID = $this->loadModel('action')->create('testtask', $taskID, 'edited', $this->post->comment); $this->action->logHistory($actionID, $changes); } $message = $this->executeHooks($taskID) ?: $this->lang->saveSuccess; if(isInModal()) return $this->send(array('result' => 'success', 'message' => $message, 'load' => true)); return $this->send(array('result' => 'success', 'message' => $message, 'load' => $this->session->testtaskList, 'closeModal' => true)); } /* Get task info. */ $task = $this->testtask->getByID($taskID); $this->testtaskZen->setMenu($task->product, $task->branch, $task->project, $task->execution, $task); $productID = $this->loadModel('product')->checkAccess($task->product, $this->products); /* 如果测试单所属产品在产品键值对中不存在,将其加入。*/ /* Prepare the product key-value pairs. */ if(!isset($this->products[$productID])) { $product = $this->product->getByID($productID); $this->products[$product->id] = $product->name; } $this->testtaskZen->assignForEdit($task, $productID); $this->display(); } /** * 开始一个测试单。 * Start a testtask. * * @param int $taskID * @access public * @return void */ public function start(int $taskID) { if(!empty($_POST)) { $task = $this->testtaskZen->buildTaskForStart($taskID); $changes = $this->testtask->start($task); if(dao::isError()) return $this->send(array('result' => 'fail', 'message' => dao::getError())); if($changes || $this->post->comment) { $actionID = $this->loadModel('action')->create('testtask', $taskID, 'Started', $this->post->comment); $this->action->logHistory($actionID, $changes); } $message = $this->executeHooks($taskID) ?: $this->lang->saveSuccess; return $this->send(array('result' => 'success', 'message' => $message, 'closeModal' => true, 'load' => true)); } /* Set menu. */ $testtask = $this->testtask->getByID($taskID); $this->testtaskZen->setMenu($testtask->product, $testtask->branch, $testtask->project, $testtask->execution, $testtask); $this->loadModel('product')->checkAccess($testtask->product, $this->products); $this->view->title = $testtask->name . $this->lang->hyphen . $this->lang->testtask->start; $this->view->actions = $this->loadModel('action')->getList('testtask', $taskID); $this->view->users = $this->loadModel('user')->getPairs('nodeleted', $testtask->owner); $this->view->testtask = $testtask; $this->display(); } /** * 关闭一个测试单。 * Close a testtask. * * @param int $taskID * @access public * @return void */ public function close(int $taskID) { if(!empty($_POST)) { $task = $this->testtaskZen->buildTaskForClose($taskID); $changes = $this->testtask->close($task); if(dao::isError()) return $this->send(array('result' => 'fail', 'message' => dao::getError())); if($changes || $this->post->comment) { $actionID = $this->loadModel('action')->create('testtask', $taskID, 'Closed', $this->post->comment); if($changes) $this->action->logHistory($actionID, $changes); } $message = $this->executeHooks($taskID) ?: $this->lang->saveSuccess; return $this->send(array('result' => 'success', 'message' => $message, 'closeModal' => true, 'load' => true)); } /* Set menu. */ $testtask = $this->testtask->getByID($taskID); $this->testtaskZen->setMenu($testtask->product, $testtask->branch, $testtask->project, $testtask->execution, $testtask); $this->loadModel('product')->checkAccess($testtask->product, $this->products); $this->view->title = $testtask->name . $this->lang->hyphen . $this->lang->close; $this->view->actions = $this->loadModel('action')->getList('testtask', $taskID); $this->view->users = $this->loadModel('user')->getPairs('noclosed|nodeleted|qdfirst'); $this->view->testtask = $testtask; $this->display(); } /** * 阻塞一个测试单。 * Block a testtask. * * @param int $taskID * @access public * @return void */ public function block(int $taskID) { if(!empty($_POST)) { $task = $this->testtaskZen->buildTaskForBlock($taskID); $changes = $this->testtask->block($task); if(dao::isError()) return $this->send(array('result' => 'fail', 'message' => dao::getError())); if($changes || $this->post->comment) { $actionID = $this->loadModel('action')->create('testtask', $taskID, 'Blocked', $this->post->comment); $this->action->logHistory($actionID, $changes); } $message = $this->executeHooks($taskID) ?: $this->lang->saveSuccess; return $this->send(array('result' => 'success', 'message' => $message, 'closeModal' => true, 'load' => true)); } /* Set menu. */ $testtask = $this->testtask->getByID($taskID); $this->testtaskZen->setMenu($testtask->product, $testtask->branch, $testtask->project, $testtask->execution, $testtask); $this->loadModel('product')->checkAccess($testtask->product, $this->products); $this->view->title = $testtask->name . $this->lang->hyphen . $this->lang->testtask->block; $this->view->actions = $this->loadModel('action')->getList('testtask', $taskID); $this->view->users = $this->loadModel('user')->getPairs('nodeleted', $testtask->owner); $this->view->testtask = $testtask; $this->display(); } /** * 激活一个测试单。 * Activate a testtask. * * @param int $taskID * @access public * @return void */ public function activate(int $taskID) { if(!empty($_POST)) { $task = $this->testtaskZen->buildTaskForActivate($taskID); $changes = $this->testtask->activate($task); if(dao::isError()) return $this->send(array('result' => 'fail', 'message' => dao::getError())); if($changes || $this->post->comment) { $actionID = $this->loadModel('action')->create('testtask', $taskID, 'Activated', $this->post->comment); $this->action->logHistory($actionID, $changes); } $message = $this->executeHooks($taskID) ?: $this->lang->saveSuccess; return $this->send(array('result' => 'success', 'message' => $message, 'closeModal' => true, 'load' => true)); } /* Set menu. */ $testtask = $this->testtask->getByID($taskID); $this->testtaskZen->setMenu($testtask->product, $testtask->branch, $testtask->project, $testtask->execution, $testtask); $this->loadModel('product')->checkAccess($testtask->product, $this->products); $this->view->title = $testtask->name . $this->lang->hyphen . $this->lang->testtask->activate; $this->view->actions = $this->loadModel('action')->getList('testtask', $taskID); $this->view->users = $this->loadModel('user')->getPairs('nodeleted', $testtask->owner); $this->view->testtask = $testtask; $this->display(); } /** * 删除一个测试单。 * Delete a testtask. * * @param int $taskID * @access public * @return void */ public function delete(int $taskID) { $task = $this->testtask->getByID($taskID); if(!$task) return $this->send(array('result' => 'fail', 'load' => array('alert' => $this->lang->notFound, 'locate' => $this->createLink('qa', 'index')))); $this->testtask->delete(TABLE_TESTTASK, $taskID); if(dao::isError()) return $this->send(array('result' => 'success', 'message' => dao::getError())); $message = $this->executeHooks($taskID) ?: $this->lang->saveSuccess; if(defined('RUN_MODE') && RUN_MODE == 'api') return $this->send(array('status' => 'success')); $browseList = inlink('browse', "productID=$task->product"); if($this->app->tab == 'execution') $browseList = $this->createLink('execution', 'testtask', "executionID=$task->execution"); if($this->app->tab == 'project') $browseList = $this->createLink('project', 'testtask', "projectID=$task->project"); return $this->send(array('result' => 'success', 'message' => $message, 'load' => $this->app->methodName == 'view' ? $browseList : true, 'closeModal' => true)); } /** * 关联测试用例到一个测试单。 * Link cases to a testtask. * * @param int $taskID * @param string $type * @param int $param * @param int $recTotal * @param int $recPerPage * @param int $pageID * @access public * @return void */ public function linkCase(int $taskID, string $type = 'all', int $param = 0, int $recTotal = 0, int $recPerPage = 20, int $pageID = 1) { if(!empty($_POST)) { $runs = form::batchData($this->config->testtask->form->linkCase)->get(); $this->testtask->linkCase($taskID, $type, $runs); if(dao::isError()) return $this->send(array('result' => 'success', 'message' => dao::getError())); return $this->send(array('result' => 'success', 'message' => $this->lang->saveSuccess, 'load' => inlink('cases', "taskID={$taskID}"))); } /* Get testtask info. */ $task = $this->testtask->getByID($taskID); if(!$task) return $this->send(array('result' => 'fail', 'load' => array('alert' => $this->lang->testtask->checkLinked, 'locate' => array('back' => true)))); /* Check if user have permission to access the product to which the testtask belongs. */ $this->testtaskZen->setMenu($task->product, $task->branch, $task->project, $task->execution, $task); $this->testtaskZen->setDropMenu($task->product, $task); $productID = $this->loadModel('product')->checkAccess($task->product, $this->products); /* Prepare the product key-value pairs. */ $product = $this->product->getByID($productID); if(!isset($this->products[$productID])) $this->products[$productID] = $product->name; /* Save session. */ $this->session->set('caseList', $this->app->getURI(true), 'qa'); $this->testtaskZen->setSearchParamsForLinkCase($product, $task, $type, $param); /* 从数据库中查询一个测试单下可以关联的测试用例。*/ /* Query the cases that can be associated with a testtask from the database. */ $this->app->loadClass('pager', true); $pager = pager::init($recTotal, $recPerPage, $pageID); $cases = $this->testtask->getLinkableCases($productID, $task, $type, $param, $pager); $this->view->title = $task->name . $this->lang->hyphen . $this->lang->testtask->linkCase; $this->view->users = $this->loadModel('user')->getPairs('noletter'); $this->view->suites = $this->loadModel('testsuite')->getSuites($task->product); $this->view->relatedTasks = $this->testtask->getRelatedTestTasks($productID, $taskID); $this->view->cases = $cases; $this->view->task = $task; $this->view->type = $type; $this->view->param = $param; $this->view->pager = $pager; $this->view->product = $product; $this->view->branch = $task->branch; $this->display(); } /** * 从测试单中移除一个用例。 * Remove a case from testtask. * * @param int $runID * @access public * @return void */ public function unlinkCase(int $runID) { $this->testtask->unlinkCase($runID); if(dao::isError()) return $this->send(array('result' => 'fail', 'message' => dao::getError())); return $this->send(array('result' => 'success', 'message' => $this->lang->saveSuccess, 'load' => true)); } /** * 批量从测试单中移除用例。 * Batch remove cases from a testtask. * * @param int $taskID * @access public * @return void */ public function batchUnlinkCases(int $taskID) { $this->testtask->batchUnlinkCases($taskID, $this->post->caseIdList); if(dao::isError()) return $this->send(array('result' => 'fail', 'message' => dao::getError())); return $this->send(array('result' => 'success', 'message' => $this->lang->saveSuccess, 'load' => true)); } /** * 执行一个用例。 * Run a case. * * @param int $runID * @param int $caseID * @param int $version * @param string $confirm * @access public * @return void */ public function runCase(int $runID, int $caseID = 0, int $version = 0, string $confirm = '') { if($runID) { $run = $this->testtask->getRunById($runID); } else { $run = new stdclass(); $run->case = $this->loadModel('testcase')->getById($caseID, $version); } $caseID = $caseID ?: $run->case->id; $this->testtaskZen->checkAndExecuteAutomatedTest($run, $runID, $caseID, $version, $confirm); $preAndNext = $this->loadModel('common')->getPreAndNextObject('testcase', $caseID); if(!empty($_POST)) { $stepResults = form::batchData($this->config->testtask->form->runCase)->get(); $deployID = (int)$this->post->deploy;// code for biz. $caseResult = $this->testtask->createResult($runID, (int)$this->post->case, (int)$this->post->version, $stepResults, $deployID); if(dao::isError()) return $this->send(array('result' => 'fail', 'message' => dao::getError())); $this->loadModel('action')->create('case', $caseID, 'run', '', zget($run, 'task', '0') . ',' . $caseResult); $this->testtaskZen->responseAfterRunCase($caseResult, $preAndNext, $run, $caseID, $version); return $this->send(array('result' => 'success', 'load' => true, 'closeModal' => true)); } $this->testtaskZen->assignForRunCase($run, $preAndNext, $runID, $caseID, $version, $confirm); $this->display(); } /** * 批量执行用例。 * Batch run cases. * * @param int $productID * @param string $orderBy * @param string $from * @param int $taskID * @param string $confirm * @access public * @return void */ public function batchRun(int $productID, string $orderBy = 'id_desc', string $from = 'testcase', int $taskID = 0, string $confirm = '') { $url = $this->session->caseList ?: inlink('browse', "productID=$productID"); if($this->post->results) { $cases = form::batchData($this->config->testtask->form->batchRun)->get(); $this->testtask->batchRun($cases, $from, $taskID); if(dao::isError()) return $this->send(array('result' => 'fail', 'message' => dao::getError())); if(!empty($taskID)) $this->testtask->updateStatus((int)$taskID); return $this->send(array('result' => 'success', 'message' => $this->lang->saveSuccess, 'load' => $url)); } /* 根据不同情况获取要批量执行的用例。*/ /* Get cases to run according to different situations. */ $caseIdList = $this->loadModel('testcase')->ignoreAutoCaseIdList((array)$this->post->caseIdList); if(empty($caseIdList)) $this->locate($url); $cases = $this->testtaskZen->prepareCasesForBatchRun($productID, $orderBy, $from, $taskID, $confirm, $caseIdList); if(empty($cases)) return $this->send(array('result' => 'fail', 'load' => array('alert' => $this->lang->testtask->skipChangedCases, 'locate' => $url))); $steps = $this->testcase->getStepGroupByIdList($caseIdList); $emptyCases = ''; foreach($cases as $caseID => $case) { if(empty($steps[$case->id])) { unset($cases[$caseID]); $emptyCases .= empty($emptyCases) ? "{$case->id}" : ",{$case->id}"; } } if(empty($cases)) return $this->send(array('result' => 'fail', 'load' => array('alert' => $this->lang->testtask->caseEmpty, 'locate' => $url))); /* 获取用例所属模块的键值对。*/ /* Get key-value pairs of case module. */ $this->loadModel('tree'); $modules = array('/'); foreach($cases as $case) $modules += $this->tree->getModulesName((array)$case->module); $this->view->title = $this->lang->testtask->batchRun; $this->view->steps = $this->loadModel('testcase')->getStepGroupByIdList($caseIdList); $this->view->modules = $modules; $this->view->cases = $cases; $this->view->caseIdList = $caseIdList; $this->view->productID = $productID; $this->view->from = $from; $this->view->emptyCases = $emptyCases; $this->display(); } /** * 查看测试用例的执行结果。 * View test results of a test run. * * @param int $runID * @param int $caseID * @param int $version * @param string $status all|done * @param string $type all|fail * @param int $deployID * @access public * @return void */ public function results(int $runID, int $caseID = 0, int $version = 0, string $status = 'done', string $type = 'all', int $deployID = 0) { /* Set project menu. */ if($this->app->tab == 'project') $this->loadModel('project')->setMenu($this->session->project); /* Set case and results. */ if($runID) { $this->app->loadLang('testcase'); /* If runID is not empty, set testtask. */ $run = $this->testtask->getRunById($runID); $case = $run->case; $results = $this->testtask->getResults($runID, 0, $status, $type); $this->view->testtask = $this->testtask->getByID($run->task); } else { $case = $this->loadModel('testcase')->getByID($caseID, $version); $results = $this->testtask->getResults(0, $caseID, $status, $type, $deployID); } /* Assign. */ $this->view->case = $case; $this->view->runID = $runID; $this->view->results = $results; $this->view->type = $type; $this->view->builds = $this->loadModel('build')->getBuildPairs(array($case->product), $case->branch); $this->view->users = $this->loadModel('user')->getPairs('noclosed, noletter'); $this->view->testtasks = $this->testtask->getPairs($case->product, 0, 0, false); $this->display(); } /** * 指派测试用例。 * Assign a testcase. * * @param int $runID * @access public * @return void */ public function assignCase(int $runID) { $run = $this->testtask->getRunById($runID); if(!empty($_POST)) { $newRun = form::data($this->config->testtask->form->assignCase) ->add('id', $runID) ->stripTags($this->config->testtask->editor->assignCase['id'], $this->config->allowedTags) ->get(); $newRun = $this->loadModel('file')->processImgURL($newRun, $this->config->testtask->editor->assignCase['id'], $newRun->uid); if(dao::isError()) return $this->send(array('result' => 'fail', 'message' => dao::getError())); $changes = $this->testtask->assignCase($newRun, $run); if(dao::isError()) return $this->send(array('result' => 'fail', 'message' => dao::getError())); if(empty($changes)) return $this->sendSuccess(array('closeModal' => true, 'load' => true)); $actionID = $this->loadModel('action')->create('case', (int)$run->case->id, 'assigned', '', $newRun->assignedTo); $this->action->logHistory($actionID, $changes); $message = $this->executeHooks($run->task) ?: $this->lang->saveSuccess; return $this->sendSuccess(array('message' => $message, 'closeModal' => true, 'load' => true)); } /* Assign. */ $this->view->users = $this->loadModel('user')->getPairs('noclosed, noletter'); $this->view->run = $run; $this->display(); } /** * 批量指派测试单中的用例。 * Batch assign cases in a testtask. * * @param int $taskID * @param string $account * @access public * @return void */ public function batchAssign(int $taskID, string $account = '') { if(empty($account)) $account = $this->post->assignedTo; $this->testtask->batchAssign($taskID, $account, $this->post->caseIdList); if(dao::isError()) return $this->send(array('result' => 'fail', 'message' => dao::getError())); return $this->send(array('result' => 'success', 'message' => $this->lang->saveSuccess, 'load' => true)); } /** * 导入单元测试结果。 * Import unit results. * * @param int $productID * @access public * @return void */ public function importUnitResult(int $productID) { /* 检查是否有权限访问要导入单元测试结果的产品。*/ /* Check if user have permission to access the product into which unit test results are to be imported. */ $productID = $this->loadModel('product')->checkAccess($productID, $this->products); if($_POST) { $this->loadModel('file'); if(!is_writable($this->file->savePath)) return $this->send(array('result' => 'fail', 'message' => sprintf(strip_tags($this->lang->file->errorCanNotWrite), $this->file->savePath, $this->file->savePath))); $task = $this->testtaskZen->buildTaskForImportUnitResult($productID); $taskID = $this->testtask->importUnitResult($task); if(dao::isError()) return $this->send(array('result' => 'fail', 'message' => dao::getError())); return $this->send(array('result' => 'success', 'message' => $this->lang->saveSuccess, 'load' => inlink('unitCases', "taskID=$taskID"))); } $this->app->loadLang('job'); $this->loadModel('qa')->setMenu($productID); /* 确保二级导航高亮的是测试用例。*/ /* Make sure the secondary navigation highlights test cases. */ $this->lang->qa->menu->testcase['subModule'] .= ',testtask'; $executions = empty($productID) ? array() : $this->product->getExecutionPairsByProduct($productID, '', 0, '', array('review', 'design', 'request')); $builds = empty($productID) ? array() : $this->loadModel('build')->getBuildPairs(array($productID), 'all', 'notrunk', 0, 'execution', '', false); $this->view->title = $this->products[$productID] . $this->lang->hyphen . $this->lang->testtask->importUnitResult; $this->view->users = $this->loadModel('user')->getPairs('noletter|nodeleted|noclosed'); $this->view->executions = $executions; $this->view->builds = $builds; $this->view->productID = $productID; $this->display(); } /** * 获取用户负责的测试单的键值对。在创建或编辑测试单类型的待办时使用。 * Get the key-value pair of the testtask that the user is responsible for. Used when creating or editing a testtask type backlog. * * @param int $userID * @param string $id * @param string $status * @access public * @return void */ public function ajaxGetUserTestTasks(int $userID = 0, string $id = '', string $status = 'all') { $account = $this->app->user->account; if($userID) { $user = $this->loadModel('user')->getById($userID, 'id'); $account = $user->account; } $testTasks = $this->testtask->getUserTestTaskPairs($account, 0, $status); $items = array(); foreach($testTasks as $taskID => $taskName) $items[] = array('text' => $taskName, 'value' => $taskID); $fieldName = $id ? "testtasks[$id]" : 'testtask'; return print(json_encode(array('name' => $fieldName, 'items' => $items))); } /** * 获取一个产品下的测试单的键值对。 * Get testtask key-value pairs of a product by ajax. * * @param int $productID * @param int $executionID * @param int $appendTaskID * @access public * @return void */ public function ajaxGetTestTasks(int $productID, int $executionID = 0, int $appendTaskID = 0) { $tasks = array(); $pairs = $this->testtask->getPairs($productID, $executionID, $appendTaskID); foreach($pairs as $taskID => $taskName) $tasks[] = array('text' => $taskName, 'value' => $taskID, 'keys' => $taskName); return $this->send(array('result' => 'success', 'tasks' => $tasks)); } /** * 获取一个产品下的测试报告键值对。 * Get test report key-value pairs of a product by ajax. * * @param int $productID * @access public * @return void */ public function ajaxGetTestReports(int $productID) { $reports = array(); $pairs = $this->loadModel('testreport')->getPairs($productID); foreach($pairs as $id => $title) $reports[] = array('text' => $title, 'value' => $id); return $this->send(array('result' => 'success', 'reports' => $reports)); } /** * 获取一个产品下的测试单的下拉列表。 * Get drop menu of testtasks of a product by ajax. * * // TODO: 暂时不做大的调整,重构 1.5 级导航时可能需要此方法提供数据。 * * @param int $productID * @param string $branch * @param int $taskID * @param string $module * @param string $method * @param string $objectType * @param int $objectID * @access public * @return void */ public function ajaxGetDropMenu(int $productID, string $branch, int $taskID, string $module, string $method, string $objectType = '', int $objectID = 0) { if($objectType && ($objectType == 'project' || $objectType == 'execution') && $objectID) { $testtasks = $objectType == 'project' ? $this->testtask->getProjectTasks($objectID, 0, 'id_desc', null) : $this->testtask->getExecutionTasks($objectID, 0, 'execution', 'id_desc', null); } else { $scope = empty($objectType) ? 'local' : 'all'; $testtasks = $this->testtask->getProductTasks($productID, $branch, "$scope,totalStatus", '', '', 'id_desc', null); } $namePairs = array(); foreach($testtasks as $testtaskID => $testtask) $namePairs[$testtaskID] = $testtask->name; $params = $method == 'report' ? "productID={$productID}&taskID=%s&browseType=all&branch={$branch}" : "taskID=%s"; $this->view->currentTaskID = $taskID; $this->view->testtasks = $testtasks; $this->view->module = $module; $this->view->method = $method; $this->view->productID = $productID; $this->view->branch = $branch; $this->view->objectType = $objectType; $this->view->objectID = $objectID; $this->view->testtasksPinyin = common::convert2Pinyin($namePairs); $this->view->link = $this->createLink($module, $method, $params); $this->display(); } /** * 根据 id 获取用例的一次运行结果。 * Get a run result of a case by ajax. * * @param int $resultID * @access public * @return void */ public function ajaxGetResult(int $resultID) { $result = $this->dao->select('*')->from(TABLE_TESTRESULT)->where('id')->eq($resultID)->fetch(); return $this->send(array('result' => 'success', 'data' => $result)); } /**  * AJAX: Get executionID by buildID. * * @param int $buildID * @access public * @return int */ public function ajaxGetExecutionByBuild(int $buildID) { $execution = $this->loadModel('execution')->getByBuild($buildID); return print($execution ? $execution->id : 0); } }