834 lines
30 KiB
PHP
834 lines
30 KiB
PHP
<?php
|
||
declare(strict_types=1);
|
||
class testcaseTao extends testcaseModel
|
||
{
|
||
/**
|
||
* 获取用例的基础数据。
|
||
* Fetch base info of a case.
|
||
*
|
||
* @param int $caseID
|
||
* @access protected
|
||
* @return object|false
|
||
*/
|
||
protected function fetchBaseInfo(int $caseID): object|false
|
||
{
|
||
return $this->dao->select('*')->from(TABLE_CASE)->where('id')->eq($caseID)->fetch();
|
||
}
|
||
|
||
/**
|
||
* 查询场景名称。
|
||
* Fetch scene name.
|
||
*
|
||
* @param int $sceneID
|
||
* @access protected
|
||
* @return void
|
||
*/
|
||
protected function fetchSceneName(int $sceneID): string|null
|
||
{
|
||
return $this->dao->findByID($sceneID)->from(TABLE_SCENE)->fetch('title');
|
||
}
|
||
|
||
/**
|
||
* 通过用例的 id 列表查询步骤。
|
||
* Fetch step by id list.
|
||
*
|
||
* @param array $caseIdList
|
||
* @access protected
|
||
* @return array
|
||
*/
|
||
protected function fetchStepsByList(array $caseIdList): array
|
||
{
|
||
return $this->dao->select('t2.*')->from(TABLE_CASE)->alias('t1')
|
||
->leftJoin(TABLE_CASESTEP)->alias('t2')->on('t1.id = t2.case')
|
||
->where('t1.id')->in($caseIdList)
|
||
->andWhere('t1.version=t2.version')
|
||
->orderBy('t2.id')
|
||
->fetchGroup('case');
|
||
}
|
||
|
||
/**
|
||
* 获取某个项目下某个模块的用例列表。
|
||
* Get project cases of a module.
|
||
*
|
||
* @param int|array $productID
|
||
* @param int|string $branch
|
||
* @param int $moduleIdList
|
||
* @param string $browseType
|
||
* @param string $auto no|unit
|
||
* @param string $caseType
|
||
* @param string $orderBy
|
||
* @param object $pager
|
||
* @access protected
|
||
* @return array
|
||
*/
|
||
protected function getModuleProjectCases(int|array $productID, int|string $branch = 0, int|array $moduleIdList = 0, string $browseType = '', string $auto = 'no', string $caseType = '', string $orderBy = 'id_desc', ?object $pager = null): array
|
||
{
|
||
return $this->dao->select('DISTINCT t1.product, t1.case, t1.version, t2.*, t4.title AS storyTitle')->from(TABLE_PROJECTCASE)->alias('t1')
|
||
->leftJoin(TABLE_CASE)->alias('t2')->on('t1.case = t2.id')
|
||
->leftJoin(TABLE_PROJECTSTORY)->alias('t3')->on('t3.story = t2.story')
|
||
->leftJoin(TABLE_STORY)->alias('t4')->on('t3.story = t4.id')
|
||
->where('t1.project')->eq((int)$this->session->project)
|
||
->beginIF(!empty($productID))->andWhere('t2.product')->in($productID)->fi()
|
||
->beginIF(!empty($productID) && $branch !== 'all')->andWhere('t2.branch')->eq($branch)->fi()
|
||
->beginIF($moduleIdList)->andWhere('t2.module')->in($moduleIdList)->fi()
|
||
->beginIF($browseType == 'wait')->andWhere('t2.status')->eq($browseType)->fi()
|
||
->beginIF($auto == 'auto' || $auto == 'unit')->andWhere('t2.auto')->eq($auto)->fi()
|
||
->beginIF($auto != 'auto' && $auto != 'unit')->andWhere('t2.auto')->ne('unit')->fi()
|
||
->beginIF($caseType)->andWhere('t2.type')->eq($caseType)->fi()
|
||
->andWhere('t2.deleted')->eq('0')
|
||
->orderBy($orderBy)
|
||
->page($pager, 't1.`case`')
|
||
->fetchAll('id');
|
||
}
|
||
|
||
/**
|
||
* 获取待确认的用例列表。
|
||
* Get need confirm case list.
|
||
*
|
||
* @param int|array $productID
|
||
* @param string|int $branch
|
||
* @param array $modules
|
||
* @param string $auto
|
||
* @param string $caseType
|
||
* @param string $sort
|
||
* @param object $pager
|
||
* @access protected
|
||
* @return array
|
||
*/
|
||
protected function getNeedConfirmList(int|array $productID, string|int $branch, array $modules, string $auto, string $caseType, string $sort, ?object $pager = null): array
|
||
{
|
||
return $this->dao->select('distinct t1.*, t2.title AS storyTitle')->from(TABLE_CASE)->alias('t1')
|
||
->leftJoin(TABLE_STORY)->alias('t2')->on('t1.story = t2.id')
|
||
->leftJoin(TABLE_PROJECTCASE)->alias('t3')->on('t1.id = t3.case')
|
||
->where('t2.status')->eq('active')
|
||
->andWhere('t1.deleted')->eq('0')
|
||
->andWhere('t2.version > t1.storyVersion')
|
||
->beginIF(!empty($productID))->andWhere('t1.product')->in($productID)->fi()
|
||
->beginIF(!empty($productID) && $branch !== 'all')->andWhere('t1.branch')->eq($branch)->fi()
|
||
->beginIF($this->app->tab == 'project')->andWhere('t3.project')->eq($this->session->project)->fi()
|
||
->beginIF($modules)->andWhere('t1.module')->in($modules)->fi()
|
||
->beginIF($auto == 'auto' || $auto == 'unit')->andWhere('t1.auto')->eq($auto)->fi()
|
||
->beginIF($auto != 'auto' && $auto != 'unit')->andWhere('t1.auto')->ne('unit')->fi()
|
||
->beginIF($caseType)->andWhere('t1.type')->eq($caseType)->fi()
|
||
->orderBy($sort)
|
||
->page($pager, 't1.id')
|
||
->fetchAll();
|
||
}
|
||
|
||
/**
|
||
* 根据套件获取用例。
|
||
* Get cases by suite.
|
||
*
|
||
* @param int $productID
|
||
* @param int|string $branch
|
||
* @param int $suiteID
|
||
* @param array $moduleIdList
|
||
* @param string $auto no|unit
|
||
* @param string $orderBy
|
||
* @param object $pager
|
||
* @access public
|
||
* @return array
|
||
*/
|
||
public function getBySuite(int $productID, int|string $branch = 0, int $suiteID = 0, array|int $moduleIdList = 0, string $auto = 'no', string $orderBy = 'id_desc', ?object $pager = null): array
|
||
{
|
||
return $this->dao->select('t1.*, t2.title AS storyTitle, t3.version AS version')->from(TABLE_CASE)->alias('t1')
|
||
->leftJoin(TABLE_STORY)->alias('t2')->on('t1.story = t2.id')
|
||
->leftJoin(TABLE_SUITECASE)->alias('t3')->on('t1.id = t3.case')
|
||
->where('t1.product')->eq($productID)
|
||
->beginIF($branch !== 'all')->andWhere('t1.branch')->eq($branch)->fi()
|
||
->beginIF($this->app->tab == 'project')->andWhere('t1.project')->eq($this->session->project)->fi()
|
||
->andWhere('t3.suite')->eq($suiteID)
|
||
->beginIF($moduleIdList)->andWhere('t1.module')->in($moduleIdList)->fi()
|
||
->beginIF($auto == 'auto' || $auto == 'unit')->andWhere('t1.auto')->eq($auto)->fi()
|
||
->beginIF($auto != 'auto' && $auto != 'unit')->andWhere('t1.auto')->ne('unit')->fi()
|
||
->andWhere('t1.deleted')->eq('0')
|
||
->orderBy($orderBy)
|
||
->page($pager)
|
||
->fetchAll('id');
|
||
}
|
||
|
||
/**
|
||
* 插入用例的步骤。
|
||
* Insert the steps of the case.
|
||
*
|
||
* @param int $caseID
|
||
* @param array $steps
|
||
* @param array $expects
|
||
* @param array $stepTypes
|
||
* @access protected
|
||
* @return void
|
||
*/
|
||
protected function insertSteps(int $caseID, array $steps, array $expects, array $stepTypes, int $version = 1)
|
||
{
|
||
$preGrade = 0;
|
||
$parentStepID = $grandPaStepID = 0;
|
||
foreach($steps as $stepKey => $stepDesc)
|
||
{
|
||
/* 跳过步骤描述为空的步骤。 */
|
||
/* If step desc is empty, skip it. */
|
||
if(empty($stepDesc)) continue;
|
||
|
||
/* 计算步骤类型和层级。 */
|
||
/* Set step type and step grade. */
|
||
$stepType = $stepTypes[$stepKey];
|
||
$grade = substr_count((string)$stepKey, '.');
|
||
|
||
/* 如果当前步骤层级为0,父ID和祖父ID清0。 */
|
||
/* If step grade is zero, set parent step id and grand step id to zero. */
|
||
if($grade == 0)
|
||
{
|
||
$parentStepID = $grandPaStepID = 0;
|
||
}
|
||
/* 如果前一个步骤的层级比当前步骤的层级大,将父ID设置为祖父ID,祖父ID清0。 */
|
||
/* If previous step grade is greater than current step grade, set parent step id to grand step id, and set grand step id to zero. */
|
||
elseif($preGrade > $grade)
|
||
{
|
||
$parentStepID = $grandPaStepID;
|
||
$grandPaStepID = 0;
|
||
}
|
||
|
||
/* 构建步骤数据,插入步骤。 */
|
||
/* Build step data, and insert it. */
|
||
$step = new stdClass();
|
||
$step->type = $stepType;
|
||
$step->parent = $parentStepID;
|
||
$step->case = $caseID;
|
||
$step->version = $version;
|
||
$step->desc = rtrim(htmlSpecialString($stepDesc));
|
||
$step->expect = $stepType == 'group' ? '' : rtrim(htmlSpecialString(zget($expects, $stepKey, '')));
|
||
|
||
$this->dao->insert(TABLE_CASESTEP)->data($step)->autoCheck()->exec();
|
||
|
||
/* 如果步骤类型是group,将祖父ID设置为父ID,父ID设置为当前步骤ID。 */
|
||
/* If step type is group, set grand step id to parent step id and set parent step id to current step id. */
|
||
if($stepType == 'group')
|
||
{
|
||
$grandPaStepID = $parentStepID;
|
||
$parentStepID = $this->dao->lastInsertID();
|
||
}
|
||
|
||
$preGrade = $grade;
|
||
}
|
||
}
|
||
|
||
/**
|
||
* 获取用例步骤。
|
||
* Get case steps.
|
||
*
|
||
* @param int $caseID
|
||
* @param int $version
|
||
* @access public
|
||
* @return array
|
||
*/
|
||
public function getSteps(int $caseID, int $version): array
|
||
{
|
||
$steps = $this->dao->select('`id`,`desc`,`expect`,`type`,`parent`')->from(TABLE_CASESTEP)->where('`case`')->eq($caseID)->andWhere('version')->eq($version)->orderBy('id')->fetchAll('id');
|
||
return $this->processSteps($steps);
|
||
}
|
||
|
||
/**
|
||
* 处理用例步骤。
|
||
* Process case steps.
|
||
*
|
||
* @param array $steps
|
||
* @access public
|
||
* @return array
|
||
*/
|
||
public function processSteps(array $steps): array
|
||
{
|
||
$caseSteps = array();
|
||
$preGrade = 1;
|
||
$parentSteps = array();
|
||
$key = array(0, 0, 0);
|
||
foreach($steps as $step)
|
||
{
|
||
$parentSteps[$step->id] = $step->parent;
|
||
$grade = 1;
|
||
if(isset($parentSteps[$step->parent])) $grade = isset($parentSteps[$parentSteps[$step->parent]]) ? 3 : 2;
|
||
|
||
if($grade > $preGrade)
|
||
{
|
||
$key[$grade - 1] = 1;
|
||
}
|
||
else
|
||
{
|
||
if($grade < $preGrade)
|
||
{
|
||
if($grade < 2) $key[1] = 0;
|
||
if($grade < 3) $key[2] = 0;
|
||
}
|
||
$key[$grade - 1] ++;
|
||
}
|
||
|
||
$name = implode('.', $key);
|
||
$name = str_replace('.0', '', $name);
|
||
|
||
$data = new stdclass();
|
||
$data->name = str_replace('.0', '', $name);
|
||
$data->id = $step->id;
|
||
$data->step = htmlspecialchars_decode($step->desc);
|
||
$data->desc = htmlspecialchars_decode($step->desc);
|
||
$data->expect = htmlspecialchars_decode($step->expect);
|
||
$data->type = $step->type;
|
||
$data->parent = $step->parent;
|
||
$data->grade = $grade;
|
||
|
||
$caseSteps[] = $data;
|
||
|
||
$preGrade = $grade;
|
||
}
|
||
|
||
return $caseSteps;
|
||
}
|
||
|
||
/**
|
||
* 获取相关的需求。
|
||
* Get related stories.
|
||
*
|
||
* @param array $cases
|
||
* @access public
|
||
* @return array
|
||
*/
|
||
protected function getRelatedStories(array $cases): array
|
||
{
|
||
$relatedStoryIdList = array();
|
||
foreach($cases as $case) $relatedStoryIdList[$case->story] = $case->story;
|
||
|
||
return $this->dao->select('id, title')->from(TABLE_STORY)->where('id')->in($relatedStoryIdList)->fetchPairs();
|
||
}
|
||
|
||
/**
|
||
* 获取相关的用例。
|
||
* Get related cases.
|
||
*
|
||
* @param array $cases
|
||
* @access public
|
||
* @return array
|
||
*/
|
||
protected function getRelatedCases(array $cases): array
|
||
{
|
||
$relatedCaseIdList = array();
|
||
foreach($cases as $case)
|
||
{
|
||
$linkCases = explode(',', $case->linkCase);
|
||
foreach($linkCases as $linkCaseID)
|
||
{
|
||
if($linkCaseID) $relatedCaseIdList[$linkCaseID] = trim($linkCaseID);
|
||
}
|
||
}
|
||
|
||
return $this->dao->select('id, title')->from(TABLE_CASE)->where('id')->in($relatedCaseIdList)->fetchPairs();
|
||
}
|
||
|
||
/**
|
||
* 获取相关的步骤。
|
||
* Get related steps.
|
||
*
|
||
* @param array $caseIdList
|
||
* @access public
|
||
* @return array
|
||
*/
|
||
protected function getRelatedSteps(array $caseIdList): array
|
||
{
|
||
return $this->dao->select('DISTINCT t2.id, t2.parent, t2.`case`, t2.version, t2.type, t2.`desc`, t2.expect')
|
||
->from(TABLE_CASE)->alias('t1')
|
||
->leftJoin(TABLE_CASESTEP)->alias('t2')->on('t1.id = t2.`case` and t1.version = t2.version')
|
||
->where('t1.id')->in($caseIdList)
|
||
->orderBy('t2.id')
|
||
->fetchGroup('case', 'id');
|
||
}
|
||
|
||
/**
|
||
* 获取相关的附件。
|
||
* Get related files.
|
||
*
|
||
* @param array $caseIdList
|
||
* @access public
|
||
* @return array
|
||
*/
|
||
protected function getRelatedFiles(array $caseIdList): array
|
||
{
|
||
return $this->dao->select('*')->from(TABLE_FILE)->where('objectType')->eq('testcase')->andWhere('objectID')->in($caseIdList)->andWhere('extra')->ne('editor')->fetchGroup('objectID', 'id');
|
||
}
|
||
|
||
/**
|
||
* 创建一个测试用例。
|
||
* Create a test case.
|
||
*
|
||
* @param object $case
|
||
* @access protected
|
||
* @return bool
|
||
*/
|
||
protected function doCreate(object $case): bool
|
||
{
|
||
if(empty($case->product)) $this->config->testcase->create->requiredFields = str_replace(array('story', 'scene'), '', $this->config->testcase->create->requiredFields);
|
||
if(!empty($case->lib)) $this->config->testcase->create->requiredFields = str_replace('product', '', $this->config->testcase->create->requiredFields);
|
||
|
||
$this->dao->insert(TABLE_CASE)->data($case, 'steps,expects,files,labels,stepType,needReview,scriptFile,scriptName')
|
||
->autoCheck()
|
||
->batchCheck($this->config->testcase->create->requiredFields, 'notempty')
|
||
->checkFlow()
|
||
->exec();
|
||
return !dao::isError();
|
||
}
|
||
|
||
/**
|
||
* 创建一个测试用例名称和前置条件。
|
||
* Create a test case spec.
|
||
*
|
||
* @param int $caseID
|
||
* @param object $case
|
||
* @param array|string $files
|
||
* @access protected
|
||
* @return void
|
||
*/
|
||
protected function doCreateSpec(int $caseID, object $case, array|string $files = array()): void
|
||
{
|
||
if(empty($caseID)) return;
|
||
|
||
$spec = new stdclass();
|
||
$spec->case = $caseID;
|
||
$spec->version = zget($case, 'version', 1);
|
||
$spec->title = $case->title;
|
||
$spec->precondition = zget($case, 'precondition', '');
|
||
$spec->files = is_string($files) ? $files : implode(',', array_keys($files));
|
||
|
||
$this->dao->insert(TABLE_CASESPEC)->data($spec)->exec();
|
||
}
|
||
|
||
/**
|
||
* 更新一个测试用例。
|
||
* Update a test case.
|
||
*
|
||
* @param object $case
|
||
* @access protected
|
||
* @return bool
|
||
*/
|
||
protected function doUpdate(object $case): bool
|
||
{
|
||
/* Remove the require field named story when the case is a lib case.*/
|
||
$requiredFields = $this->config->testcase->edit->requiredFields;
|
||
if(!empty($case->lib)) $requiredFields = str_replace(array('story', 'scene'), '', $requiredFields);
|
||
|
||
$this->dao->update(TABLE_CASE)->data($case, 'deleteFiles,uid,stepChanged,comment,steps,expects,stepType,linkBug,renameFiles,addedFiles,files')
|
||
->autoCheck()
|
||
->batchCheck($requiredFields, 'notempty')
|
||
->checkFlow()
|
||
->where('id')->eq((int)$case->id)
|
||
->exec();
|
||
return !dao::isError();
|
||
}
|
||
|
||
/*
|
||
* 处理用例和项目的关系。
|
||
* Deal with the relationship between the case and project when edit the case.
|
||
*
|
||
* @param object $oldCase
|
||
* @param object $case
|
||
* @access public
|
||
* @return void
|
||
*/
|
||
protected function updateCase2Project(object $oldCase, object $case): bool
|
||
{
|
||
$productChanged = $oldCase->product != $case->product;
|
||
$storyChanged = $oldCase->story != zget($case, 'story', $oldCase->story);
|
||
|
||
if(!$productChanged && !$storyChanged) return true;
|
||
|
||
if($productChanged) $this->dao->update(TABLE_PROJECTCASE)->set('product')->eq($case->product)->set('version')->eq($case->version)->where('`case`')->eq($oldCase->id)->exec();
|
||
|
||
if($storyChanged)
|
||
{
|
||
/* 取消之前需求对应项目和用例的关联关系。*/
|
||
/* If the new related story isn't linked the project, unlink the case. */
|
||
$projects = $this->dao->select('project')->from(TABLE_PROJECTSTORY)->where('story')->eq($oldCase->story)->fetchAll('project');
|
||
$this->dao->delete()->from(TABLE_PROJECTCASE)->where('project')->in(array_keys($projects))->andWhere('`case`')->eq($oldCase->id)->exec();
|
||
|
||
/* 设置需求对应项目和用例的关联关系。*/
|
||
/* If the new related story is not null, make the case link the project which link the new related story. */
|
||
if(!empty($case->story))
|
||
{
|
||
$projects = $this->dao->select('project')->from(TABLE_PROJECTSTORY)->where('story')->eq($case->story)->fetchAll('project');
|
||
if($projects)
|
||
{
|
||
$projects = array_keys($projects);
|
||
$lastOrders = $this->dao->select('project, MAX(`order`) AS lastOrder')->from(TABLE_PROJECTCASE)->where('project')->in($projects)->groupBy('project')->fetchPairs();
|
||
|
||
foreach($projects as $projectID)
|
||
{
|
||
$lastOrder = isset($lastOrders[$projectID]) ? $lastOrders[$projectID] : 0;
|
||
|
||
$data = new stdclass();
|
||
$data->project = $projectID;
|
||
$data->product = $case->product;
|
||
$data->case = $oldCase->id;
|
||
$data->version = $oldCase->version;
|
||
$data->order = ++ $lastOrder;
|
||
|
||
$this->dao->replace(TABLE_PROJECTCASE)->data($data)->autoCheck()->exec();
|
||
}
|
||
}
|
||
}
|
||
}
|
||
|
||
return !dao::isError();
|
||
}
|
||
|
||
/**
|
||
* 更新用例步骤。
|
||
* Update step.
|
||
*
|
||
* @param object $case
|
||
* @param object $oldCase
|
||
* @access protected
|
||
* @return bool
|
||
*/
|
||
protected function updateStep(object $case, object $oldCase): bool
|
||
{
|
||
if($oldCase->lib && empty($oldCase->product))
|
||
{
|
||
$fromcaseVersion = $this->dao->select('fromCaseVersion')->from(TABLE_CASE)->where('fromCaseID')->eq($case->id)->fetch('fromCaseVersion');
|
||
$fromcaseVersion = (int)$fromcaseVersion + 1;
|
||
$this->dao->update(TABLE_CASE)->set('`fromCaseVersion`')->eq($fromcaseVersion)->where('`fromCaseID`')->eq($case->id)->exec();
|
||
}
|
||
|
||
if($case->steps)
|
||
{
|
||
$this->insertSteps($oldCase->id, $case->steps, $case->expects, (array)$case->stepType, $case->version);
|
||
}
|
||
else
|
||
{
|
||
foreach($oldCase->steps as $step)
|
||
{
|
||
unset($step->id);
|
||
unset($step->name);
|
||
unset($step->step);
|
||
unset($step->grade);
|
||
|
||
$step->case = $oldCase->id;
|
||
$step->version = $case->version;
|
||
$this->dao->insert(TABLE_CASESTEP)->data($step)->autoCheck()->exec();
|
||
}
|
||
}
|
||
|
||
return !dao::isError();
|
||
}
|
||
|
||
/**
|
||
* 更新用例和 bug 的关联关系。
|
||
* Link bugs with case.
|
||
*
|
||
* @param int $caseID
|
||
* @param array $linkedBugs
|
||
* @param object $case
|
||
* @access protected
|
||
* @return bool
|
||
*/
|
||
protected function linkBugs(int $caseID, array $linkedBugs, object $case): bool
|
||
{
|
||
$toLinkBugs = $case->linkBug;
|
||
$newBugs = array_diff($toLinkBugs, $linkedBugs);
|
||
$removeBugs = array_diff($linkedBugs, $toLinkBugs);
|
||
|
||
foreach($newBugs as $bugID)
|
||
{
|
||
$this->dao->update(TABLE_BUG)->set('`case`')->eq($caseID)->set('caseVersion')->eq($case->version)->set('`story`')->eq($case->story)->set('storyVersion')->eq($case->storyVersion)->where('id')->eq($bugID)->exec();
|
||
|
||
if($this->config->edition != 'open')
|
||
{
|
||
$relation = new stdClass();
|
||
$relation->relation = 'generated';
|
||
$relation->AID = $bugID;
|
||
$relation->AType = 'bug';
|
||
$relation->BID = $caseID;
|
||
$relation->BType = 'testcase';
|
||
$relation->product = 0;
|
||
$this->dao->replace(TABLE_RELATION)->data($relation)->exec();
|
||
}
|
||
}
|
||
foreach($removeBugs as $bugID)
|
||
{
|
||
$this->dao->update(TABLE_BUG)->set('`case`')->eq(0)->set('caseVersion')->eq(0)->set('`story`')->eq(0)->set('storyVersion')->eq(0)->where('id')->eq($bugID)->exec();
|
||
|
||
if($this->config->edition != 'open')
|
||
{
|
||
$this->dao->delete()->from(TABLE_RELATION)
|
||
->where('relation')->eq('generated')
|
||
->andWhere('AID')->eq($bugID)
|
||
->andWhere('AType')->eq('bug')
|
||
->andWhere('BID')->eq($caseID)
|
||
->andWhere('BType')->eq('testcase')
|
||
->exec();
|
||
}
|
||
}
|
||
|
||
return !dao::isError();
|
||
}
|
||
|
||
/**
|
||
* 解除用例和测试单的关联。
|
||
* Unlink case from test task.
|
||
*
|
||
* @param int $caseID
|
||
* @param array $testtasks
|
||
* @access protected
|
||
* @return bool
|
||
*/
|
||
protected function unlinkCaseFromTesttask(int $caseID, int $branch, array $testtasks): bool
|
||
{
|
||
$this->loadModel('action');
|
||
foreach($testtasks as $taskID => $testtask)
|
||
{
|
||
if(strpos(",{$testtask->branch},", ",{$branch},") === false && $taskID)
|
||
{
|
||
$this->dao->delete()->from(TABLE_TESTRUN)->where('task')->eq($taskID)->andWhere('`case`')->eq($caseID)->exec();
|
||
$this->action->create('case' ,$caseID, 'unlinkedfromtesttask', '', $taskID);
|
||
}
|
||
}
|
||
|
||
return !dao::isError();
|
||
}
|
||
|
||
/**
|
||
* 获取待评审的用例数量。
|
||
* Get the amount of cases pending review.
|
||
*
|
||
* @access protected
|
||
* @return int
|
||
*/
|
||
protected function getReviewAmount(): int
|
||
{
|
||
return $this->dao->select('COUNT(id) AS count')->from(TABLE_CASE)->where('status')->eq('wait')->fetch('count');
|
||
}
|
||
|
||
/**
|
||
* 导入步骤。
|
||
* Import steps.
|
||
*
|
||
* @param int $caseID
|
||
* @param array $steps
|
||
* @access public
|
||
* @return bool
|
||
*/
|
||
public function importSteps(int $caseID, array $steps): bool
|
||
{
|
||
/* 插入步骤。 */
|
||
/* Insert steps. */
|
||
$parentSteps = array();
|
||
foreach($steps as $stepID => $step)
|
||
{
|
||
$step->case = $caseID;
|
||
if(!empty($parentSteps[$step->parent])) $step->parent = $parentSteps[$step->parent];
|
||
unset($step->id);
|
||
|
||
$this->dao->insert(TABLE_CASESTEP)->data($step)->exec();
|
||
|
||
$parentSteps[$stepID] = $this->dao->lastInsertID();
|
||
}
|
||
return !dao::isError();
|
||
}
|
||
|
||
/**
|
||
* 导入文件。
|
||
* Import files.
|
||
*
|
||
* @param int $caseID
|
||
* @param array $files
|
||
* @access public
|
||
* @return bool
|
||
*/
|
||
public function importFiles(int $caseID, array $files): bool
|
||
{
|
||
/* 插入文件。 */
|
||
/* Insert files. */
|
||
foreach($files as $fileID => $file)
|
||
{
|
||
if(isset($file->oldpathname))
|
||
{
|
||
$file->pathname = str_replace('.', "copy{$caseID}.", $file->oldpathname);
|
||
if(!empty($file->oldpathname))
|
||
{
|
||
$originName = pathinfo($file->oldpathname, PATHINFO_FILENAME);
|
||
$datePath = substr($file->oldpathname, 0, 6);
|
||
$originFile = $this->app->getAppRoot() . "www/data/upload/{$this->app->company->id}/" . "{$datePath}/" . $originName;
|
||
|
||
$copyName = $originName . 'copy' . $caseID;
|
||
$copyFile = $this->app->getAppRoot() . "www/data/upload/{$this->app->company->id}/" . "{$datePath}/" . $copyName;
|
||
copy($originFile, $copyFile);
|
||
}
|
||
|
||
unset($file->oldpathname);
|
||
}
|
||
|
||
$file->objectID = $caseID;
|
||
$file->addedBy = $this->app->user->account;
|
||
$file->addedDate = helper::now();
|
||
$file->downloads = 0;
|
||
unset($file->id);
|
||
$this->dao->insert(TABLE_FILE)->data($file)->exec();
|
||
}
|
||
return !dao::isError();
|
||
}
|
||
|
||
/**
|
||
* 保存场景。
|
||
* Save scene.
|
||
*
|
||
* @param array $sceneData
|
||
* @param array $sceneList
|
||
* @access protected
|
||
* @return array
|
||
*/
|
||
protected function saveScene(array $sceneData, array $sceneList): array
|
||
{
|
||
$scene = new stdclass();
|
||
$scene->title = $sceneData['name'];
|
||
$scene->product = $sceneData['product'];
|
||
$scene->branch = $sceneData['branch'];
|
||
$scene->module = $sceneData['module'] ?? 0;
|
||
|
||
if(!isset($sceneData['id']))
|
||
{
|
||
$scene->openedBy = $this->app->user->account;
|
||
$scene->openedDate = helper::now();
|
||
|
||
$this->dao->insert(TABLE_SCENE)->data($scene)->autoCheck()->exec();
|
||
$sceneID = $this->dao->lastInsertID();
|
||
|
||
$this->dao->update(TABLE_SCENE)->set('sort')->eq($sceneID)->where('id')->eq($sceneID)->exec();
|
||
}
|
||
else
|
||
{
|
||
$sceneID = $sceneData['id'];
|
||
|
||
$scene->lastEditedBy = $this->app->user->account;
|
||
$scene->lastEditedDate = helper::now();
|
||
|
||
$affectedRows = $this->dao->update(TABLE_SCENE)->data($scene)->where('id')->eq($sceneID)->exec();
|
||
if(empty($affectedRows)) return array('result' => 'fail', 'message' => sprintf($this->lang->testcase->errorSceneNotExist, $sceneID));
|
||
}
|
||
|
||
if(dao::isError()) return array('result' => 'fail', 'message' => dao::getError());
|
||
|
||
$tmpPId = $sceneData['tmpPId'];
|
||
$pScene = zget($sceneList, $tmpPId, array());
|
||
$this->fixScenePath($sceneID, $pScene);
|
||
|
||
if(dao::isError()) return array('result' => 'fail', 'message' => dao::getError());
|
||
|
||
return array('result' => 'success', 'message' => $this->lang->saveSuccess, 'sceneID' => $sceneID);
|
||
}
|
||
|
||
/**
|
||
* 调整场景的路径。
|
||
* Fix the scene path.
|
||
*
|
||
* @param int $sceneID
|
||
* @param array $pScene
|
||
* @access protected
|
||
* @return bool
|
||
*/
|
||
protected function fixScenePath(int $sceneID, array $pScene = array()): bool
|
||
{
|
||
$parent = 0;
|
||
$grade = 1;
|
||
$path = ",{$sceneID},";
|
||
|
||
if(!empty($pScene))
|
||
{
|
||
$parent = $pScene['id'];
|
||
$parentScene = $this->dao->findById((int)$parent)->from(TABLE_SCENE)->fetch();
|
||
$path = $parentScene->path . "{$sceneID},";
|
||
$grade = $parentScene->grade + 1;
|
||
}
|
||
|
||
$this->dao->update(TABLE_SCENE)->set('parent')->eq($parent)->set('path')->eq($path)->set('grade')->eq($grade)->where('id')->eq($sceneID)->exec();
|
||
|
||
return !dao::isError();
|
||
}
|
||
|
||
/**
|
||
* 基于用例构建场景数据。
|
||
* Build scene base on case.
|
||
*
|
||
* @param object $scene
|
||
* @param array $fieldTypes
|
||
* @access public
|
||
* @return object
|
||
*/
|
||
public function buildSceneBaseOnCase(object $scene, array $fieldTypes): object
|
||
{
|
||
/* Set default value for the fields exist in TABLE_CASE but not in TABLE_SCENE. */
|
||
foreach($fieldTypes as $field => $type)
|
||
{
|
||
if(!isset($scene->{$field})) $scene->{$field} = $type['rule'] == 'int' ? '0' : '';
|
||
}
|
||
|
||
$scene->caseID = $scene->id;
|
||
$scene->bugs = 0;
|
||
$scene->results = 0;
|
||
$scene->caseFails = 0;
|
||
$scene->stepNumber = 0;
|
||
$scene->isScene = true;
|
||
|
||
return $scene;
|
||
}
|
||
|
||
/**
|
||
* 通过搜索获取执行的用例。
|
||
* Get execution cases by search.
|
||
*
|
||
* @param int $executionID
|
||
* @param int $productID
|
||
* @param int $branchID
|
||
* @param int $paramID
|
||
* @param string $orderBy
|
||
* @param object $pager
|
||
* @access public
|
||
* @return array
|
||
* */
|
||
public function getExecutionCasesBySearch(int $executionID = 0, int $productID = 0, int|string $branchID = 0, int $paramID = 0, string $orderBy = 'id_desc', ?object $pager = null): array
|
||
{
|
||
if($paramID)
|
||
{
|
||
$query = $this->loadModel('search')->getQuery($paramID);
|
||
if($query)
|
||
{
|
||
$this->session->set('executionCaseQuery', $query->sql);
|
||
$this->session->set('executionCaseForm', $query->form);
|
||
}
|
||
}
|
||
if($this->session->executionCaseQuery === false) $this->session->set('executionCaseQuery', ' 1 = 1');
|
||
|
||
$caseQuery = '(' . $this->session->executionCaseQuery;
|
||
|
||
/* 处理用例查询中的产品条件。*/
|
||
if(strpos($this->session->executionCaseQuery, "`product` = 'all'") !== false)
|
||
{
|
||
$caseQuery = str_replace("`product` = 'all'", '1 = 1', $caseQuery);
|
||
$caseQuery .= ' AND t2.`product` ' . helper::dbIN($this->app->user->view->products);
|
||
}
|
||
elseif($productID)
|
||
{
|
||
$caseQuery .= " AND t2.`product` ='$productID'";
|
||
}
|
||
|
||
/* 处理用例查询中的产品分支条件。*/
|
||
if(strpos($caseQuery, "`branch` = 'all'") !== false) $caseQuery = str_replace("`branch` = 'all'", '1 = 1', $caseQuery);
|
||
if($branchID !== 'all' && strpos($caseQuery, '`branch` =') === false) $caseQuery .= " AND t2.`branch` in ('$branchID')";
|
||
|
||
/* 处理用例查询中的版本条件。*/
|
||
$caseQuery = str_replace(array('`version`', ' `product`', ' `project`'), array('t2.`version`', ' t2.`product`', ' t1.`project`'), $caseQuery);
|
||
$caseQuery .= ')';
|
||
|
||
return $this->dao->select('distinct t1.*, t2.*')->from(TABLE_PROJECTCASE)->alias('t1')
|
||
->leftJoin(TABLE_CASE)->alias('t2')->on('t1.case = t2.id')
|
||
->where('t1.project')->eq($executionID)
|
||
->andWhere('t2.deleted')->eq('0')
|
||
->andWhere($caseQuery)
|
||
->orderBy($orderBy)
|
||
->page($pager)
|
||
->fetchAll('id');
|
||
}
|
||
}
|