Merge branch release/21.7.3 of zentao/zentaopms (#10163)

This commit is contained in:
刘刚
2025-07-03 13:50:00 +08:00
committed by Gitfox
138 changed files with 2393 additions and 1299 deletions
+3 -3
View File
@@ -6,13 +6,13 @@
},
"zentaoext": {
"gitRepo": "zentao/zentaoext",
"gitVersion": "release/12.1"
"gitVersion": "release/12.2"
},
"zentaomax": {
"gitVersion": "release/7.1"
"gitVersion": "release/7.2"
},
"zentaoipd": {
"gitVersion": "release/4.1"
"gitVersion": "release/4.2"
},
"devops": {
"gitRepo": "zentao/devops"
+1
View File
@@ -363,6 +363,7 @@ $config->logonMethods[] = 'user.deny';
$config->logonMethods[] = 'user.logout';
$config->logonMethods[] = 'zanode.nodelist';
$config->logonMethods[] = 'screen.viewold';
$config->logonMethods[] = 'system.backupview';
$config->openModules = array();
$config->openModules[] = 'install';
+11 -9
View File
@@ -2338,11 +2338,11 @@ CREATE TABLE `zt_metriclib` (
`id` bigint(20) unsigned NOT NULL AUTO_INCREMENT,
`metricID` int(11) unsigned NOT NULL DEFAULT '0',
`metricCode` varchar(100) NOT NULL DEFAULT '',
`system` tinyint(1) NOT NULL DEFAULT '0',
`program` int(11) NOT NULL DEFAULT '0',
`project` int(11) NOT NULL DEFAULT '0',
`product` int(11) NOT NULL DEFAULT '0',
`execution` int(11) NOT NULL DEFAULT '0',
`system` tinyint(1) unsigned NOT NULL DEFAULT '0',
`program` int(11) unsigned NOT NULL DEFAULT '0',
`project` int(11) unsigned NOT NULL DEFAULT '0',
`product` int(11) unsigned NOT NULL DEFAULT '0',
`execution` int(11) unsigned NOT NULL DEFAULT '0',
`code` char(30) NOT NULL DEFAULT '',
`pipeline` char(30) NOT NULL DEFAULT '',
`repo` char(30) NOT NULL DEFAULT '',
@@ -2358,10 +2358,12 @@ CREATE TABLE `zt_metriclib` (
`date` datetime DEFAULT NULL,
`deleted` enum('0','1') NOT NULL DEFAULT '0',
PRIMARY KEY (`id`),
KEY `metricID` (`metricID`),
KEY `metricCode` (`metricCode`),
KEY `date` (`date`),
KEY `deleted` (`deleted`)
KEY `metricCode_system_date` (`metricCode`, `system`, `date`),
KEY `metricCode_program_date` (`metricCode`, `program`, `date`),
KEY `metricCode_project_date` (`metricCode`, `project`, `date`),
KEY `metricCode_product_date` (`metricCode`, `product`, `date`),
KEY `metricCode_execution_date` (`metricCode`, `execution`, `date`),
KEY `metricCode_user_date` (`metricCode`, `user`, `date`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
CREATE TABLE `zt_module` (
`id` mediumint(8) unsigned NOT NULL AUTO_INCREMENT,
+4 -1
View File
@@ -48,4 +48,7 @@ UPDATE `zt_workflowfield` SET `options` = (SELECT `id` FROM `zt_workflowdatasour
UPDATE `zt_workflowfield` SET `options` = (SELECT `id` FROM `zt_workflowdatasource` WHERE `code` = 'charterReviewStatus' LIMIT 1) WHERE `module` = 'charter' AND `field` = 'reviewStatus';
CREATE OR REPLACE VIEW `ztv_executionsummary` AS SELECT `zt_task`.`execution` AS `execution`,SUM(IF((`zt_task`.`isParent` > '0'),`zt_task`.`estimate`,0)) AS `estimate`,SUM(IF((`zt_task`.`isParent` > '0'),`zt_task`.`consumed`,0)) AS `consumed`,SUM(IF(((`zt_task`.`status` <> 'cancel') AND (`zt_task`.`status` <> 'closed') AND (`zt_task`.`isParent` > '0')),`zt_task`.`left`,0)) AS `left`,COUNT(0) AS `number`,SUM(IF(((`zt_task`.`status` <> 'done') AND (`zt_task`.`status` <> 'closed')),1,0)) AS `undone`,SUM(IF(`zt_task`.`isParent` > '0',`zt_task`.`consumed`,0) + IF(((`zt_task`.`status` <> 'cancel') AND (`zt_task`.`status` <> 'closed') AND (`zt_task`.`isParent` > '0')),`zt_task`.`left`,0)) AS `totalReal` FROM `zt_task` WHERE (`zt_task`.`deleted` = '0') GROUP BY `zt_task`.`execution`;
CREATE OR REPLACE VIEW `ztv_projectsummary` AS SELECT `zt_task`.`project` AS `project`,SUM(IF((`zt_task`.`isParent` > '0'),`zt_task`.`estimate`,0)) AS `estimate`,SUM(IF((`zt_task`.`isParent` > '0'),`zt_task`.`consumed`,0)) AS `consumed`,SUM(IF(((`zt_task`.`status` <> 'cancel') AND (`zt_task`.`status` <> 'closed') AND (`zt_task`.`isParent` > '0')),`zt_task`.`left`,0)) AS `left`,COUNT(0) AS `number`,SUM(IF(((`zt_task`.`status` <> 'done') AND (`zt_task`.`status` <> 'closed')),1,0)) AS `undone`,SUM(IF(`zt_task`.`isParent` > '0',`zt_task`.`consumed`,0) + IF(((`zt_task`.`status` <> 'cancel') AND (`zt_task`.`status` <> 'closed') AND (`zt_task`.`isParent` > '0')),`zt_task`.`left`,0)) AS `totalReal` FROM `zt_task` WHERE (`zt_task`.`deleted` = '0') GROUP BY `zt_task`.`project`;
CREATE OR REPLACE VIEW `ztv_projectsummary` AS SELECT `zt_task`.`project` AS `project`,SUM(IF((`zt_task`.`isParent` > '0'),`zt_task`.`estimate`,0)) AS `estimate`,SUM(IF((`zt_task`.`isParent` > '0'),`zt_task`.`consumed`,0)) AS `consumed`,SUM(IF(((`zt_task`.`status` <> 'cancel') AND (`zt_task`.`status` <> 'closed') AND (`zt_task`.`isParent` > '0')),`zt_task`.`left`,0)) AS `left`,COUNT(0) AS `number`,SUM(IF(((`zt_task`.`status` <> 'done') AND (`zt_task`.`status` <> 'closed')),1,0)) AS `undone`,SUM(IF(`zt_task`.`isParent` > '0',`zt_task`.`consumed`,0) + IF(((`zt_task`.`status` <> 'cancel') AND (`zt_task`.`status` <> 'closed') AND (`zt_task`.`isParent` > '0')),`zt_task`.`left`,0)) AS `totalReal` FROM `zt_task` WHERE (`zt_task`.`deleted` = '0') GROUP BY `zt_task`.`project`;
ALTER TABLE `zt_history` MODIFY `new` longtext NULL;
ALTER TABLE `zt_history` MODIFY `old` longtext NULL;
+25
View File
@@ -0,0 +1,25 @@
ALTER TABLE `zt_project` ADD `tplAcl` char(30) NOT NULL DEFAULT 'open' AFTER `whitelist`;
ALTER TABLE `zt_project` ADD `tplWhiteList` text NULL AFTER `tplAcl`;
INSERT INTO `zt_workflowdatasource` (`type`, `name`, `code`, `buildin`, `vision`, `createdBy`, `createdDate`, `datasource`, `view`, `keyField`, `valueField`)
SELECT 'lang', '立项级别', 'charterLevel', '1', 'rnd', 'admin', '1970-01-01 00:00:01', 'charterLevel', '', '', '' FROM DUAL WHERE NOT EXISTS ( SELECT 1 FROM `zt_workflowdatasource` WHERE `type` = 'lang' AND `code` = 'charterLevel');
INSERT INTO `zt_workflowdatasource` (`type`, `name`, `code`, `buildin`, `vision`, `createdBy`, `createdDate`, `datasource`, `view`, `keyField`, `valueField`)
SELECT 'lang', '立项类型', 'charterCategory', '1', 'rnd', 'admin', '1970-01-01 00:00:01', 'charterCategory', '', '', '' FROM DUAL WHERE NOT EXISTS ( SELECT 1 FROM `zt_workflowdatasource` WHERE `type` = 'lang' AND `code` = 'charterCategory');
INSERT INTO `zt_workflowdatasource` (`type`, `name`, `code`, `buildin`, `vision`, `createdBy`, `createdDate`, `datasource`, `view`, `keyField`, `valueField`)
SELECT 'lang', '立项适用市场', 'charterMarket', '1', 'rnd', 'admin', '1970-01-01 00:00:01', 'charterMarket', '', '', '' FROM DUAL WHERE NOT EXISTS ( SELECT 1 FROM `zt_workflowdatasource` WHERE `type` = 'lang' AND `code` = 'charterMarket');
INSERT INTO `zt_workflowdatasource` (`type`, `name`, `code`, `buildin`, `vision`, `createdBy`, `createdDate`, `datasource`, `view`, `keyField`, `valueField`)
SELECT 'lang', '立项状态', 'charterStatus', '1', 'rnd', 'admin', '1970-01-01 00:00:01', 'charterStatus', '', '', '' FROM DUAL WHERE NOT EXISTS ( SELECT 1 FROM `zt_workflowdatasource` WHERE `type` = 'lang' AND `code` = 'charterStatus');
INSERT INTO `zt_workflowdatasource` (`type`, `name`, `code`, `buildin`, `vision`, `createdBy`, `createdDate`, `datasource`, `view`, `keyField`, `valueField`)
SELECT 'lang', '立项关闭原因', 'charterCloseReason', '1', 'rnd', 'admin', '1970-01-01 00:00:01', 'charterCloseReason', '', '', '' FROM DUAL WHERE NOT EXISTS ( SELECT 1 FROM `zt_workflowdatasource` WHERE `type` = 'lang' AND `code` = 'charterCloseReason');
INSERT INTO `zt_workflowdatasource` (`type`, `name`, `code`, `buildin`, `vision`, `createdBy`, `createdDate`, `datasource`, `view`, `keyField`, `valueField`)
SELECT 'lang', '立项审批结果', 'charterReviewResult', '1', 'rnd', 'admin', '1970-01-01 00:00:01', 'charterReviewResult', '', '', '' FROM DUAL WHERE NOT EXISTS ( SELECT 1 FROM `zt_workflowdatasource` WHERE `type` = 'lang' AND `code` = 'charterReviewResult');
INSERT INTO `zt_workflowdatasource` (`type`, `name`, `code`, `buildin`, `vision`, `createdBy`, `createdDate`, `datasource`, `view`, `keyField`, `valueField`)
SELECT 'lang', '立项审批状态', 'charterReviewStatus', '1', 'rnd', 'admin', '1970-01-01 00:00:01', 'charterReviewStatus', '', '', '' FROM DUAL WHERE NOT EXISTS ( SELECT 1 FROM `zt_workflowdatasource` WHERE `type` = 'lang' AND `code` = 'charterReviewStatus');
UPDATE `zt_workflowfield` SET `options` = (SELECT `id` FROM `zt_workflowdatasource` WHERE `code` = 'charterLevel' LIMIT 1) WHERE `module` = 'charter' AND `field` = 'level';
UPDATE `zt_workflowfield` SET `options` = (SELECT `id` FROM `zt_workflowdatasource` WHERE `code` = 'charterCategory' LIMIT 1) WHERE `module` = 'charter' AND `field` = 'category';
UPDATE `zt_workflowfield` SET `options` = (SELECT `id` FROM `zt_workflowdatasource` WHERE `code` = 'charterMarket' LIMIT 1) WHERE `module` = 'charter' AND `field` = 'market';
UPDATE `zt_workflowfield` SET `options` = (SELECT `id` FROM `zt_workflowdatasource` WHERE `code` = 'charterStatus' LIMIT 1) WHERE `module` = 'charter' AND `field` = 'status';
UPDATE `zt_workflowfield` SET `options` = (SELECT `id` FROM `zt_workflowdatasource` WHERE `code` = 'charterCloseReason' LIMIT 1) WHERE `module` = 'charter' AND `field` = 'closedReason';
UPDATE `zt_workflowfield` SET `options` = (SELECT `id` FROM `zt_workflowdatasource` WHERE `code` = 'charterReviewResult' LIMIT 1) WHERE `module` = 'charter' AND `field` = 'reviewedResult';
UPDATE `zt_workflowfield` SET `options` = (SELECT `id` FROM `zt_workflowdatasource` WHERE `code` = 'charterReviewStatus' LIMIT 1) WHERE `module` = 'charter' AND `field` = 'reviewStatus';
+15 -13
View File
@@ -1058,9 +1058,9 @@ CREATE TABLE IF NOT EXISTS `zt_history` (
`id` int(11) unsigned NOT NULL auto_increment,
`action` mediumint(8) unsigned NOT NULL default '0',
`field` varchar(30) NOT NULL default '',
`old` text NULL,
`old` longtext NULL,
`oldValue` text NULL,
`new` text NULL,
`new` longtext NULL,
`newValue` text NULL,
`diff` mediumtext NULL,
PRIMARY KEY (`id`)
@@ -1615,6 +1615,8 @@ CREATE TABLE IF NOT EXISTS `zt_project` (
`team` varchar(90) NOT NULL DEFAULT '',
`acl` char(30) NOT NULL DEFAULT 'open',
`whitelist` text NULL,
`tplAcl` char(30) NOT NULL DEFAULT 'open',
`tplWhiteList` text NULL,
`order` mediumint(8) unsigned NOT NULL DEFAULT '0',
`vision` varchar(10) NOT NULL DEFAULT 'rnd',
`stageBy` enum('project','product') NOT NULL DEFAULT 'product',
@@ -16174,11 +16176,11 @@ CREATE TABLE IF NOT EXISTS `zt_metriclib` (
`id` bigint(20) unsigned NOT NULL AUTO_INCREMENT,
`metricID` int(11) unsigned NOT NULL DEFAULT '0',
`metricCode` varchar(100) NOT NULL DEFAULT '',
`system` tinyint(1) NOT NULL DEFAULT '0',
`program` int(11) NOT NULL DEFAULT '0',
`project` int(11) NOT NULL DEFAULT '0',
`product` int(11) NOT NULL DEFAULT '0',
`execution` int(11) NOT NULL DEFAULT '0',
`system` tinyint(1) unsigned NOT NULL DEFAULT '0',
`program` int(11) unsigned NOT NULL DEFAULT '0',
`project` int(11) unsigned NOT NULL DEFAULT '0',
`product` int(11) unsigned NOT NULL DEFAULT '0',
`execution` int(11) unsigned NOT NULL DEFAULT '0',
`code` char(30) NOT NULL DEFAULT '',
`pipeline` char(30) NOT NULL DEFAULT '',
`repo` char(30) NOT NULL DEFAULT '',
@@ -16195,12 +16197,12 @@ CREATE TABLE IF NOT EXISTS `zt_metriclib` (
`deleted` ENUM('0', '1') NOT NULL DEFAULT '0',
PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
CREATE INDEX `metricCode_system_date` ON `zt_metriclib`(`metricCode`, `system`, `date`);
CREATE INDEX `metricCode_program_date` ON `zt_metriclib`(`metricCode`, `program`, `date`);
CREATE INDEX `metricCode_project_date` ON `zt_metriclib`(`metricCode`, `project`, `date`);
CREATE INDEX `metricCode_product_date` ON `zt_metriclib`(`metricCode`, `product`, `date`);
CREATE INDEX `metricCode_execution_date` ON `zt_metriclib`(`metricCode`, `execution`, `date`);
CREATE INDEX `metricCode_user_date` ON `zt_metriclib`(`metricCode`, `user`, `date`);
CREATE INDEX `metricCode_system_date` ON `zt_metriclib` (`metricCode`, `system`, `date`);
CREATE INDEX `metricCode_program_date` ON `zt_metriclib` (`metricCode`, `program`, `date`);
CREATE INDEX `metricCode_project_date` ON `zt_metriclib` (`metricCode`, `project`, `date`);
CREATE INDEX `metricCode_product_date` ON `zt_metriclib` (`metricCode`, `product`, `date`);
CREATE INDEX `metricCode_execution_date` ON `zt_metriclib` (`metricCode`, `execution`, `date`);
CREATE INDEX `metricCode_user_date` ON `zt_metriclib` (`metricCode`, `user`, `date`);
-- DROP TABLE IF EXISTS `zt_duckdbqueue`;
CREATE TABLE IF NOT EXISTS `zt_duckdbqueue` (
+34 -20
View File
@@ -3548,31 +3548,45 @@ class baseRouter
}
}
/**
* config类。
* The config class.
*
* @package framework
*/
class config
if(version_compare(PHP_VERSION, '8.2.0', '>='))
{
#[\AllowDynamicProperties] // https://www.php.net/manual/zh/class.allowdynamicproperties.php
class config
{
public function set(string $key, $value)
{
helper::setMember('config', $key, $value);
}
}
}
else
{
/**
* 设置成员变量,成员可以是'db.user'类似的格式。
* Set the value of a member. the member can be the format like db.user.
* config类。
* The config class.
*
* <code>
* <?php
* $config->set('db.user', 'wwccss');
* ?>
* </code>
* @param string $key the key of the member
* @param mixed $value the value
* @access public
* @return void
* @package framework
*/
public function set(string $key, $value)
class config
{
helper::setMember('config', $key, $value);
/**
* 设置成员变量,成员可以是'db.user'类似的格式。
* Set the value of a member. the member can be the format like db.user.
*
* <code>
* <?php
* $config->set('db.user', 'wwccss');
* ?>
* </code>
* @param string $key the key of the member
* @param mixed $value the value
* @access public
* @return void
*/
public function set(string $key, $value)
{
helper::setMember('config', $key, $value);
}
}
}
+10
View File
@@ -534,6 +534,16 @@ class control extends baseControl
$uiID = $this->loadModel('workflowlayout')->getUIByData($flow->module, $action->action, $object);
$fieldList = $this->workflowaction->getPageFields($flow->module, $action->action, true, null, $uiID, $groupID);
/* 复制项目时显示被复制项目的工作流字段值。*/
if($moduleName == 'project' && $methodName == 'create')
{
foreach($fieldList as $field)
{
$objectValue = zget($object, $field->field, '');
if($objectValue) $field->default = $objectValue;
}
}
return $this->loadModel('flow')->buildFormFields($fields, $fieldList, array(), $object);
}
+1
View File
@@ -225,6 +225,7 @@ class dao extends baseDAO
$method = $this->app->getMethodName();
if($module == 'story' && $this->app->rawModule == 'requirement') $module = 'requirement';
if($module == 'story' && $this->app->rawModule == 'epic') $module = 'epic';
if($module == 'project' && $method == 'createtemplate') $method = 'create';
$flowAction = $this->dbh->query("SELECT * FROM " . TABLE_WORKFLOWACTION . " WHERE `module` = '{$module}' AND `action` = '{$method}' AND `buildin` = '1' AND `extensionType` = 'extend' AND `vision` = '{$this->config->vision}'")->fetch(PDO::FETCH_OBJ);
if(!$flowAction) return $this;
+4
View File
@@ -119,6 +119,10 @@ class form extends fixer
if($moduleName == 'projectrelease') $moduleName = 'release';
if($moduleName == 'projectbuild') $moduleName = 'build';
/* 项目复制用项目创建的工作流。 */
if($moduleName == 'project' && $methodName == 'copyconfirm') $methodName = 'create';
if($moduleName == 'project' && $methodName == 'edittemplate') $methodName = 'edit';
/* 用户需求和业务需求用自己的工作流。*/
if($moduleName == 'story' && $app->rawModule == 'requirement') $moduleName = 'requirement';
if($moduleName == 'story' && $app->rawModule == 'epic') $moduleName = 'epic';
+1 -1
View File
@@ -61,7 +61,7 @@ class backBtn extends btn
'build' => 'execution-build,build-view,project-index,kanban-view,testtask-browse,projectbuild-browse,my-effort,company-effort,company-calendar,my-index,project-dynamic,execution-dynamic,project-view,execution-view',
'projectbuild' => 'projectbuild-browse,projectbuild-view,project-index',
'repo' => 'repo-maintain,repo-log,repo-browse,repo-view,repo-review,mr-view,repo-browsewebhooks,repo-browserule',
'mr' => 'pullreq-browse,mr-browse',
'mr' => 'pullreq-browse,mr-browse,my-audit',
'pullreq' => 'pullreq-browse',
'compile' => 'compile-browse',
'store' => 'store-browse',
+1 -1
View File
@@ -70,7 +70,7 @@ class docList extends wg
set::multiple(true),
set::maxItemsCount(50),
set::menu(array('checkbox' => true)),
!empty($items) ? set::toolbar(true) : null
!empty($docs) ? set::toolbar(true) : null
) : null,
div(setClass('mt-2'), $docBox)
);
+1
View File
@@ -3,3 +3,4 @@
#pick-pop-admin-menu .dropmenu-item + .dropmenu-item {margin-top: 4px}
#pick-pop-admin-menu .dropmenu-item.active {background-color: unset;}
#pick-pop-admin-menu .dropmenu-item:hover {color: rgba(var(--color-primary-500-rgb),var(--tw-text-opacity)); background-color: rgba(var(--color-primary-50-rgb),var(--tw-bg-opacity));}
.dropmenu-item .item-content .label {flex-shrink: 0;}
+5 -1
View File
@@ -77,12 +77,13 @@ class formPanel extends panel
{
global $app;
$moduleName = $app->getModuleName();
$methodName = $app->getMethodName();
if($moduleName == 'caselib') return data('lib');
if($moduleName == 'flow') return data('data');
if($moduleName == 'productplan') return data('plan');
if($moduleName == 'projectrelease') return data('release');
if($moduleName == 'projectbuild') return data('build');
if($moduleName == 'project' && $app->getMethodName() == 'create') return data('copyProject');
if($moduleName == 'project' && ($methodName == 'create' or $methodName == 'createtemplate')) return data('copyProject') ? data('copyProject') : null;
return data($moduleName);
}
@@ -105,6 +106,9 @@ class formPanel extends panel
}
}
if($moduleName == 'project' && $methodName == 'createtemplate') $methodName = 'create';
if($moduleName == 'project' && $methodName == 'edittemplate') $methodName = 'edit';
/* 反馈转化。 */
if($moduleName == 'feedback')
{
+4 -1
View File
@@ -23,12 +23,15 @@ class remotePicker extends wg
if(!$items)
{
$type = $this->prop('type');
$params = $this->prop('params') ? $this->prop('params') : 'noclosed|nodeleted';
$params = $this->prop('params') ? $this->prop('params') : 'noclosed,nodeleted';
switch($type)
{
case 'user':
$items = createLink('user', 'ajaxGetItems', 'params=' . $params);
break;
default:
$items = createLink('user', 'ajaxGetItems', 'params=' . $params);
break;
}
$this->setProp('items', $items);
}
+1 -2
View File
@@ -218,7 +218,6 @@ class actionModel extends model
$this->loadModel('workflow');
foreach($actions as $actionID => $action)
{
$extra = $action->extra;
$actionName = strtolower($action->action);
if($this->config->edition != 'open' && !isset($flowList[$action->objectType])) $flowList[$action->objectType] = $this->workflow->getByModule($action->objectType);
@@ -1293,7 +1292,7 @@ class actionModel extends model
/* If idList include ',*,' Format ',*,' to '*'. */
$projectIdList = array();
foreach($projectGroups as $key => $idList)
foreach($projectGroups as $idList)
{
$idList = explode(',', (string)$idList);
foreach($idList as $id) $projectIdList[] = $id;
@@ -113,17 +113,12 @@ class actionTest
* @access public
* @return string
*/
public function getListTest(string $objectType, int $objectID, string $edition = 'open'): string
public function getListTest(string $objectType, int $objectID): string
{
global $tester;
$oldEdition = $tester->config->edition;
$tester->config->edition = $edition;
$objects = $this->objectModel->getList($objectType, $objectID);
$tester->config->edition = $oldEdition;
global $tester;
$modules = $objectType == 'module' ? $tester->dao->select('id')->from(TABLE_MODULE)->where('root')->in($objectID)->fetchPairs('id') : array();
$actions = $this->objectModel->getActionListByTypeAndID($objectType, $objectID, $modules);
@@ -22,9 +22,9 @@ cid=1
- 查找用户admin动态 @12
- 查找用户dev17动态 @12
- 查找用户test18动态 @11
- 查找今天的动态 @0
- 查找昨天的动态 @1
- 查找上周的动态 @3
- 查找今天的动态 @1
- 查找昨天的动态 @0
- 查找上周的动态 @2
- 查找今天的动态 @0
- 查找用户admin动态 @12
- 查找用户dev17动态 @12
@@ -50,9 +50,9 @@ r($action->getDynamicByAccountTest($accountList[3])) && p() && e('11'); // 查
zenData('action')->loadYaml('action_week')->gen(35, true, false);
zenData('actionrecent')->loadYaml('action_week')->gen(35, true, false);
r($action->getDynamicByAccountTest($accountList[1], $typeList[1])) && p() && e('0'); // 查找今天的动态
r($action->getDynamicByAccountTest($accountList[1], $typeList[2])) && p() && e('1'); // 查找昨天的动态
r($action->getDynamicByAccountTest($accountList[1], $typeList[3])) && p() && e('3'); // 查找上周的动态
r($action->getDynamicByAccountTest($accountList[1], $typeList[1])) && p() && e('1'); // 查找今天的动态
r($action->getDynamicByAccountTest($accountList[1], $typeList[2])) && p() && e('0'); // 查找昨天的动态
r($action->getDynamicByAccountTest($accountList[1], $typeList[3])) && p() && e('2'); // 查找上周的动态
zenData('action')->loadYaml('action_year')->gen(35, true, false);
zenData('actionrecent')->loadYaml('action_year')->gen(35, true, false);
@@ -27,9 +27,9 @@ cid=1
- 查找用户admin动态 @12
- 查找用户dev17动态 @12
- 查找用户test18动态 @11
- 查找今天的动态 @0
- 查找昨天的动态 @1
- 查找上周的动态 @3
- 查找今天的动态 @1
- 查找昨天的动态 @0
- 查找上周的动态 @2
- 查找今天的动态 @5
*/
@@ -53,9 +53,9 @@ $action->gen(35, true, false);
$actionrecent = zenData('actionrecent')->loadYaml('action_week');
$actionrecent->execution->range('101');
$actionrecent->gen(35, true, false);
r($actionTest->getDynamicByExecutionTest($executionID, $accountList[1], $typeList[1])) && p() && e('0'); // 查找今天的动态
r($actionTest->getDynamicByExecutionTest($executionID, $accountList[1], $typeList[2])) && p() && e('1'); // 查找昨天的动态
r($actionTest->getDynamicByExecutionTest($executionID, $accountList[1], $typeList[3])) && p() && e('3'); // 查找上周的动态
r($actionTest->getDynamicByExecutionTest($executionID, $accountList[1], $typeList[1])) && p() && e('1'); // 查找今天的动态
r($actionTest->getDynamicByExecutionTest($executionID, $accountList[1], $typeList[2])) && p() && e('0'); // 查找昨天的动态
r($actionTest->getDynamicByExecutionTest($executionID, $accountList[1], $typeList[3])) && p() && e('2'); // 查找上周的动态
$action = zenData('action')->loadYaml('action_year');
$action->execution->range('101');
@@ -23,8 +23,8 @@ cid=1
- 查找用户admin动态 @3
- 查找用户dev17动态 @2
- 查找用户test18动态 @2
- 查找今天的动态 @0
- 查找昨天的动态 @1
- 查找今天的动态 @1
- 查找昨天的动态 @0
- 查找上周的动态 @0
- 查找今天的动态 @1
@@ -45,8 +45,8 @@ r($actionTest->getDynamicByProductTest($productID, $accountList[3])) && p() && e
zenData('action')->loadYaml('action_week')->gen(35, true, false);
zenData('actionrecent')->loadYaml('action_week')->gen(35, true, false);
r($actionTest->getDynamicByProductTest($productID, $accountList[1], $typeList[1])) && p() && e('0'); // 查找今天的动态
r($actionTest->getDynamicByProductTest($productID, $accountList[1], $typeList[2])) && p() && e('1'); // 查找昨天的动态
r($actionTest->getDynamicByProductTest($productID, $accountList[1], $typeList[1])) && p() && e('1'); // 查找今天的动态
r($actionTest->getDynamicByProductTest($productID, $accountList[1], $typeList[2])) && p() && e('0'); // 查找昨天的动态
r($actionTest->getDynamicByProductTest($productID, $accountList[1], $typeList[3])) && p() && e('0'); // 查找上周的动态
zenData('action')->loadYaml('action_year')->gen(35, true, false);
@@ -27,9 +27,9 @@ cid=1
- 查找用户admin动态 @12
- 查找用户dev17动态 @12
- 查找用户test18动态 @11
- 查找今天的动态 @0
- 查找昨天的动态 @1
- 查找上周的动态 @3
- 查找今天的动态 @1
- 查找昨天的动态 @0
- 查找上周的动态 @2
- 查找今天的动态 @5
*/
@@ -53,9 +53,9 @@ $action->gen(35, true, false);
$actionrecent = zenData('actionrecent')->loadYaml('action_week');
$actionrecent->project->range('101');
$actionrecent->gen(35, true, false);
r($actionTest->getDynamicByProjectTest($projectID, $accountList[1], $typeList[1])) && p() && e('0'); // 查找今天的动态
r($actionTest->getDynamicByProjectTest($projectID, $accountList[1], $typeList[2])) && p() && e('1'); // 查找昨天的动态
r($actionTest->getDynamicByProjectTest($projectID, $accountList[1], $typeList[3])) && p() && e('3'); // 查找上周的动态
r($actionTest->getDynamicByProjectTest($projectID, $accountList[1], $typeList[1])) && p() && e('1'); // 查找今天的动态
r($actionTest->getDynamicByProjectTest($projectID, $accountList[1], $typeList[2])) && p() && e('0'); // 查找昨天的动态
r($actionTest->getDynamicByProjectTest($projectID, $accountList[1], $typeList[3])) && p() && e('2'); // 查找上周的动态
$action = zenData('action')->loadYaml('action_year');
$action->project->range('101');
+21 -15
View File
@@ -19,19 +19,25 @@ timeout=0
cid=1
- 获取排序为date倒序的所有动态
- 第46条的id属性 @46
- 第46条的objectType属性 @review
- 第78条的id属性 @78
- 第78条的objectType属性 @testcase
- 第0条的id属性 @62
- 第0条的objectType属性 @branch
- 第1条的id属性 @31
- 第1条的objectType属性 @bug
- 获取排序为date正序的所有动态
- 第1条的id属性 @1
- 第1条的objectType属性 @product
- 第0条的id属性 @63
- 第0条的objectType属性 @module
- 第1条的id属性 @32
- 第1条的objectType属性 @testcase
- 获取排序为date倒序的今年之后的动态
- 第46条的id属性 @46
- 第46条的objectType属性 @review
- 第0条的id属性 @62
- 第0条的objectType属性 @branch
- 第1条的id属性 @31
- 第1条的objectType属性 @bug
- 获取排序为date正序的所有动态
- 第2条的id属性 @2
- 第2条的objectType属性 @story
- 第0条的id属性 @61
- 第0条的objectType属性 @todo
- 第1条的id属性 @30
- 第1条的objectType属性 @build
- 获取排序为date倒序的今年之后的动态
- 第0条的id属性 @31
- 第0条的objectType属性 @bug
@@ -53,8 +59,8 @@ $directionList = array('next', 'pre');
$action = new actionTest();
r(array_values($action->getDynamicBySearchTest($queryID[0], $orderByList[0], $limit, $dateList[0], $directionList[0]))) && p('0:id,objectType;1:id,objectType') && e('64,testsuite;32,testcase'); // 获取排序为date倒序的所有动态
r(array_values($action->getDynamicBySearchTest($queryID[0], $orderByList[1], $limit, $dateList[0], $directionList[0]))) && p('0:id,objectType;1:id,objectType') && e('65,caselib;33,case'); // 获取排序为date正序的所有动态
r(array_values($action->getDynamicBySearchTest($queryID[0], $orderByList[0], $limit, $dateList[0], $directionList[1]))) && p('0:id,objectType;1:id,objectType') && e('64,testsuite;32,testcase'); // 获取排序为date倒序的今年之后的动态
r(array_values($action->getDynamicBySearchTest($queryID[0], $orderByList[0], $limit, $dateList[1], $directionList[0]))) && p('0:id,objectType;1:id,objectType') && e('63,module;31,bug'); // 获取排序为date倒序的今天之前的动态
r(array_values($action->getDynamicBySearchTest($queryID[1], $orderByList[0], $limit, $dateList[0], $directionList[1]))) && p('0:id,objectType') && e('31,bug'); // 获取排序为date倒序的今年之后的动态
r(array_values($action->getDynamicBySearchTest($queryID[0], $orderByList[0], $limit, $dateList[0], $directionList[0]))) && p('0:id,objectType;1:id,objectType') && e('62,branch;31,bug'); // 获取排序为date倒序的所有动态
r(array_values($action->getDynamicBySearchTest($queryID[0], $orderByList[1], $limit, $dateList[0], $directionList[0]))) && p('0:id,objectType;1:id,objectType') && e('63,module;32,testcase'); // 获取排序为date正序的所有动态
r(array_values($action->getDynamicBySearchTest($queryID[0], $orderByList[0], $limit, $dateList[0], $directionList[1]))) && p('0:id,objectType;1:id,objectType') && e('62,branch;31,bug'); // 获取排序为date倒序的今年之后的动态
r(array_values($action->getDynamicBySearchTest($queryID[0], $orderByList[0], $limit, $dateList[1], $directionList[0]))) && p('0:id,objectType;1:id,objectType') && e('61,todo;30,build'); // 获取排序为date倒序的今天之前的动态
r(array_values($action->getDynamicBySearchTest($queryID[1], $orderByList[0], $limit, $dateList[0], $directionList[1]))) && p('0:id,objectType') && e('31,bug'); // 获取排序为date倒序的今年之后的动态
+8 -8
View File
@@ -107,10 +107,10 @@ cid=1
- 测试获取对象类型 testtask 对象ID 1 的动态信息 @link
- 测试获取对象类型 testtask 对象ID 2 的动态信息 @link
- 测试获取对象类型 testtask 对象ID 3 的动态信息 @link
- 测试获取对象类型 task 对象ID 12 的动态信息 @nochanged
- 测试 开源版 获取对象类型 risk 对象ID 1 的动态信息 @nochanged
- 测试 开源版 获取对象类型 isue 对象ID 1 的动态信息 @nochanged
- 测试 开源版 获取对象类型 opportunity 对象ID 1 的动态信息 @nochanged
- 测试获取对象类型 task 对象ID 12 的动态信息 @link
- 测试 开源版 获取对象类型 risk 对象ID 1 的动态信息 @link
- 测试 开源版 获取对象类型 isue 对象ID 1 的动态信息 @link
- 测试 开源版 获取对象类型 opportunity 对象ID 1 的动态信息 @link
- 测试获取对象类型 execution 对象ID 1 的动态信息 @link
- 测试获取对象类型 execution 对象ID 2 的动态信息 @link
- 测试获取对象类型 execution 对象ID 3 的动态信息 @link
@@ -325,16 +325,16 @@ r($action->getListTest($objectType[9], $testtaskID[2])) && p() && e('link'); //
// 开源版 导入资产库相关
// 操作 importfromstorylib
r($action->getListTest($objectType[1], $taskID[11])) && p() && e('nochanged'); // 测试获取对象类型 task 对象ID 12 的动态信息
r($action->getListTest($objectType[1], $taskID[11])) && p() && e('link'); // 测试获取对象类型 task 对象ID 12 的动态信息
// 操作 importfromrisklib
r($action->getListTest($objectType[10], $hasOneID[0])) && p() && e('nochanged'); // 测试 开源版 获取对象类型 risk 对象ID 1 的动态信息
r($action->getListTest($objectType[10], $hasOneID[0])) && p() && e('link'); // 测试 开源版 获取对象类型 risk 对象ID 1 的动态信息
// 操作 importfromissuelib
r($action->getListTest($objectType[11], $hasOneID[0])) && p() && e('nochanged'); // 测试 开源版 获取对象类型 isue 对象ID 1 的动态信息
r($action->getListTest($objectType[11], $hasOneID[0])) && p() && e('link'); // 测试 开源版 获取对象类型 isue 对象ID 1 的动态信息
// 操作 importfromopportunitylib
r($action->getListTest($objectType[12], $hasOneID[0])) && p() && e('nochanged'); // 测试 开源版 获取对象类型 opportunity 对象ID 1 的动态信息
r($action->getListTest($objectType[12], $hasOneID[0])) && p() && e('link'); // 测试 开源版 获取对象类型 opportunity 对象ID 1 的动态信息
// 操作 opened 对象类型 execution
r($action->getListTest($objectType[13], $executionID[0])) && p() && e('link'); // 测试获取对象类型 execution 对象ID 1 的动态信息
+1 -1
View File
@@ -5,7 +5,7 @@ $config->admin->log->saveDays = 30;
if(!isset($config->safe)) $config->safe = new stdclass();
if(!isset($config->safe->weak)) $config->safe->weak = '123456,password,12345,12345678,qwerty,123456789,1234,1234567,abc123,111111,123123';
$config->admin->menuGroup['system'] = array('custom|mode', 'backup', 'cron', 'action|trash', 'admin|xuanxuan', 'setting|xuanxuan', 'admin|license', 'admin|checkweak', 'admin|resetpwdsetting', 'admin|safe', 'cache|setting', 'custom|timezone', 'search|buildindex', 'admin|tableengine', 'ldap', 'custom|libreoffice', 'conference', 'watermark', 'client', 'system|browsebackup', 'system|restorebackup');
$config->admin->menuGroup['system'] = array('custom|mode', 'backup', 'cron', 'action|trash', 'admin|xuanxuan', 'setting|xuanxuan', 'admin|license', 'admin|checkweak', 'admin|resetpwdsetting', 'admin|safe', 'cache|setting', 'custom|timezone', 'search|buildindex', 'admin|tableengine', 'admin|metriclib', 'ldap', 'custom|libreoffice', 'conference', 'watermark', 'client', 'system|browsebackup', 'system|restorebackup');
$config->admin->menuGroup['company'] = array('dept', 'company', 'user', 'group', 'tutorial');
$config->admin->menuGroup['switch'] = array('admin|setmodule');
$config->admin->menuGroup['feature'] = array('auditcl', 'stage', 'baseline', 'deliverable', 'design', 'cmcl', 'reviewcl', 'custom|flow', 'custom|code', 'custom|percent','custom|estimate', 'custom|hours', 'subject', 'process', 'activity', 'zoutput', 'classify', 'holiday', 'reviewsetting', 'custom|project', 'custom|set', 'custom|product', 'custom|execution', 'custom|required', 'custom|kanban', 'measurement', 'meetingroom', 'custom|browsestoryconcept', 'custom|kanban', 'sqlbuilder', 'report', 'custom|limittaskdate', 'measurement');
+12
View File
@@ -0,0 +1,12 @@
<?php
$config->admin->metricLib = new stdClass();
$config->admin->metricLib->updateSQLs[1] = 'CREATE INDEX IF NOT EXISTS `metricCode_system_date` ON ' . TABLE_METRICLIB . ' (`metricCode`, `system`, `date`)';
$config->admin->metricLib->updateSQLs[2] = 'CREATE INDEX IF NOT EXISTS `metricCode_program_date` ON ' . TABLE_METRICLIB . ' (`metricCode`, `program`, `date`)';
$config->admin->metricLib->updateSQLs[3] = 'CREATE INDEX IF NOT EXISTS `metricCode_project_date` ON ' . TABLE_METRICLIB . ' (`metricCode`, `project`, `date`)';
$config->admin->metricLib->updateSQLs[4] = 'CREATE INDEX IF NOT EXISTS `metricCode_product_date` ON ' . TABLE_METRICLIB . ' (`metricCode`, `product`, `date`)';
$config->admin->metricLib->updateSQLs[5] = 'CREATE INDEX IF NOT EXISTS `metricCode_execution_date` ON ' . TABLE_METRICLIB . ' (`metricCode`, `execution`, `date`)';
$config->admin->metricLib->updateSQLs[6] = 'CREATE INDEX IF NOT EXISTS `metricCode_user_date` ON ' . TABLE_METRICLIB . ' (`metricCode`, `user`(30), `date`)';
$config->admin->metricLib->updateSQLs[7] = 'ALTER TABLE ' . TABLE_METRICLIB . ' DROP INDEX IF EXISTS `metricID`';
$config->admin->metricLib->updateSQLs[8] = 'ALTER TABLE ' . TABLE_METRICLIB . ' DROP INDEX IF EXISTS `metricCode`';
$config->admin->metricLib->updateSQLs[9] = 'ALTER TABLE ' . TABLE_METRICLIB . ' DROP INDEX IF EXISTS `date`';
$config->admin->metricLib->updateSQLs[10] = 'ALTER TABLE ' . TABLE_METRICLIB . ' DROP INDEX IF EXISTS `deleted`';
+33
View File
@@ -354,6 +354,39 @@ class admin extends control
$this->display();
}
/**
* 更新度量库表的索引。
* Upgrade metriclib table index.
*
* @param int $key
* @access public
* @return void
*/
public function metriclib(int $key = 0)
{
$sql = $this->config->admin->metricLib->updateSQLs[$key] ?? '';
if($sql)
{
set_time_limit(0);
session_write_close();
try
{
$this->dbh->exec($sql);
if(isset($this->config->admin->metricLib->updateSQLs[++$key])) return $this->send(['result' => 'success', 'key' => $key]);
return $this->send(['result' => 'success']);
}
catch(PDOException $e)
{
return $this->sendError($e->getMessage());
}
}
$this->view->title = $this->lang->metriclib->common;
$this->display();
}
/**
* 更换表引擎为InnoDB。
* Ajax change table engine.
+28
View File
@@ -0,0 +1,28 @@
/**
* Update metriclib table structure.
*/
function updateMetriclib(key)
{
$('#updateSQLs > #sql' + key).removeClass('hidden');
$('#startUpdate').remove();
$.getJSON($.createLink('admin', 'metriclib', 'key=' + key), function(resp)
{
if(resp.result == 'success')
{
$('#sql' + key).addClass('text-success').find('.icon').toggleClass('animate-spin icon-spinner-indicator icon-check');
if(resp.key)
{
updateMetriclib(resp.key);
}
else
{
$('#updated').removeClass('hidden');
}
}
else
{
zui.Modal.alert(resp.message).then(() => {loadCurrentPage()});
}
});
}
+19 -4
View File
@@ -26,10 +26,10 @@ $lang->admin->setModuleIndex = 'Set Module';
$lang->admin->mon = 'month';
$lang->admin->day = 'day';
$lang->admin->updateDynamics = 'updateDynamics';
$lang->admin->updatePatch = 'updatePatch';
$lang->admin->upgradeRecommend = 'upgradeRecommend';
$lang->admin->zentaoUsed = '';
$lang->admin->updateDynamics = 'Update Dynamics';
$lang->admin->updatePatch = 'Patch Update';
$lang->admin->upgradeRecommend = 'Upgrade Recommend';
$lang->admin->zentaoUsed = 'You have been using ZenTao';
$lang->admin->api = 'API';
$lang->admin->log = 'Log';
@@ -301,4 +301,19 @@ $lang->admin->community->uxPlan->cancel = 'Canceled';
$lang->admin->community->unBind = new stdclass();
$lang->admin->community->unBind->success = 'Disconnect';
$lang->admin->nickname = 'Nickname';
$lang->admin->position = 'Position';
$lang->admin->company = 'Company Name';
$lang->admin->solvedProblems = 'Project Management Problems';
$lang->admin->mobile = 'Mobile Phone';
$lang->admin->code = 'SMS Verification Code';
$lang->admin->agreeUX = 'User Experience Program';
$lang->admin->metricLib = new stdclass();
$lang->admin->metricLib->startUpdate = 'Start Update';
$lang->admin->metricLib->updating = 'Updating';
$lang->admin->metricLib->updated = 'Update Completed';
$lang->admin->metricLib->tips = "Due to the large amount of data in the metric library table, updating indexes may take a long time. Please operate during low business hours. During this time, you can perform other operations, but please do not close the current page or browser.";
include dirname(__FILE__) . '/menu.php';
+1
View File
@@ -44,6 +44,7 @@ $lang->admin->menuList->system['subMenu']['cron'] = array('link' => "{$la
$lang->admin->menuList->system['subMenu']['timezone'] = array('link' => "{$lang->timezone}|custom|timezone|");
$lang->admin->menuList->system['subMenu']['buildindex'] = array('link' => "{$lang->admin->buildIndex}|search|buildindex|");
$lang->admin->menuList->system['subMenu']['tableengine'] = array('link' => "{$lang->admin->tableEngine}|admin|tableengine|");
if($config->edition != 'open') $lang->admin->menuList->system['subMenu']['metriclib'] = array('link' => "{$lang->metriclib->common}|admin|metriclib|");
if(in_array($this->config->db->driver, $this->config->mysqlDriverList)) $lang->admin->menuList->system['subMenu']['backup'] = array('link' => "{$lang->backup->common}|backup|index|");
$lang->admin->menuList->system['menuOrder']['5'] = 'mode';
+6
View File
@@ -308,4 +308,10 @@ $lang->admin->mobile = '手机号';
$lang->admin->code = '短信验证码';
$lang->admin->agreeUX = '用户体验计划';
$lang->admin->metricLib = new stdclass();
$lang->admin->metricLib->startUpdate = '开始更新';
$lang->admin->metricLib->updating = '正在更新';
$lang->admin->metricLib->updated = '更新完成';
$lang->admin->metricLib->tips = "由于度量库表数据量较大,更新索引可能需要较长时间,请在业务低峰期操作。在此期间您可以进行其他操作,但请勿关闭当前页面或浏览器。";
include dirname(__FILE__) . '/menu.php';
+55
View File
@@ -0,0 +1,55 @@
<?php
declare(strict_types=1);
/**
* The metric library view file of admin module of ZenTaoPMS.
* @copyright Copyright 2009-2023 禅道软件(青岛)有限公司(ZenTao Software (Qingdao) Co., Ltd. www.zentao.net)
* @license ZPL(https://zpl.pub/page/zplv12.html) or AGPL(https://www.gnu.org/licenses/agpl-3.0.en.html)
* @author Gang Liu <liugang@easycorp.ltd>
* @package admin
* @link https://www.zentao.net
*/
namespace zin;
$sqls = [];
foreach($config->admin->metricLib->updateSQLs as $key => $sql)
{
$sqls[] = div
(
setID('sql' . $key),
setClass('hidden'),
icon(setClass('icon icon-spinner-indicator mr-2 animate-spin')),
$sql
);
}
panel
(
setClass('m-auto w-2/3'),
set::title($lang->metriclib->common),
set::headingClass('justify-start border-b'),
div
(
setClass('mb-4'),
$lang->admin->metricLib->tips
),
div
(
setID('updateSQLs'),
setClass('mb-4'),
$sqls,
div
(
setID('updated'),
setClass('text-success hidden'),
icon(setClass('icon icon-check mr-2')),
$lang->admin->metricLib->updated
)
),
a
(
setID('startUpdate'),
setClass('btn primary'),
on::click('updateMetriclib(1)'),
$lang->admin->metricLib->startUpdate
)
);
+2
View File
@@ -138,6 +138,8 @@ if($config->inQuickon)
if($this->config->inQuickon)
{
$data = array();
$config->system->dtable->backup->fieldList['name']['link'] = array('module' => 'system', 'method' => 'backupView', 'params' => 'name={id}');
$config->system->dtable->backup->fieldList['name']['data-toggle'] = 'modal';
if(!empty($backups)) $data = initTableData($backups, $config->system->dtable->backup->fieldList, $this->system);
foreach($data as &$backup)
+1
View File
@@ -1,6 +1,7 @@
$(function()
{
if($('#expiredModal').length) zui.Modal.open({id: 'expiredModal'});
if($('#metriclibModal').length) zui.Modal.open({id: 'metriclibModal'});
if($('#annualModal').length) zui.Modal.open({id: 'annualModal'});
if($('#upgradeModal').length) zui.Modal.open({id: 'upgradeModal'});
});
+7
View File
@@ -50,6 +50,13 @@ $remind ? modal
html($remind)
) : null;
$metriclibRemind = $this->misc->getMetriclibRemind();
$metriclibRemind ? modal
(
setID('metriclibModal'),
html($metriclibRemind)
) : null;
$upgradeRemind = $this->misc->getUpgradeRemind();
if($upgradeRemind)
{
+1 -1
View File
@@ -41,7 +41,7 @@ class bugModel extends model
if(!empty($bug->assignedTo)) $this->action->create('bug', $bugID, 'Assigned', '', $bug->assignedTo);
/* Add score for create. */
$files = $this->loadModel('file')->saveUpload('bug', $bugID);
$this->loadModel('file')->saveUpload('bug', $bugID);
if(!empty($bug->case))
{
$this->loadModel('score')->create('bug', 'createFormCase', $bug->case);
+3 -1
View File
@@ -39,6 +39,7 @@ detailHeader
),
);
$editorUid = uniqid();
detailBody
(
on::change('[name="product"]', 'changeProduct'),
@@ -89,6 +90,7 @@ detailBody
editor
(
set::name('steps'),
set::uid($editorUid),
html($bug->steps)
)
),
@@ -100,7 +102,7 @@ detailBody
section
(
set::title($lang->bug->legendComment),
editor(set::name('comment'))
editor(set::uid($editorUid), set::name('comment'))
)
),
history(),
+3 -2
View File
@@ -1163,7 +1163,7 @@ class bugZen extends bug
if($product->type != 'normal') $branchTagOption = $this->getBranchOptions($product->id);
if($this->config->edition == 'max') $this->view->injectionList = $this->view->identifyList = $this->loadModel('review')->getPairs($bug->project, $bug->product, true);
$this->assignVarsForEdit($bug);
$this->assignVarsForEdit($bug, $product);
$this->view->title = $this->lang->bug->edit . "BUG #$bug->id $bug->title - " . $this->products[$bug->product];
$this->view->bug = $bug;
@@ -1181,10 +1181,11 @@ class bugZen extends bug
* Assign variables for editing bug.
*
* @param object $bug
* @param object $product
* @access protected
* @return void
*/
protected function assignVarsForEdit(object $bug): void
protected function assignVarsForEdit(object $bug, object $product): void
{
/* Add product related to the bug when it is not in the products. */
$product = $this->loadModel('product')->fetchByID($bug->product);
+5 -6
View File
@@ -245,8 +245,8 @@ class caselibZen extends caselib
if(empty($rows))
{
$row->typeValue = join("\n", $this->lang->testcase->typeList);
$row->stageValue = join("\n", $this->lang->testcase->stageList);
$row->typeValue = implode("\n", $this->lang->testcase->typeList);
$row->stageValue = implode("\n", $this->lang->testcase->stageList);
}
$rows[] = $row;
@@ -444,7 +444,7 @@ class caselibZen extends caselib
$case->stage = array();
foreach($stages as $stage) $case->stage[] = array_search($stage, $this->lang->testcase->stageList);
$case->stage = join(',', $case->stage);
$case->stage = implode(',', $case->stage);
}
else
{
@@ -558,7 +558,6 @@ class caselibZen extends caselib
{
$users = $this->loadModel('user')->getPairs('noletter');
$relatedModules = $this->loadModel('tree')->getModulePairs($libID, 'caselib');
$relatedCases = $this->loadModel('testcase')->getRelatedCases($cases);
$relatedSteps = $this->testcase->getRelatedSteps(array_keys($cases));
$relatedFiles = $this->testcase->getRelatedFiles(array_keys($cases));
@@ -655,7 +654,7 @@ class caselibZen extends caselib
{
$case->stage = explode(',', $case->stage);
foreach($case->stage as $key => $stage) $case->stage[$key] = isset($this->lang->testcase->stageList[$stage]) ? $this->lang->testcase->stageList[$stage] : $stage;
$case->stage = join("\n", $case->stage);
$case->stage = implode("\n", $case->stage);
}
/**
@@ -697,6 +696,6 @@ class caselibZen extends caselib
$linkCaseID = trim($linkCaseID);
$tmpLinkCases[] = isset($relatedCases[$linkCaseID]) ? $relatedCases[$linkCaseID] . "(#$linkCaseID)" : $linkCaseID;
}
$case->linkCase = join("; \n", $tmpLinkCases);
$case->linkCase = implode("; \n", $tmpLinkCases);
}
}
+22
View File
@@ -1136,4 +1136,26 @@ class cneModel extends model
$apiUrl = "/api/cne/system/update";
return $this->apiPost($apiUrl, $apiParams, $this->config->CNE->api->headers);
}
/**
* 获取备份的详细信息。
* Get backup detail.
*
* @param object $instance
* @param string $backupName
* @access public
* @return object|false
*/
public function backupDetail(object $instance, string $backupName): object|false
{
if(empty($instance->k8name) || empty($instance->spaceData)) return false;
$apiUrl = "/api/cne/app/backup/detail";
$apiParams = array('name' => $instance->k8name, 'namespace' => $instance->spaceData->k8space, 'backup_name' => $backupName);
$result = $this->apiGet($apiUrl, $apiParams, $this->config->CNE->api->headers);
if(empty($result) || $result->code != 200 || empty($result->data)) return false;
return $result->data;
}
}
@@ -382,4 +382,27 @@ class cneTest
{
return $this->objectModel->tryAllocate($resources);
}
/**
* Test backupDetail method.
*
* @param object $instance
* @param int $count
* @access public
* @return object|bool|string
*/
public function backupDetailTest(object $instance, int $count): object|bool|string
{
$name = '';
if($count > 0)
{
$backupList = $this->objectModel->getBackupList($instance);
if(empty($backupList->data)) return false;
if(!empty($backupList->data[$count - 1]->name)) $name = $backupList->data[$count - 1]->name;
}
$result = $this->objectModel->backupDetail($instance, $name);
if(!$result) return $result;
if(!empty($result->message)) return $result->message;
return $result->backup_details;
}
}
+37
View File
@@ -0,0 +1,37 @@
#!/usr/bin/env php
<?php
/**
title=测试 cneModel->backupDetail();
timeout=0
cid=1
- 空的数据 @0
- 错误的空间 @0
- 备份的数据库
- 第0条的db_type属性 @mysql
- 第0条的status属性 @completed
- 备份的数据
- 第0条的volume属性 @data
- 第0条的status属性 @completed
*/
include dirname(__FILE__, 5) . '/test/lib/init.php';
include dirname(__FILE__, 2) . '/lib/cne.unittest.class.php';
$cneModel = new cneTest();
$instance = new stdClass();
r($cneModel->backupDetailTest($instance, 0)) && p() && e('0'); // 空的数据
$instance->spaceData = new stdClass();
$instance->spaceData->k8space = '';
r($cneModel->backupDetailTest($instance, 0)) && p() && e('0'); // 错误的空间
$instance->spaceData->k8space = 'quickon-system';
$instance->k8name = 'zentaopaas';
r($cneModel->backupDetailTest($instance, 1)->db) && p('0:db_type,status') && e('mysql,completed'); // 备份的数据库
r($cneModel->backupDetailTest($instance, 2)->volume) && p('0:volume,status') && e('data,completed'); // 备份的数据
+1 -1
View File
@@ -25,4 +25,4 @@ $certName = 'fail-name';
r($cneModel->certInfoTest($certName)) && p() && e('0'); // 证书名称错误
$certName = 'tls-haogs-cn';
r($cneModel->certInfoTest($certName)) && p('sans:0') && e('*.devops.corp.cc'); // 证书名称正确
r($cneModel->certInfoTest($certName)) && p('sans:0') && e('devops.corp.cc'); // 证书名称正确
@@ -12,10 +12,10 @@ timeout=0
cid=1
- 执行 @1
- 执行common模块的getPreAndNextObject方法,参数是'', 3
- 执行common模块的getPreAndNextObject方法,参数是'', 3
- 属性pre @~~
- 属性next @~~
- 执行common模块的getPreAndNextObject方法,参数是'story', 3
- 执行common模块的getPreAndNextObject方法,参数是'story', 3
- 属性pre @~~
- 属性next @~~
- 执行$preAndNextObject->pre属性id @2
+1 -1
View File
@@ -596,7 +596,7 @@ class custom extends control
$menus = json_decode($items);
$menuNames = array();
foreach($menus as $key => $item) $menuNames[] = $item->name;
foreach($menus as $item) $menuNames[] = $item->name;
foreach($oldMenus as $key => $item)
{
+6
View File
@@ -211,6 +211,12 @@ class datatable extends control
if(!$execution->hasProduct) unset($cols['branch']);
}
if($module == 'project' && $method == 'execution')
{
$project = $this->datatable->fetchByID($this->session->project, 'project');
if(!empty($project->isTpl)) unset($cols['deliverable']);
}
if($extra == 'unsetStory' && isset($cols['story'])) unset($cols['story']);
if($this->config->edition == 'ipd' && $module == 'product' && $method == 'browse' && $extra == 'story') unset($cols['roadmap']);
+11 -5
View File
@@ -367,6 +367,7 @@ class doc extends control
public function createLib(string $type = '', int $objectID = 0, int $libID = 0)
{
$this->app->loadLang('api');
$this->doc->setMenuByType($type, (int)$objectID, (int)$libID);
if(!empty($_POST))
{
@@ -386,10 +387,10 @@ class doc extends control
}
$objects = array();
if($type == 'project' && $this->app->tab == 'doc')
if($type == 'project')
{
$this->view->executionPairs = $this->execution->getPairs($objectID, 'all', 'multiple,leaf,noprefix');
$this->view->project = $this->project->getById($objectID);
$this->view->project = $this->project->getById($objectID);
if($this->app->tab == 'doc') $this->view->executionPairs = $this->execution->getPairs($objectID, 'all', 'multiple,leaf,noprefix');
}
if($type == 'execution')
@@ -397,6 +398,7 @@ class doc extends control
$objects = $this->execution->getPairs(0, 'all', 'multiple,leaf,noprefix,withobject');
$execution = $this->execution->getByID($objectID);
if($execution->type == 'stage') $this->lang->doc->execution = str_replace($this->lang->executionCommon, $this->lang->project->stage, $this->lang->doc->execution);
$this->view->execution = $this->doc->fetchByID($objectID, 'execution');
}
if($type == 'custom' || $type == 'mine' || $type == 'doctemplate')
@@ -456,6 +458,11 @@ class doc extends control
{
if(!common::hasPriv('doc', 'editSpace') && !common::hasPriv('doc', 'editLib')) return;
$lib = $this->doc->getLibByID($libID);
if($lib->type != 'project') $this->docZen->setAclForEditLib($lib);
$this->doc->setMenuByType($lib->type, (int)zget($lib, $lib->type, 0), (int)$libID);
if($lib->type == 'project') $this->docZen->setAclForEditLib($lib);
if(!empty($_POST))
{
$this->lang->doc->name = $this->lang->nameAB;
@@ -499,8 +506,6 @@ class doc extends control
$this->view->object = $execution;
}
$this->docZen->setAclForEditLib($lib);
$this->view->lib = $lib;
$this->view->groups = $this->loadModel('group')->getPairs();
$this->view->users = $this->user->getPairs('noletter|noclosed', $lib->users);
@@ -2208,6 +2213,7 @@ class doc extends control
$this->doc->setMenuByType($type, $spaceID, $libID);
$objectKey = $type . 'ID';
$this->view->$objectKey = $spaceID;
$this->view->$type = $this->doc->fetchByID($spaceID, $type);
}
if($type == 'mine') $menuType = 'my';
+29 -27
View File
@@ -188,21 +188,22 @@ function submitNewDoc(doc, spaceID, libID, moduleID, formData, afterCreate)
const url = $.createLink('doc', 'createTemplate', `libID=${libID}&moduleID=${moduleID}`);
const docData =
{
rawContent : doc.content,
content : doc.html,
status : doc.status || 'normal',
contentType : doc.contentType,
type : doc.type || 'text',
lib : libID,
module : moduleID,
title : doc.title,
keywords : doc.keywords,
acl : 'private',
space : spaceType,
project : 0,
templateType: module && module.data ? module.data.short : '',
uid : (doc.uid || `doc${doc.id}`),
parent : doc.parent
rawContent : doc.content,
content : doc.html,
status : doc.status || 'normal',
contentType : doc.contentType,
type : doc.type || 'text',
lib : libID,
module : moduleID,
title : doc.title,
keywords : doc.keywords,
templateDesc : doc.templateDesc,
acl : 'private',
space : spaceType,
project : 0,
templateType : module && module.data ? module.data.short : '',
uid : (doc.uid || `doc${doc.id}`),
parent : doc.parent
};
if(formData) mergeDocFormData(docData, formData);
docApp.props.fetcher = $.createLink('doc', 'ajaxGetSpaceData', `type=template&spaceID=${spaceID}&picks={picks}&libID=${libID}`);
@@ -327,18 +328,19 @@ function handleSaveDoc(doc)
const moduleID = docApp.signals.moduleID.value;
const url = $.createLink('doc', 'editTemplate', `docID=${doc.id}`);
const docData = {
rawContent : doc.content,
status : doc.status || 'normal',
contentType: doc.contentType,
type : 'text',
lib : libID,
module : moduleID,
title : doc.title,
keywords : doc.keywords,
acl : doc.acl,
content : doc.html,
space : spaceType,
uid : (doc.uid || `doc${doc.id}`),
rawContent : doc.content,
status : doc.status || 'normal',
contentType : doc.contentType,
type : 'text',
lib : libID,
module : moduleID,
title : doc.title,
keywords : doc.keywords,
templateDesc : doc.templateDesc,
acl : doc.acl,
content : doc.html,
space : spaceType,
uid : (doc.uid || `doc${doc.id}`),
};
const docAppData = docApp.doc.data;
+26 -11
View File
@@ -15,17 +15,7 @@ window.setDocAppOptions = function(_, options)
onSwitchView: function(mode, location, info)
{
onSwitchView.call(this, mode, location, info);
if(location.libID !== this.props.libID)
{
const lib = this.lib;
const quickType = lib ? lib.data.quickType : 'view';
const url = $.createLink('doc', 'quick', `type=${quickType}`);
this.signals.loading.value = true;
loadPartial(url, '#mainContent', {complete: () =>
{
this.signals.loading.value = false;
}});
}
trySwitchView(this, location.libID);
},
formatDataItem: function(type, item)
{
@@ -35,6 +25,31 @@ window.setDocAppOptions = function(_, options)
});
};
/**
* 尝试切换视图。
* Try to switch view.
* @param {Object} docApp - 文档应用实例。
* @param {number} libID - 库ID。
*/
function trySwitchView(docApp, libID)
{
if(window._trySwitchViewTimer) clearTimeout(window._trySwitchViewTimer);
window._trySwitchViewTimer = setTimeout(() => {
window._trySwitchViewTimer = 0;
if(libID === docApp.props.libID) return;
const lib = docApp.lib;
const quickType = lib ? lib.data.quickType : 'view';
const url = $.createLink('doc', 'quick', `type=${quickType}`);
docApp.signals.loading.value = true;
loadPartial(url, '#mainContent', {complete: () =>
{
docApp.signals.loading.value = false;
}});
}, 10);
}
function getObjectBrowseUrl(object, objectType, libID)
{
const browsePath =
+10 -9
View File
@@ -25,26 +25,27 @@ if($type === 'project')
* 定义文档界面上的权限。
* Define the privs of doc app.
*/
$hasCustomSpace = $type == 'mine' || $type == 'custom';
$hasCustomSpace = $type == 'mine' || $type == 'custom';
$projectTemplate = ($type === 'project' && !empty($project->isTpl)) || ($type == 'execution' && !empty($execution->isTpl));
$privs = array();
$privs['create'] = hasPriv('doc', 'create');
$privs['edit'] = hasPriv('doc', 'edit');
$privs['delete'] = hasPriv('doc', 'delete');
$privs['create'] = hasPriv('doc', 'create') && !$projectTemplate;
$privs['edit'] = hasPriv('doc', 'edit') && !$projectTemplate;
$privs['delete'] = hasPriv('doc', 'delete') && !$projectTemplate;
$privs['view'] = hasPriv('doc', 'view');
$privs['effort'] = $this->config->edition != 'open' && hasPriv('effort', 'createForObject');
$privs['exportDoc'] = $this->config->edition != 'open' && hasPriv('doc', $type . '2export');
$privs['exportDoc'] = $this->config->edition != 'open' && hasPriv('doc', $type . '2export') && !$projectTemplate;
$privs['exportApi'] = $this->config->edition != 'open' && hasPriv('api', 'export');
$privs['moveDoc'] = hasPriv('doc', 'moveDoc');
$privs['moveDoc'] = hasPriv('doc', 'moveDoc') && !$projectTemplate;
$privs['collect'] = hasPriv('doc', 'collect');
$privs['createLib'] = hasPriv('doc', 'createLib');
$privs['editLib'] = hasPriv('doc', 'editLib');
$privs['moveLib'] = hasPriv('doc', 'moveLib');
$privs['moveLib'] = hasPriv('doc', 'moveLib') && !$projectTemplate;
$privs['deleteLib'] = hasPriv('doc', 'deleteLib');
$privs['sortDocLib'] = hasPriv('doc', 'sortDocLib');
$privs['uploadFile'] = hasPriv('doc', 'create');
$privs['uploadFile'] = hasPriv('doc', 'create') && !$projectTemplate;
$privs['editFile'] = hasPriv('file', 'edit');
$privs['deleteFile'] = hasPriv('doc', 'deleteFile');
$privs['exportFiles'] = hasPriv('doc', 'exportFiles');
$privs['exportFiles'] = hasPriv('doc', 'exportFiles') && !$projectTemplate;
$privs['createSpace'] = $hasCustomSpace && hasPriv('doc', 'createSpace');
$privs['deleteSpace'] = $hasCustomSpace && hasPriv('doc', 'deleteSpace');
$privs['editSpace'] = $hasCustomSpace && hasPriv('doc', 'editSpace');
+1
View File
@@ -26,6 +26,7 @@ $renderObjectBox = function(string $type) use ($lang, $objects, $objectID)
return formRow(
setClass('objectBox'),
formGroup(
set::id('object'),
set::width('5/6'),
set::label($lang->doc->{$type}),
set::name($type),
+1 -1
View File
@@ -2738,7 +2738,7 @@ class execution extends control
$executionGroups = $this->dao->select('*')->from(TABLE_EXECUTION) /* 按照项目分组,获取有权限访问的执行列表。*/
->where('deleted')->eq('0')
->andWhere('multiple')->eq('1')
->andWhere('type')->in('sprint,stage,kanban')
->andWhere('type')->in(!empty($execution->isTpl) ? 'sprint,stage' : 'sprint,stage,kanban')
->beginIF(!$this->app->user->admin)->andWhere('id')->in($this->app->user->view->sprints)->fi()
->beginIF(!empty($execution->isTpl))->andWhere('isTpl')->eq('1')->fi()
->andWhere('project')->in(array_keys($projects))
+17 -1
View File
@@ -153,7 +153,6 @@ class executionModel extends model
unset($this->lang->execution->menu->burn);
unset($this->lang->execution->menu->kanban);
unset($this->lang->execution->menu->view);
unset($this->lang->execution->menu->story);
unset($this->lang->execution->menu->qa);
unset($this->lang->execution->menu->devops);
@@ -162,6 +161,22 @@ class executionModel extends model
unset($this->lang->execution->menu->effort);
unset($this->lang->execution->menu->more);
if(!empty($this->lang->execution->menu->view['subMenu']->gantt))
{
$this->lang->execution->menu->gantt = $this->lang->execution->menu->view['subMenu']->gantt;
$taskOrder = 0;
foreach($this->lang->execution->menuOrder as $order => $menu) if($menu == 'task') $taskOrder = $order;
$this->lang->execution->menuOrder[$taskOrder + 1] = 'gantt';
if(!empty($this->lang->project->menuOrder))
{
$taskOrder = 0;
foreach($this->lang->project->menuOrder as $order => $menu) if($menu == 'task') $taskOrder = $order;
$this->lang->project->menuOrder[$taskOrder + 1] = 'gantt';
}
}
if(!empty($this->lang->execution->menu->other['dropMenu']->pssp))
{
$this->lang->execution->menu->pssp = $this->lang->execution->menu->other['dropMenu']->pssp;
@@ -181,6 +196,7 @@ class executionModel extends model
}
}
unset($this->lang->execution->menu->view);
unset($this->lang->execution->menu->other);
if(isset($this->lang->execution->menu->settings['subMenu']->products)) unset($this->lang->execution->menu->settings['subMenu']->products); // 模板下隐藏产品
if(isset($this->lang->execution->menu->settings['subMenu']->whitelist)) unset($this->lang->execution->menu->settings['subMenu']->whitelist); // 模板下隐藏白名单
@@ -11,6 +11,7 @@ class createExecutionTester extends tester
*/
public function inputFields($execution)
{
$this->switchVision('lite');
$form = $this->initForm('execution', 'create', '', 'appIframe-project');
$form->wait(1);
if(isset($execution['project'])) $form->dom->project->picker($execution['project']);
@@ -88,10 +89,11 @@ class createExecutionTester extends tester
*/
public function create($execution, $module = 'execution')
{
$this->switchVision('lite');
$this->inputFields($execution);
/* 创建成功会跳转至看板列表全部标签下,从url中获取status字段内容 */
$form = $this->loadPage();
$form->wait(1);
$url = explode('status=', $this->response('url'));
/* 根据url中是否包含status,判断是否创建成功 */
if(!isset($url[1]))
@@ -112,7 +114,6 @@ class createExecutionTester extends tester
*/
public function createWithEmptyName($execution, $field)
{
$this->switchVision('lite');
$this->inputFields($execution);
$form = $this->loadPage();
$text = $form->dom->{$field . 'Tip'}->getText();
@@ -134,7 +135,6 @@ class createExecutionTester extends tester
*/
public function createWithRepeatName($execution, $field)
{
$this->switchVision('lite');
$this->inputFields($execution);
if($this->checkRepeatInfo($field)) return $this->success('创建看板表单页提示信息正确');
return $this->failed('创建看板表单页提示信息不正确');
@@ -151,7 +151,6 @@ class createExecutionTester extends tester
*/
public function createWithDateError($execution, $dateType = 'end')
{
$this->switchVision('lite');
$this->inputFields($execution);
if($this->checkDateInfo($dateType)) return $this->success('创建看板表单页提示信息正确');
return $this->failed('创建看板表单页提示信息不正确');
+154
View File
@@ -0,0 +1,154 @@
<?php
include dirname(__FILE__, 5) . '/test/lib/ui.php';
class editExecutionTester extends tester
{
/**
* 输入表单字段内容。
* Input fields
*
* @param array $execution
* @access public
*/
public function inputFields($execution)
{
$form = $this->initForm('execution', 'kanban', array('kanbanID' => '2'), 'appIframe-project');
$form->wait(1);
$form->dom->kanbanSettingInLite->click();
$form->wait();
$form = $this->loadPage();
$form->wait(1);
$form->dom->btn($this->lang->execution->setKanban)->click();
$form->wait(1);
if(isset($execution['name'])) $form->dom->name->setValue($execution['name']);
if(isset($execution['begin'])) $form->dom->begin->datePicker($execution['begin']);
if(isset($execution['end'])) $form->dom->end->datePicker($execution['end']);
$form->dom->editSubmitInLite->click();
$form->wait(1);
}
/**
* 看板名称已存在时获取提示信息。
* Get error info of repeat name.
*
* @access public
* @return bool
*/
public function checkRepeatInfo()
{
$form = $this->loadPage();
$form->wait(1);
if(!is_object($form->dom->nameTip)) return $this->failed('看板名称重复没有提示信息');
$text = $form->dom->nameTip->getText();
$info = sprintf($this->lang->error->repeat, $this->lang->kanban->name, $form->dom->name->getValue());
if($text == $info) return $this->success('编辑看板表单页提示信息正确');
return $this->failed('编辑看板表单页提示信息不正确');
}
/**
* 计划起止日期错误时获取提示信息。
* Get error info of begin and end date.
*
* @param string $dateType begin|end
* @access public
* @return bool
*/
public function checkDateInfo($dateType = 'end')
{
$form = $this->loadPage();
$form->wait(1);
if($dateType == 'begin')
{
$text = $form->dom->beginTip->getText();
if($this->config->default->lang == 'zh-cn') $info = '看板开始日期应大于等于项目的开始日期:。';
if($this->config->default->lang == 'en') $info = 'The start date of Kanban should be ≥ the start date of Project : .';
}
else
{
$text = $form->dom->endTip->getText();
if($this->config->default->lang == 'zh-cn') $info = '看板截止日期应小于等于项目的截止日期:。';
if($this->config->default->lang == 'en') $info = 'The deadline of Kanban should be ≤ the deadline of Project : .';
}
/* 获取页面返回信息中除日期外的内容 */
preg_match_all('/(\d{4}-\d{2}-\d{2})/', $text, $matches);
$date = $matches[0];
$params = str_replace($date, '', $text);
$params = trim($params);
return $params == $info;
}
/**
* 编辑看板。
* Edit kanban.
*
* @param array $execution
* @access public
* @return object
*/
public function edit($execution)
{
$this->switchVision('lite');
$this->inputFields($execution);
if($this->checkFormTips('execution')) return $this->success('编辑看板表单页提示信息正确');
/* 访问view方法,方便获取信息 */
$form = $this->initForm('execution', 'view', array('kanbanID' => '2' ), 'appIframe-project');
if(isset($execution['name']) && $form->dom->executionName->getText() != $execution['name']) return $this->failed('编辑看板名称失败');
if(isset($execution['begin']) && $form->dom->plannedBeginInLite->getText() != $execution['begin']) return $this->failed('编辑预计开始日期失败');
if(isset($execution['end']) && $form->dom->plannedEndInLite->getText() != $execution['end']) return $this->failed('编辑预计结束日期失败');
return $this->success('编辑看板成功');
}
/**
* 看板名称为空编辑执行。
* Edit kanban with no name.
*
* @param array $execution
* @access public
* @return object
*/
public function editWithEmptyName($execution)
{
$this->switchVision('lite');
$this->inputFields($execution);
$form = $this->loadPage();
$form->wait(1);
$text = $form->dom->nameTip->getText();
$info = sprintf($this->lang->error->notempty, $this->lang->kanban->name);
if($text == $info) return $this->success('编辑看板表单页提示信息正确');
return $this->failed('编辑看板表单页提示信息不正确');
}
/**
* 看板名称已存在编辑执行。
* Edit kanban with repeat name.
*
* @param array $execution
* @access public
* @return object
*/
public function editWithRepeatName($execution)
{
$this->switchVision('lite');
$this->inputFields($execution);
return $this->checkRepeatInfo();
}
/**
* 看板起止日期错误编辑看板。
* Edit kanban with date error.
*
* @param array $execution
* @param string $dateType begin|end
* @access public
* @return object
*/
public function editWithDateError($execution, $dateType = 'end')
{
$this->switchVision('lite');
$this->inputFields($execution);
if($this->checkDateInfo($dateType)) return $this->success('编辑看板表单页提示信息正确');
return $this->failed('编辑看板表单页提示信息不正确');
}
}
@@ -17,6 +17,7 @@ class kanbanTester extends tester
{
$this->switchVision('lite');
$form = $this->initForm('execution', 'kanban', array('kanbanID' => '2'), 'appIframe-project');
$form->wait(1);
/* 选择泳道分组 */
if(!empty($groupId))
@@ -0,0 +1,118 @@
<?php
include dirname(__FILE__, 5) . '/test/lib/ui.php';
class putoffExecutionTester extends tester
{
/**
* 输入延期执行表单字段。
* Input fields.
*
* @param array $execution
* @param string $executionId
* @access public
* @return bool
*/
public function inputFields($execution, $executionId)
{
$this->switchVision('lite');
$form = $this->initForm('execution', 'kanban', array('kanbanID' => $executionId ), 'appIframe-project');
$form->wait(1);
$form->dom->kanbanSettingInLite->click();
$form->wait(1);
$form->dom->btn($this->lang->execution->putoff)->click();
if(isset($execution['begin'])) $form->dom->begin->datePicker($execution['begin']);
if(isset($execution['end'])) $form->dom->end->datePicker($execution['end']);
if(isset($execution['days'])) $form->dom->days->setValue($execution['days']);
$form->dom->putoffSubmitInLite->click();
$form->wait(1);
}
/**
* 延期执行。
* Putoff execution.
*
* @param array $execution
* @param string $executionId
* @access public
* @return bool
*/
public function putoff($execution, $executionId)
{
$this->inputFields($execution, $executionId);
$form = $this->loadPage();
$form->wait(1);
if(is_object($form->dom->putoffSubmitInLite)) return $this->failed('延期失败');
$form = $this->initForm('execution', 'view', array('kanbanID' => $executionId ), 'appIframe-project');
if(isset($execution['begin']) && $form->dom->plannedBeginInLite->getText() != $execution['begin']) return $this->failed('计划开始时间错误');
if(isset($execution['end']) && $form->dom->plannedEndInLite->getText() != $execution['end']) return $this->failed('计划完成时间错误');
return $this->success('延期执行成功');
}
/**
* 延期执行的计划起止日期不正确。
* Putoff execution with wrong date.
*
* @param array $execution
* @param string $executionId
* @param string $dateType begin|end
* @access public
* @return bool
*/
public function putoffWithWrongDate($execution, $executionId, $dateType = 'end')
{
$this->inputFields($execution, $executionId);
$form = $this->loadPage();
$form->wait(1);
if(!is_object($form->dom->putoffSubmitInLite)) return $this->failed('错误的延期成功');
if($dateType == 'end' && $execution['end'] != '')
{
$text = $form->dom->endTip->getText();
if($this->config->default->lang == 'zh-cn') $info = '看板截止日期应小于等于项目的截止日期:。';
if($this->config->default->lang == 'en') $info = 'The deadline of Kanban should be ≤ the deadline of Project : .';
}
if($dateType == 'begin')
{
$text = $form->dom->beginTip->getText();
if($this->config->default->lang == 'zh-cn') $info = '看板开始日期应大于等于项目的开始日期:。';
if($this->config->default->lang == 'en') $info = 'The start date of Kanban should be ≥ the start date of Project : .';
}
/* 计划结束日期为空时,错误提示中没有日期 */
if($dateType == 'end' && $execution['end'] == '')
{
$info = sprintf($this->lang->error->notempty, $this->lang->execution->end);
$params = $form->dom->endTip->getText();
}
else
{
/* 获取页面返回信息中除日期外的内容 */
preg_match_all('/(\d{4}-\d{2}-\d{2})/', $text, $matches); ~
$date = $matches[0][0]; ~
$params = str_replace($date, '', $text); ~
$params = trim($params);
}
if($params == $info) return $this->success('延期执行表单页提示信息正确');
return $this->failed('延期执行表单页提示信息不正确');
}
/**
* 延期执行的可用工日不正确。
* Putoff execution with wrong days.
*
* @param array $execution
* @param string $executionId
* @access public
* @return bool
*/
public function putoffWithWrongDays($execution, $executionId)
{
$this->inputFields($execution, $executionId);
$form = $this->loadPage();
$form->wait(1);
if(!is_object($form->dom->putoffSubmitInLite)) return $this->failed('错误的延期成功');
if($form->dom->daysTip->getText() == $this->lang->project->copyProject->daysTips) return $this->success('延期执行表单页提示信息正确');
return $this->failed('延期执行表单页提示信息不正确');
}
}
@@ -0,0 +1,67 @@
<?php
include dirname(__FILE__, 5) . '/test/lib/ui.php';
class startExecutionTester extends tester
{
/**
* 开始看板弹窗中输入实际开始日期。
* Input fields.
*
* @param string $realBegan
* @param string $kanbanId
* @access public
*/
public function inputFields($realBegan, $kanbanId)
{
$form = $this->initForm('execution', 'kanban', array('kanbanID' => $kanbanId ), 'appIframe-project');
$form->wait(1);
$form->dom->kanbanSettingInLite->click();
$form->wait(1);
$form->dom->btn($this->lang->execution->start)->click();
$form->wait(1);
if(isset($realBegan)) $form->dom->realBegan->datePicker($realBegan);
$form->wait(1);
$form->dom->startSubmitInLite->click();
$form->wait(1);
}
/**
* 正常开始看板。
* Start kanban.
*
* @param string $realBegan
* @param string $kanbanId
* @access public
* @return bool
*/
public function start($realBegan, $kanbanId)
{
$this->switchVision('lite');
$this->inputFields($realBegan, $kanbanId);
$form = $this->initForm('execution', 'view', array('kanbanID' => $kanbanId ), 'appIframe-project');
if($form->dom->status->getText() != $this->lang->execution->statusList->doing) return $this->failed('看板状态错误');
if($form->dom->realBeganView->getText() != $realBegan) return $this->failed('看板实际开始日期错误');
return $this->success('开始看板成功');
}
/**
* 实际开始日期大于当前日期。
* The real start date is greater than the current date.
*
* @param string $realBegan
* @param string $kanbanId
* @access public
* @return bool
*/
public function startWithGreaterDate($realBegan, $kanbanId)
{
$this->switchVision('lite');
$this->inputFields($realBegan, $kanbanId);
$form = $this->loadPage();
$field = $form->dom->realBegan->getValue();
$text = $form->dom->realBeganTip->getText();
$info = sprintf($this->lang->error->le, $field, $realBegan);
if($text != $info) return $this->success('开始看板表单页提示信息正确');
return $this->failed('开始看板表单页提示信息不正确');
}
}
@@ -0,0 +1,29 @@
<?php
include dirname(__FILE__, 5) . '/test/lib/ui.php';
class suspendExecutionTester extends tester
{
/**
* 运营界面挂起看板。
* Suspend execution.
*
* @param string $executionId
* @access public
* @return void
*/
public function suspend($executionId)
{
$this->switchVision('lite');
$form = $this->initForm('execution', 'kanban', array('execution' => $executionId ), 'appIframe-project');
$form->wait(1);
$form->dom->kanbanSettingInLite->click();
$form->wait(1);
$form->dom->btn($this->lang->execution->suspend)->click();
$form->dom->suspendSubmitInLite->click();
$form->wait(3);
/* 调用view方法,方便获取数据 */
$viewForm = $this->initForm('execution', 'view', array('execution' => $executionId ), 'appIframe-project');
if($viewForm->dom->status->getText() != $this->lang->execution->statusList->suspended) return $this->failed('看板状态错误');
return $this->success('挂起看板成功');
}
}
@@ -6,14 +6,15 @@ class taskkanbanTester extends tester
* 检查看板数据。
* Check kanban.
*
* @param string $col
* @param string $num
* @param string $nameId
* @param string $groupId
* @param string $lane 页面上显示的第几个泳道
* @param string $col 泳道中的第几个列
* @access public
* @return object
*/
public function checkKanban($col, $num, $nameId = '', $groupId = '')
public function checkKanban($lane, $col, $num, $nameId = '', $groupId = '')
{
$form = $this->initForm('execution', 'taskkanban', array('execution' => '2'), 'appIframe-execution');
@@ -30,12 +31,13 @@ class taskkanbanTester extends tester
{
$form->dom->xpath['kanbanGroup'] = "(//menu/menu/li)[{$groupId}]";
$form->dom->groupPicker->click();
$form->wait(1);
$form->dom->kanbanGroup->click();
$form->wait(1);
}
/* 检查对应泳道下的卡片数量 */
$form->dom->xpath['col'] = "//div[@z-col='{$col}']//div[@class='card']";
$form->dom->xpath['col'] = "//div[@class='kanban-body']/div[{$lane}]/div[2]/div[{$col}]//div[@class='card']";
if(count($form->dom->getElementList($form->dom->xpath['col'])->element) != $num) return $this->failed("数据错误");
return $this->success("数据正确");
}
+120
View File
@@ -0,0 +1,120 @@
#!/usr/bin/env php
<?php
/**
title=运营界面编辑看板
timeout=0
cid=1
- 编辑看板成功
- 最终测试状态 @SUCCESS
- 测试结果 @编辑看板成功
- 看板名称为空,编辑失败
- 最终测试状态 @SUCCESS
- 测试结果 @编辑看板表单页提示信息正确
- 计划开始时间为空,编辑失败
- 最终测试状态 @SUCCESS
- 测试结果 @编辑看板表单页提示信息正确
- 计划结束时间为空,编辑失败
- 最终测试状态 @SUCCESS
- 测试结果 @编辑看板表单页提示信息正确
- 看板名称重复,编辑失败
- 最终测试状态 @SUCCESS
- 测试结果 @编辑看板表单页提示信息正确
- 看板的计划开始时间早于项目的计划开始时间,编辑失败
- 最终测试状态 @SUCCESS
- 测试结果 @编辑看板表单页提示信息正确
- 看板的计划结束时间晚于项目的计划结束时间,编辑失败
- 最终测试状态 @SUCCESS
- 测试结果 @编辑看板表单页提示信息正确
*/
chdir(__DIR__);
include '../lib/editinlite.ui.class.php';
$project = zenData('project');
$project->id->range('1-100');
$project->project->range('0, 1{2}');
$project->model->range('kanban, []{2}');
$project->type->range('project, kanban{2}');
$project->auth->range('[]');
$project->storyType->range('[]');
$project->parent->range('0, 1{2}');
$project->path->range('`,1,`, `,1,2,`, `,1,3,`');
$project->grade->range('1');
$project->name->range('项目, 看板1, 看板2');
$project->hasProduct->range('0');
$project->begin->range('(-2M)-(-M):1D')->type('timestamp')->format('YY/MM/DD');
$project->end->range('(+2M)-(+3M):1D')->type('timestamp')->format('YY/MM/DD');
$project->openedBy->range('user1');
$project->acl->range('open');
$project->status->range('wait');
$project->vision->range('lite');
$project->gen(3);
$product = zenData('product');
$product->id->range('1-100');
$product->name->range('项目');
$product->shadow->range('1');
$product->bind->range('1');
$product->type->range('normal');
$product->vision->range('lite');
$product->gen(1);
$projectProduct = zenData('projectproduct');
$projectProduct->project->range('1-3');
$projectProduct->product->range('1');
$projectProduct->plan->range('0');
$projectProduct->gen(3);
$tester = new editExecutionTester();
$tester->login();
$execution = array(
'0' => array(
'name' => '编辑看板1',
'begin' => date('Y-m-d'),
'end' => date('Y-m-d', strtotime('+2 days')),
),
'1' => array(
'name' => '',
'begin' => date('Y-m-d'),
'end' => date('Y-m-d', strtotime('+2 days')),
),
'2' => array(
'name' => '编辑看板2',
'begin' => '',
'end' => date('Y-m-d', strtotime('+2 days')),
),
'3' => array(
'name' => '编辑看板3',
'begin' => date('Y-m-d'),
'end' => '',
),
'4' => array(
'name' => '看板2',
'begin' => date('Y-m-d'),
'end' => date('Y-m-d', strtotime('+2 days')),
),
'5' => array(
'name' => '编辑看板5',
'begin' => date('Y-m-d', strtotime('-2 years')),
'end' => date('Y-m-d', strtotime('+2 days')),
),
'6' => array(
'name' => '测试看板9',
'begin' => date('Y-m-d'),
'end' => date('Y-m-d', strtotime('+2 years')),
),
);
r($tester->edit($execution['0'])) && p('status,message') && e('SUCCESS,编辑看板成功'); //编辑看板成功
r($tester->editWithEmptyName($execution['1'])) && p('status,message') && e('SUCCESS,编辑看板表单页提示信息正确'); //看板名称为空,编辑失败
r($tester->edit($execution['2'])) && p('status,message') && e('SUCCESS,编辑看板表单页提示信息正确'); //计划开始时间为空,编辑失败
r($tester->edit($execution['3'])) && p('status,message') && e('SUCCESS,编辑看板表单页提示信息正确'); //计划结束时间为空,编辑失败
r($tester->editWithRepeatName($execution['4'])) && p('status,message') && e('SUCCESS,编辑看板表单页提示信息正确'); //看板名称重复,编辑失败
r($tester->editWithDateError($execution['5'], 'begin')) && p('status,message') && e('SUCCESS,编辑看板表单页提示信息正确'); //看板的计划开始时间早于项目的计划开始时间,编辑失败
r($tester->editWithDateError($execution['6'], 'end')) && p('status,message') && e('SUCCESS,编辑看板表单页提示信息正确'); //看板的计划结束时间晚于项目的计划结束时间,编辑失败
$tester->closeBrowser();
+11 -2
View File
@@ -5,8 +5,17 @@ class kanbanPage extends page
{
parent::__construct($webdriver);
$xpath = array(
'settings' => "//*[@id='navbar']//a[@data-id='settings']/span",
'groupPickerInLite' => "//*[@id='actionBar']/button[1]"
'settings' => "//*[@id='navbar']//a[@data-id='settings']/span",
'groupPickerInLite' => "//*[@id='actionBar']/button[1]",
'kanbanSettingInLite' => "//*[@id='actionBar']/button[2]/span",
/* 编辑看板弹窗中的提交按钮 */
'editSubmitInLite' => "//form[contains(@id,'zin_execution_edit')]/div[last()]/button",
/* 开始看板弹窗中的提交按钮 */
'startSubmitInLite' => "//*[@data-name='realBegan']/..//button",
/* 延期看板弹窗中的提交按钮 */
'putoffSubmitInLite' => "//form[contains(@id,'zin_execution_putoff')]//button",
/* 挂起看板弹窗中的提交按钮 */
'suspendSubmitInLite' => "//form[contains(@id,'zin_execution_suspend')]//button",
);
$this->dom->xpath = array_merge($this->dom->xpath, $xpath);
}
+3
View File
@@ -48,6 +48,9 @@ class viewPage extends page
'putoffSubmit' => "//*[@class='modal-actions']/../div[3]/div/div/form/div[4]/div/button",
/* 激活执行弹窗中的元素 */
'activateSubmit' => "//*[@class='modal-actions']/../div[3]/div/div/form/div[3]/div/button",
/* 运营管理界面下元素 */
'plannedBeginInLite' => "//*[@id='mainContent']/div[2]/div[1]/div/table[2]/tbody/tr/td/div/div[1]/span[2]",
'plannedEndInLite' => "//*[@id='mainContent']/div[2]/div[1]/div/table[2]/tbody/tr/td/div/div[2]/span[2]",
);
$this->dom->xpath = array_merge($this->dom->xpath, $xpath);
}
+154
View File
@@ -0,0 +1,154 @@
#!/usr/bin/env php
<?php
/**
title=运营管理界面延期看板
timeout=0
cid=1
- 未开始的执行,修改可用工作日,保存成功
- 最终测试状态 @SUCCESS
- 测试结果 @延期执行成功
- 进行中的执行,修改可用工作日,保存成功
- 最终测试状态 @SUCCESS
- 测试结果 @延期执行成功
- 未开始的执行,延期弹窗中修改正确的起止日期后点击保存
- 最终测试状态 @SUCCESS
- 测试结果 @延期执行成功
- 进行中的执行,延期弹窗中修改正确的起止日期后点击保存
- 最终测试状态 @SUCCESS
- 测试结果 @延期执行成功
- 计划结束日期为空,可用工日不为空
- 最终测试状态 @SUCCESS
- 测试结果 @延期执行表单页提示信息正确
- 计划开始日期为空
- 最终测试状态 @SUCCESS
- 测试结果 @延期执行表单页提示信息正确
- 计划开始日期小于项目计划开始日期
- 最终测试状态 @SUCCESS
- 测试结果 @延期执行表单页提示信息正确
- 计划结束日期大于项目计划完成日期
- 最终测试状态 @SUCCESS
- 测试结果 @延期执行表单页提示信息正确
- 可用工日为空
- 最终测试状态 @SUCCESS
- 测试结果 @延期执行表单页提示信息正确
- 可用工日为非数字
- 最终测试状态 @SUCCESS
- 测试结果 @延期执行表单页提示信息正确
*/
chdir(__DIR__);
include '../lib/putoffinlite.ui.class.php';
$project = zenData('project');
$project->id->range('1-100');
$project->project->range('0');
$project->model->range('kanban');
$project->type->range('project');
$project->auth->range('[]');
$project->storyType->range('[]');
$project->parent->range('0');
$project->path->range('`,1,`');
$project->grade->range('1');
$project->name->range('项目');
$project->hasProduct->range('0');
$project->begin->range('(-6M)-(-5M):1D')->type('timestamp')->format('YY/MM/DD');
$project->end->range('(+5M)-(+6M):1D')->type('timestamp')->format('YY/MM/DD');
$project->openedBy->range('user1');
$project->acl->range('open');
$project->status->range('doing');
$project->vision->range('lite');
$project->gen(1);
$execution = zenData('project');
$execution->id->range('2, 3');
$execution->project->range('1{2}');
$execution->model->range('[]{2}');
$execution->type->range('kanban{2}');
$execution->auth->range('[]');
$execution->storyType->range('[]');
$execution->parent->range('1{2}');
$execution->path->range('`,1,2,`, `,1,3,`');
$execution->grade->range('1');
$execution->name->range('看板1, 看板2');
$execution->hasProduct->range('0');
$execution->begin->range('(-2M)-(-M):1D')->type('timestamp')->format('YY/MM/DD');
$execution->end->range('(+2M)-(+3M):1D')->type('timestamp')->format('YY/MM/DD');
$execution->openedBy->range('user1');
$execution->acl->range('open');
$execution->status->range('wait, doing');
$execution->vision->range('lite');
$execution->gen(2, false);
$product = zenData('product');
$product->id->range('1-100');
$product->name->range('项目');
$product->shadow->range('1');
$product->bind->range('1');
$product->type->range('normal');
$product->vision->range('lite');
$product->gen(1);
$projectProduct = zenData('projectproduct');
$projectProduct->project->range('1-3');
$projectProduct->product->range('1');
$projectProduct->plan->range('0');
$projectProduct->gen(3);
$tester = new putoffexecutionTester();
$tester->login();
$execution = array(
'0' => array(
'days' => '10',
),
'1' => array(
'begin' => date('Y-m-d', strtotime('-1 days')),
'end' => date('Y-m-d', strtotime('+1 days')),
'days' => '10',
),
'2' => array(
'begin' => '',
'end' => date('Y-m-d'),
'days' => '10',
),
'3' =>array(
'begin' => date('Y-m-d'),
'end' => '',
'days' => '10',
),
'4' => array(
'begin' => date('Y-m-d', strtotime('-10 months')),
'end' => date('Y-m-d'),
'days' => '10',
),
'5' => array(
'begin' => date('Y-m-d'),
'end' => date('Y-m-d', strtotime('+10 months')),
'days' => '10',
),
'6' => array(
'begin' => date('Y-m-d'),
'end' => date('Y-m-d'),
'days' => '',
),
'7' => array(
'begin' => date('Y-m-d'),
'end' => date('Y-m-d'),
'days' => 'aaa',
),
);
r($tester->putoff($execution['0'], '2')) && p('status,message') && e('SUCCESS,延期执行成功'); //未开始的执行,修改可用工作日,保存成功
r($tester->putoff($execution['0'], '3')) && p('status,message') && e('SUCCESS,延期执行成功'); //进行中的执行,修改可用工作日,保存成功
r($tester->putoff($execution['1'], '2')) && p('status,message') && e('SUCCESS,延期执行成功'); //未开始的执行,延期弹窗中修改正确的起止日期后点击保存
r($tester->putoff($execution['1'], '3')) && p('status,message') && e('SUCCESS,延期执行成功'); //进行中的执行,延期弹窗中修改正确的起止日期后点击保存
r($tester->putoffWithWrongDate($execution['3'], '3', 'end')) && p('status,message') && e('SUCCESS,延期执行表单页提示信息正确'); //计划结束日期为空,可用工日不为空
r($tester->putoffWithWrongDate($execution['2'], '2', 'begin')) && p('status,message') && e('SUCCESS,延期执行表单页提示信息正确'); //计划开始日期为空
r($tester->putoffWithWrongDate($execution['4'], '3', 'begin')) && p('status,message') && e('SUCCESS,延期执行表单页提示信息正确'); //计划开始日期小于项目计划开始日期
r($tester->putoffWithWrongDate($execution['5'], '2', 'end')) && p('status,message') && e('SUCCESS,延期执行表单页提示信息正确'); //计划结束日期大于项目计划完成日期
r($tester->putoffWithWrongDays($execution['6'], '2')) && p('status,message') && e('SUCCESS,延期执行表单页提示信息正确'); //可用工日为空
r($tester->putoffWithWrongDays($execution['7'], '2')) && p('status,message') && e('SUCCESS,延期执行表单页提示信息正确'); //可用工日为非数字
$tester->closeBrowser();
+39
View File
@@ -0,0 +1,39 @@
#!/usr/bin/env php
<?php
/**
title=运营管理界面开始看板
timeout=0
cid=1
*/
chdir(__DIR__);
include '../lib/startinlite.ui.class.php';
$project = zenData('project');
$project->id->range('1-100');
$project->project->range('0, 1{2}');
$project->model->range('kanban, []{2}');
$project->type->range('project, kanban{2}');
$project->auth->range('[]');
$project->storyType->range('[]');
$project->parent->range('0, 1{2}');
$project->path->range('`,1,`, `,1,2,`, `,1,3,`');
$project->grade->range('1');
$project->name->range('项目, 看板1, 看板2');
$project->begin->range('(-2M)-(-M):1D')->type('timestamp')->format('YY/MM/DD');
$project->end->range('(+2M)-(+3M):1D')->type('timestamp')->format('YY/MM/DD');
$project->openedBy->range('user1');
$project->acl->range('open');
$project->status->range('wait');
$project->vision->range('lite');
$project->gen(3);
$tester = new startExecutionTester();
$tester->login();
$realBegan = array(date('Y-m-d', strtotime('+20 days')), date('Y-m-d'));
r($tester->startWithGreaterDate($realBegan[0], 2)) && p('status,message') && e('SUCCESS,开始看板表单页提示信息正确');
r($tester->start($realBegan[1], 2)) && p('status,message') && e('SUCCESS,开始看板成功');
$tester->closeBrowser();
@@ -0,0 +1,37 @@
#!/usr/bin/env php
<?php
/**
title=运营界面挂起看板
timeout=0
cid=1
*/
chdir(__DIR__);
include '../lib/suspendinlite.ui.class.php';
$project = zenData('project');
$project->id->range('1-100');
$project->project->range('0, 1{2}');
$project->model->range('kanban, []{2}');
$project->type->range('project, kanban{2}');
$project->auth->range('[]');
$project->storyType->range('[]{2}');
$project->parent->range('0, 1{2}');
$project->path->range('`,1,`, `,1,2,`, `,1,3,`');
$project->grade->range('1');
$project->name->range('项目, 未开始的看板, 已关闭的看板');
$project->begin->range('(-2M)-(-M):1D')->type('timestamp')->format('YY/MM/DD');
$project->end->range('(+2M)-(+3M):1D')->type('timestamp')->format('YY/MM/DD');
$project->openedBy->range('user1');
$project->acl->range('open');
$project->status->range('doing, wait, doing');
$project->vision->range('lite');
$project->gen(3);
$tester = new suspendExecutionTester();
$tester->login();
r($tester->suspend('2')) && p('status,message') && e('SUCCESS,挂起看板成功'); //挂起未开始的看板
r($tester->suspend('3')) && p('status,message') && e('SUCCESS,挂起看板成功'); //挂起进行中的看板
$tester->closeBrowser();
+3 -6
View File
@@ -99,10 +99,7 @@ $bug->gen(8);
$tester = new taskkanbanTester();
$tester->login();
r($tester->checkKanban('1', 1)) && p('status,message') && e('SUCCESS,数据正确');
r($tester->checkKanban('4', 4)) && p('status,message') && e('SUCCESS,数据正确');
r($tester->checkKanban('10', 1)) && p('status,message') && e('SUCCESS,数据正确');
r($tester->checkKanban('20', 0)) && p('status,message') && e('SUCCESS,数据正确');
r($tester->checkKanban('30', 1)) && p('status,message') && e('SUCCESS,数据正确');
r($tester->checkKanban('1', 1, '4', '3')) && p('status,message') && e('SUCCESS,数据正确');
r($tester->checkKanban('1', '1', '2', '3', '1')) && p('status,message') && e('SUCCESS,数据正确');
r($tester->checkKanban('1', '4', '4', '4', '5')) && p('status,message') && e('SUCCESS,数据正确');
r($tester->checkKanban('1', '1', '1', '4', '3')) && p('status,message') && e('SUCCESS,数据正确');
$tester->closeBrowser();
+1 -2
View File
@@ -31,5 +31,4 @@ r($gitlab->addPushWebhookTest(1, $token, 2)) && p() && e('1'); //使用repoID
r($gitlab->addPushWebhookTest(1, $token, -1)) && p() && e('0'); //使用repoID为1,异常的项目id推送webhook
r($gitlab->addPushWebhookTest(2, $token)) && p() && e('0'); //使用repoID为2,不存在的项目id推送webhook
r($gitlab->addPushWebhookTest(2, $token, 2)) && p() && e('1'); //使用repoID为2,存在的项目id推送webhook
r($gitlab->addPushWebhookTest(2, $token, 2)) && p() && e('1'); //使用repoID为2,存在的项目id推送webhook
+7 -3
View File
@@ -1074,9 +1074,13 @@ $config->group->package->projectTemplate = new stdclass();
$config->group->package->projectTemplate->order = 5;
$config->group->package->projectTemplate->subset = 'projectTemplate';
$config->group->package->projectTemplate->privs = array();
$config->group->package->projectTemplate->privs['project-template'] = array('edition' => 'max,ipd', 'vision' => 'rnd', 'order' => 5, 'depend' => array('project-index'));
$config->group->package->projectTemplate->privs['project-createTemplate'] = array('edition' => 'max,ipd', 'vision' => 'rnd', 'order' => 10, 'depend' => array('project-index', 'project-template'));
$config->group->package->projectTemplate->privs['project-deleteTemplate'] = array('edition' => 'max,ipd', 'vision' => 'rnd', 'order' => 15, 'depend' => array('project-index', 'project-template'));
$config->group->package->projectTemplate->privs['project-template'] = array('edition' => 'max,ipd', 'vision' => 'rnd', 'order' => 5, 'depend' => array('project-index'));
$config->group->package->projectTemplate->privs['project-createTemplate'] = array('edition' => 'max,ipd', 'vision' => 'rnd', 'order' => 10, 'depend' => array('project-index', 'project-template'));
$config->group->package->projectTemplate->privs['project-editTemplate'] = array('edition' => 'max,ipd', 'vision' => 'rnd', 'order' => 15, 'depend' => array('project-index', 'project-template'));
$config->group->package->projectTemplate->privs['project-publishTemplate'] = array('edition' => 'max,ipd', 'vision' => 'rnd', 'order' => 20, 'depend' => array('project-index', 'project-template'));
$config->group->package->projectTemplate->privs['project-disableTemplate'] = array('edition' => 'max,ipd', 'vision' => 'rnd', 'order' => 25, 'depend' => array('project-index', 'project-template'));
$config->group->package->projectTemplate->privs['project-templatePriv'] = array('edition' => 'max,ipd', 'vision' => 'rnd', 'order' => 30, 'depend' => array('project-index', 'project-template'));
$config->group->package->projectTemplate->privs['project-deleteTemplate'] = array('edition' => 'max,ipd', 'vision' => 'rnd', 'order' => 35, 'depend' => array('project-index', 'project-template'));
$config->group->package->browseExecution = new stdclass();
$config->group->package->browseExecution->order = 5;
+5 -5
View File
@@ -36,11 +36,11 @@ zenData('usergroup')->loadYaml('usergroup')->gen(10);
$groupID = 2;
$normal1Group = array('name' => '复制的分组1');
$normal2Group = array('name' => '复制的分组2');
$normal3Group = array('name' => '复制的分组3');
$normal4Group = array('name' => '复制的分组4');
$existGroup = array('name' => '这是一个新的用户分组5');
$normal1Group = array('name' => '复制的分组1', 'project' => '0');
$normal2Group = array('name' => '复制的分组2', 'project' => '0');
$normal3Group = array('name' => '复制的分组3', 'project' => '0');
$normal4Group = array('name' => '复制的分组4', 'project' => '0');
$existGroup = array('name' => '这是一个新的用户分组5', 'project' => '0');
$group = new groupTest();
r($group->copyTest($groupID, $normal1Group)) && p('name') && e('复制的分组1'); // 正常复制
+5 -5
View File
@@ -21,11 +21,11 @@ include dirname(__FILE__, 2) . '/lib/group.unittest.class.php';
zenData('group')->gen(5);
su('admin');
$normalGroup = array('name' => '我是一个分组', 'desc' => '');
$repeatGroup = array('name' => '这是一个新的用户分组5', 'desc' => '');
$visionGroup = array('name' => '我是当前管理界面的分组', 'desc' => '');
$limitedGroup1 = array('name' => '我是一个受限分组', 'desc' => '', 'role' => 'limited');
$limitedGroup2 = array('name' => '我是另一个受限分组', 'desc' => '', 'limited' => 1);
$normalGroup = array('name' => '我是一个分组', 'desc' => '', 'project' => '0');
$repeatGroup = array('name' => '这是一个新的用户分组5', 'desc' => '', 'project' => '0');
$visionGroup = array('name' => '我是当前管理界面的分组', 'desc' => '', 'project' => '0');
$limitedGroup1 = array('name' => '我是一个受限分组', 'desc' => '', 'role' => 'limited', 'project' => '0');
$limitedGroup2 = array('name' => '我是另一个受限分组', 'desc' => '', 'limited' => 1, 'project' => '0');
$group = new groupTest();
+12 -12
View File
@@ -18,9 +18,9 @@ title=测试 messageModel->saveNotice();
cid=1
- 发送 需求 2 动态 2 用户 admin 的消息
- 属性id @1
- 属性objectType @message
- 属性action @2
- 属性id @1
- 属性objectType @message
- 属性action @2
- 属性createdBy @admin
- 发送 任务 6 动态 6 用户 admin 的消息
- 属性id @2
@@ -39,15 +39,15 @@ cid=1
- 属性createdBy @user1
- 发送 任务 6 动态 6 用户 user1 的消息
- 属性id @4
- 属性objectType @message
- 属性action @6
- 属性createdBy @user1
- 发送 反馈 0 动态 0 用户 user1 的消息
- 属性id @0
- 属性objectType @0
- 属性action @0
- 属性objectType @message
- 属性action @6
- 属性createdBy @user1
- 发送 反馈 0 动态 0 用户 user1 的消息
- 属性id @0
- 属性objectType @0
- 属性action @0
- 属性createdBy @0
- 发送 需求 2 动态 2 不传 actor 的消息
- 发送 需求 2 动态 2 不传 actor 的消息
- 属性id @5
- 属性objectType @message
- 属性action @2
@@ -89,4 +89,4 @@ r($message->saveNoticeTest($objectType[2], $objectID[2], $actionType[2], $action
r($message->saveNoticeTest($objectType[0], $objectID[0], $actionType[0], $actionID[0], $actor[3])) && p('id') && e('0'); // 发送 需求 2 动态 2 未登录 的消息
r($message->saveNoticeTest($objectType[1], $objectID[1], $actionType[1], $actionID[1], $actor[3])) && p('id') && e('0'); // 发送 任务 6 动态 6 未登录 的消息
r($message->saveNoticeTest($objectType[2], $objectID[2], $actionType[2], $actionID[2], $actor[3])) && p('id') && e('0'); // 发送 反馈 0 动态 0 未登录 的消息
r($message->saveNoticeTest($objectType[2], $objectID[2], $actionType[2], $actionID[2], $actor[3])) && p('id') && e('0'); // 发送 反馈 0 动态 0 未登录 的消息
+12 -6
View File
@@ -170,10 +170,12 @@ class metric extends control
*/
public function updateMetricLib()
{
// 保存当前的错误报告级别和显示错误的设置
/* 保存当前的错误报告级别和显示错误的设置。*/
/* Save the current error reporting level and display error settings. */
$originalDebug = $this->config->debug;
// 开启调试模式
/* 开启调试模式。*/
/* Enable debug mode. */
$this->config->debug = 2;
$this->metric->saveLogs('----------------------------------');
@@ -184,7 +186,8 @@ class metric extends control
$classifiedCalcGroup = $this->metric->classifyCalc($calcList);
$this->metricZen->calculateMetric($classifiedCalcGroup);
// 恢复之前的调试状态
/* 恢复之前的调试状态。*/
/* Restore the previous debug state. */
$this->config->debug = $originalDebug;
if(dao::isError())
@@ -197,10 +200,12 @@ class metric extends control
public function updateDashboardMetricLib()
{
// 保存当前的错误报告级别和显示错误的设置
/* 保存当前的错误报告级别和显示错误的设置。*/
/* Save the current error reporting level and display error settings. */
$originalDebug = $this->config->debug;
// 开启调试模式
/* 开启调试模式。*/
/* Enable debug mode. */
$this->config->debug = 2;
$this->metric->saveLogs('-------------------------------------------');
@@ -213,7 +218,8 @@ class metric extends control
$classifiedCalcGroup = $this->metric->classifyCalc($calcList);
$this->metricZen->calculateMetric($classifiedCalcGroup);
// 恢复之前的调试状态
/* 恢复之前的调试状态。*/
/* Restore the previous debug state. */
$this->config->debug = $originalDebug;
if(dao::isError())
+77 -5
View File
@@ -11,6 +11,8 @@ declare(strict_types=1);
*/
class metricZen extends metric
{
private $validObjects;
/**
* 构建创建度量的数据。
* Build metric data for create.
@@ -213,6 +215,40 @@ class metricZen extends metric
return number_format((float)$time, 5, '.', '0');
}
/**
* 获取有效的对象。
* Get valid objects.
*
* @access private
* @return array
*/
private function getValidObjects()
{
if($this->validObjects !== null) return $this->validObjects;
/* 保证逻辑集中,这里直接使用sql查询获取数据,保证查询性能。*/
/* To ensure logical concentration, here we directly use sql to get data to ensure query performance. */
$productList = $this->dao->select('id')->from(TABLE_PRODUCT)
->where('deleted')->eq(0)
->andWhere('status')->ne('closed')
->andWhere('shadow')->eq(0)
->fetchPairs('id');
$projectList = $this->dao->select('id')->from(TABLE_PROJECT)
->where('deleted')->eq(0)
->andWhere('status')->notIN('closed,done')
->andWhere('type')->eq('project')
->fetchPairs('id');
$executionList = $this->dao->select('id')->from(TABLE_EXECUTION)
->where('deleted')->eq(0)
->andWhere('status')->notIN('closed,done')
->andWhere('type')->in('sprint,stage,kanban')
->fetchPairs('id');
$this->validObjects = array('product' => $productList, 'project' => $projectList, 'execution' => $executionList);
}
/**
* 计算度量数据。
* Calculate metric data.
@@ -225,23 +261,35 @@ class metricZen extends metric
{
set_time_limit(0);
$calcBeginTime = $this->startTime();
foreach($classifiedCalcGroup as $calcGroup)
{
try
{
/* 开始计算度量数据。*/
/* Start calculating metric data. */
$beginTime = $this->startTime();
$statement = $this->prepareDataset($calcGroup);
$sql = $statement ? $statement->get() : '';
$this->calcMetric($statement, $calcGroup->calcList);
$recordWithCode = $this->prepareMetricRecord($calcGroup->calcList);
$calcTime = $this->endTime($beginTime);
/* 开始插入度量数据。*/
/* Start inserting metric data. */
$beginTime = $this->startTime();
$this->metric->insertMetricLib($recordWithCode);
$executeTime = $this->endTime($beginTime);
/* 记录度量数据计算和插入的时间以及sql语句。*/
/* Record the time of metric data calculation and insertion and sql statement. */
$total = 0;
$codes = '';
$sql = $statement ? $statement->get() : '';
foreach($recordWithCode as $code => $records)
{
$count = count($records);
@@ -260,9 +308,13 @@ class metricZen extends metric
}
}
/* 记录度量数据计算的总时间。*/
/* Record the total time of metric data calculation. */
$executeTime = $this->endTime($calcBeginTime);
$this->metric->saveLogs("Calculate all consumed time: {$executeTime} seconds");
/* 开始去重。*/
/* Start deduplication. */
$beginTime = $this->startTime();
$metrics = $this->metric->getExecutableMetric();
foreach($metrics as $code)
@@ -272,7 +324,13 @@ class metricZen extends metric
$executeTime = $this->endTime($deduplicationBeginTime);
$this->metric->saveLogs("Deduplication consumed time: {$executeTime} seconds, the code: $code");
}
/* 重建主键。*/
/* Rebuild the primary key. */
$this->metric->rebuildPrimaryKey();
/* 记录去重的时间。*/
/* Record the time of deduplication. */
$executeTime = $this->endTime($beginTime);
$this->metric->saveLogs("Deduplicate all consumed time: {$executeTime} seconds");
}
@@ -287,30 +345,44 @@ class metricZen extends metric
*/
protected function prepareMetricRecord($calcList)
{
/* 获取今天和昨天的日期。*/
/* Get the date of today and yesterday. */
$yesterday = date('j', strtotime('-1 day', strtotime('today')));
$today = date('j');
$options = array('year' => date('Y'), 'month' => date('n'), 'week' => substr(date('oW'), -2), 'day' => "$today,$yesterday");
$now = helper::now();
$dateValues = $this->metric->parseDateStr($now);
/* 获取未关闭的对象id。*/
/* Get the id of the unclosed objects. */
$this->getValidObjects();
$validObjects = $this->validObjects;
$records = array();
foreach($calcList as $code => $calc)
{
$metric = $this->metric->getByCode($code);
/* 如果度量项是复用的,则计算复用的度量数据。*/
/* If the metric is reused, calculate the reused metric data. */
if($calc->reuse) $this->prepareReuseMetricResult($calc, $options);
/* 判断是否统计关闭时的对象的度量数据。*/
$endWithClosing = preg_match('/_when_closing$/', $code);
$results = $calc->getResult($options);
$records[$code] = array();
if(is_array($results))
{
$scope = $metric->scope;
foreach($results as $record)
{
$record = (object)$record;
if(!is_numeric($record->value) || empty($record->value)) continue;
/* 如果度量项是产品、项目、执行且不统计关闭时的对象的度量数据,则过滤掉已关闭的数据。*/
/* If the metric is product, project, execution, filter out the closed data. */
if(!$endWithClosing && isset($validObjects[$scope]) && !isset($validObjects[$scope][$record->$scope])) continue;
$record->metricID = $calc->id;
$record->metricCode = $code;
$record->date = $now;
$record->date = helper::now();
$record->system = $metric->scope == 'system' ? 1 : 0;
$records[$code][] = $record;
+1
View File
@@ -78,6 +78,7 @@ $lang->misc->expiredCountTips = 'There are <span class="expired-tips text-blu
$lang->misc->expiredPluginTips = 'Expired plugins are: %s. ';
$lang->misc->expiringPluginTips = 'The plug-ins that will expire are: %s.';
$lang->misc->expiredTipsForAdmin = 'There are %s plug-ins in the current system that will expire soon. To avoid affecting the regular use of the function, please renew or uninstall them in the system background plug-in management as soon as possible.';
$lang->misc->metriclibTips = 'Added a new metric library indexing function, which can significantly improve the query speed of related metrics after updating the index. You can update it on the "Admin->System->Metric Library" page.';
$lang->misc->noticeRepair = "<h5>If you are not Administrator, contact your ZenTao Administrator to repair tables.</h5>
<h5>If you are the Administrator, login your ZenTao host and create a file named <span>%s</span>.</h5>
+1
View File
@@ -78,6 +78,7 @@ $lang->misc->expiredCountTips = '系统中有<span class="expired-tips text-b
$lang->misc->expiredPluginTips = '已到期的插件为:%s。';
$lang->misc->expiringPluginTips = '即将到期的插件为:%s。';
$lang->misc->expiredTipsForAdmin = '当前系统中有%s个插件即将到期,为避免影响功能的正常使用,请尽快到系统后台插件管理中进行续费或卸载处理。';
$lang->misc->metriclibTips = '新增更新度量库索引功能,更新索引后可大幅度提升相关度量项的查询速度,可以到『后台->系统设置->度量库』页面更新。';
$lang->misc->noticeRepair = "<h5>普通用户请联系管理员进行修复</h5>
<h5>管理员请登录禅道所在的服务器,创建<span>%s</span>文件。</h5>
+22
View File
@@ -84,6 +84,28 @@ class miscModel extends model
return $remind;
}
/**
* 获取更新度量库表索引的通知。
* Get the notification information about updating metriclib table index.
*
* @access public
* @return string
*/
public function getMetriclibRemind(): string
{
if(!$this->app->user->admin) return '';
if($this->config->edition == 'open') return '';
$remind = '';
if(empty($this->config->global->metriclibShowed))
{
$remind .= '<p>' . $this->lang->misc->metriclibTips . '</p>';
$remind .= "<p class='text-center mt-4'>" . html::a(helper::createLink('admin', 'metriclib'), $this->lang->misc->view, '', "id='metriclibButton' class='btn primary wide' data-app='admin'", false) . '</p>';
$this->loadModel('setting')->setItem("{$this->app->user->account}.common.global.metriclibShowed", 1);
}
return $remind;
}
/**
* 检查一键安装包的安全性。
* Check one click package.
+46
View File
@@ -0,0 +1,46 @@
#!/usr/bin/env php
<?php
include dirname(__FILE__, 5) . '/test/lib/init.php';
zenData('company')->gen(1);
zenData('user')->gen(2);
zenData('config')->gen(0);
su('user1');
/**
title=测试 miscModel->getMetriclibRemind();
timeout=0
cid=1
- 非管理员用户返回空字符串。 @0
- 管理员用户开源版返回空字符串。 @0
- 管理员用户企业版返回正常内容。 @1
- 管理员用户企业版返回正常内容后数据库中记录已显示过。 @1
- 管理员用户企业版返回正常内容一次后第二次返回空字符串。 @0
*/
global $tester, $config;
$config->edition = 'open';
$misc = $tester->loadModel('misc');
$remind = "<p>新增更新度量库索引功能,更新索引后可大幅度提升相关度量项的查询速度";
r($misc->getMetriclibRemind()) && p() && e(0); //非管理员用户返回空字符串。
su('admin');
r($misc->getMetriclibRemind()) && p() && e(0); //管理员用户开源版返回空字符串。
$config->edition = 'biz';
r(mb_substr($misc->getMetriclibRemind(), 0, 36) == $remind) && p() && e(1); //管理员用户企业版返回正常内容。
$showed = $tester->loadModel('setting')->getItem('owner=admin&module=common&section=global&key=metriclibShowed');
r($showed) && p() && e(1); //管理员用户企业版返回正常内容后数据库中记录已显示过。
$config->global->metriclibShowed = $showed;
r($misc->getMetriclibRemind()) && p() && e(0); //管理员用户企业版返回正常内容一次后第二次返回空字符串。
@@ -8,11 +8,23 @@ title=测试 miscModel->getPluginRemind();
timeout=0
cid=1
- 调用方法返回值 @0
- 调用方法返回值 @0
- 调用方法返回值 @0
- 调用方法返回值 @0
- 调用方法返回值 @0
*/
zenData('extension')->gen(10);
global $tester;
$tester->loadModel('misc');
/* 由于没办法上传过期授权文件,这个单测无法实际测试。 */
r($tester->misc->getPluginRemind()) && p() && e('0'); //调用方法返回值
r($tester->misc->getPluginRemind()) && p() && e('0'); //调用方法返回值
r($tester->misc->getPluginRemind()) && p() && e('0'); //调用方法返回值
r($tester->misc->getPluginRemind()) && p() && e('0'); //调用方法返回值
r($tester->misc->getPluginRemind()) && p() && e('0'); //调用方法返回值
?>
+6
View File
@@ -16,6 +16,12 @@ window.onRenderCell = function(result, {row, col})
link = oaReviewLink.replace('{module}', row.data.module);
delete result[0].props.items[0]['data-toggle'];
}
else if(['mr', 'pullreq'].includes(row.data.module))
{
link = $.createLink(row.data.module, 'view', 'id=' + row.data.id);
result[0].props.items[0]['disabled'] = viewPrivs[row.data.module];
delete result[0].props.items[0]['data-toggle'];
}
else
{
if(!noFlowAuditModules.includes(row.data.module) && row.data.module != 'charter') link = flowReviewLink;
+1
View File
@@ -165,6 +165,7 @@ $lang->my->featureBar['audit']['story'] = $lang->SRCommon;
$lang->my->featureBar['audit']['requirement'] = $lang->URCommon;
$lang->my->featureBar['audit']['epic'] = $lang->ERCommon;
$lang->my->featureBar['audit']['testcase'] = 'Test case';
$lang->my->featureBar['audit']['mr'] = 'Merge request';
if(in_array($config->edition, array('max', 'ipd')) and (helper::hasFeature('waterfall') or helper::hasFeature('waterfallplus'))) $lang->my->featureBar['audit']['project'] = $lang->projectCommon;
if($config->edition != 'open') $lang->my->featureBar['audit']['feedback'] = 'Feedback';
if($config->edition != 'open' and helper::hasFeature('OA')) $lang->my->featureBar['audit']['oa'] = 'OA';
+1
View File
@@ -165,6 +165,7 @@ $lang->my->featureBar['audit']['story'] = $lang->SRCommon;
$lang->my->featureBar['audit']['requirement'] = $lang->URCommon;
$lang->my->featureBar['audit']['epic'] = $lang->ERCommon;
$lang->my->featureBar['audit']['testcase'] = 'Test case';
$lang->my->featureBar['audit']['mr'] = 'Merge request';
if(in_array($config->edition, array('max', 'ipd')) and (helper::hasFeature('waterfall') or helper::hasFeature('waterfallplus'))) $lang->my->featureBar['audit']['project'] = $lang->projectCommon;
if($config->edition != 'open') $lang->my->featureBar['audit']['feedback'] = 'Feedback';
if($config->edition != 'open' and helper::hasFeature('OA')) $lang->my->featureBar['audit']['oa'] = 'OA';
+1
View File
@@ -165,6 +165,7 @@ $lang->my->featureBar['audit']['story'] = $lang->SRCommon;
$lang->my->featureBar['audit']['requirement'] = $lang->URCommon;
$lang->my->featureBar['audit']['epic'] = $lang->ERCommon;
$lang->my->featureBar['audit']['testcase'] = 'Test case';
$lang->my->featureBar['audit']['mr'] = 'Merge request';
if(in_array($config->edition, array('max', 'ipd')) and (helper::hasFeature('waterfall') or helper::hasFeature('waterfallplus'))) $lang->my->featureBar['audit']['project'] = $lang->projectCommon;
if($config->edition != 'open') $lang->my->featureBar['audit']['feedback'] = 'Feedback';
if($config->edition != 'open' and helper::hasFeature('OA')) $lang->my->featureBar['audit']['oa'] = 'OA';
+1
View File
@@ -165,6 +165,7 @@ $lang->my->featureBar['audit']['story'] = $lang->SRCommon;
$lang->my->featureBar['audit']['requirement'] = $lang->URCommon;
$lang->my->featureBar['audit']['epic'] = $lang->ERCommon;
$lang->my->featureBar['audit']['testcase'] = '用例';
$lang->my->featureBar['audit']['mr'] = '合并请求';
if(in_array($config->edition, array('max', 'ipd')) and (helper::hasFeature('waterfall') or helper::hasFeature('waterfallplus'))) $lang->my->featureBar['audit']['project'] = $lang->projectCommon;
if($config->edition != 'open') $lang->my->featureBar['audit']['feedback'] = '反馈';
if($config->edition != 'open' and helper::hasFeature('OA')) $lang->my->featureBar['audit']['oa'] = '办公';
+21
View File
@@ -956,6 +956,7 @@ class myModel extends model
if($this->config->vision != 'or' && $this->getReviewingApprovals('id_desc', true)) $typeList[] = 'project';
if($this->getReviewingFeedbacks('id_desc', true)) $typeList[] = 'feedback';
if($this->config->vision != 'or' && $this->getReviewingOA('status', true)) $typeList[] = 'oa';
if($this->config->vision != 'or' && $this->getReviewingMRs('id_desc')) $typeList[] = 'mr';
$typeList = array_merge($typeList, $this->getReviewingFlows('all', 'id_desc', true));
$flows = $this->config->edition == 'open' ? array() : $this->dao->select('module,name')->from(TABLE_WORKFLOW)->where('module')->in($typeList)->andWhere('buildin')->eq(0)->fetchPairs('module', 'name');
@@ -991,6 +992,7 @@ class myModel extends model
if($vision != 'or' && ($browseType == 'all' || $browseType == 'testcase') && common::hasPriv('testcase', 'review')) $reviewList = array_merge($reviewList, $this->getReviewingCases());
if($vision != 'or' && ($browseType == 'all' || $browseType == 'project')) $reviewList = array_merge($reviewList, $this->getReviewingApprovals());
if($vision != 'or' && ($browseType == 'all' || $browseType == 'oa')) $reviewList = array_merge($reviewList, $this->getReviewingOA());
if($vision != 'or' && ($browseType == 'all' || $browseType == 'mr')) $reviewList = array_merge($reviewList, $this->getReviewingMRs());
if($browseType == 'all' || !in_array($browseType, $this->config->my->noFlowAuditModules)) $reviewList = array_merge($reviewList, $this->getReviewingFlows($browseType));
if(($browseType == 'all' || $browseType == 'feedback') && common::hasPriv('feedback', 'review')) $reviewList = array_merge($reviewList, $this->getReviewingFeedbacks());
if(empty($reviewList)) return array();
@@ -1151,6 +1153,25 @@ class myModel extends model
return array_values($cases);
}
/**
* 获取待评审的MR。
* Get reviewing mrs.
*
* @param string $orderBy
* @access public
* @return array
*/
public function getReviewingMRs(string $orderBy = 'id_desc'): array
{
return $this->dao->select("`id`, `title`, IF(`isFlow`='1', 'pullreq', 'mr') AS type, `createdDate` AS time, `approvalStatus` AS status, 0 AS product, 0 AS project")->from(TABLE_MR)
->where('deleted')->eq('0')
->andWhere('approvalStatus')->notIn(array('approved', 'rejected'))
->andWhere('status')->ne('closed')
->andWhere('assignee')->eq($this->app->user->account)
->orderBy($orderBy)
->fetchAll('id');
}
/**
* 获取待评审的审批。
* Get reviewing approvals.
+20
View File
@@ -6,6 +6,7 @@ class myTest
{
global $tester;
$this->objectModel = $tester->loadModel('my');
$tester->dao->delete()->from(TABLE_ACTIONRECENT)->exec();
}
/**
@@ -440,6 +441,25 @@ class myTest
return !empty($return) ? implode(',', array_column($return, 'id')) : 'empty';
}
/**
* 测试获取审批中的合并请求。
* Test get reviewing mrs.
*
* @param string $account
* @param string $orderBy
* @param bool $checkExist
* @access public
* @return string|array
*/
public function getReviewingMRsTest(string $account, string $orderBy): string|array
{
su($account);
$result = $this->objectModel->getReviewingMRs($orderBy);
if(dao::isError()) return dao::getError();
return $result;
}
/**
* 测试获取审批中的用例。
* Test get reviewing cases.
+5 -5
View File
@@ -37,8 +37,8 @@ $app->loadLang('action');
$my = new myTest();
$actions = $my->getActionsTest();
r($actions) && p('0') && e('96'); // 正常查询action
r($actions) && p('1') && e('64'); // 正常查询action
r($actions) && p('2') && e('32'); // 正常查询action
r($actions) && p('3') && e('95'); // 正常查询action
r($actions) && p('4') && e('63'); // 正常查询action
r($actions) && p('0') && e('93'); // 正常查询action
r($actions) && p('1') && e('62'); // 正常查询action
r($actions) && p('2') && e('31'); // 正常查询action
r($actions) && p('3') && e('92'); // 正常查询action
r($actions) && p('4') && e('61'); // 正常查询action
@@ -18,6 +18,7 @@ cid=1
- 测试获取排序 id_desc 的审批是否存在。 @empty
- 测试获取排序 id_asc 的审批id。 @empty
- 测试获取排序 id_asc 的审批是否存在。 @empty
- 测试获取排序 id_asc 的审批是否存在。 @empty
*/
@@ -29,4 +30,5 @@ $checkExist = array(false, true);
r($my->getReviewingApprovalsTest($orderBy[0], $checkExist[0])) && p() && e('empty'); // 测试获取排序 id_desc 的审批id。
r($my->getReviewingApprovalsTest($orderBy[0], $checkExist[1])) && p() && e('empty'); // 测试获取排序 id_desc 的审批是否存在。
r($my->getReviewingApprovalsTest($orderBy[1], $checkExist[0])) && p() && e('empty'); // 测试获取排序 id_asc 的审批id。
r($my->getReviewingApprovalsTest($orderBy[1], $checkExist[1])) && p() && e('empty'); // 测试获取排序 id_asc 的审批是否存在。
r($my->getReviewingApprovalsTest($orderBy[1], $checkExist[1])) && p() && e('empty'); // 测试获取排序 id_asc 的审批是否存在。
r($my->getReviewingApprovalsTest($orderBy[1], $checkExist[1])) && p() && e('empty'); // 测试获取排序 id_asc 的审批是否存在。
@@ -5,6 +5,7 @@ include dirname(__FILE__, 5) . '/test/lib/init.php';
include dirname(__FILE__, 2) . '/lib/my.unittest.class.php';
zenData('case')->gen('20');
zenData('product')->gen('20');
zenData('user')->gen('2');
/**
+8 -11
View File
@@ -11,6 +11,7 @@ zenData('demand')->loadYaml('demand_reviewing')->gen('20');
zenData('demandreview')->gen('20');
zenData('user')->gen('10');
zenData('action')->gen('0');
zenData('mr')->gen('1');
/**
@@ -18,14 +19,10 @@ title=测试 myModel->getReviewingList();
timeout=0
cid=1
- 测试获取用户 admin 类型 all 排序 id_desc 不分页 的待评审类型。 @testcase,17;testcase,13;story,10;testcase,9;story,8;story,6;testcase,5;story,4;story,2;testcase,1;
- 测试获取用户 admin 类型 all 排序 id_asc 不分页 的待评审类型。 @testcase,1;story,2;story,4;testcase,5;story,6;story,8;testcase,9;story,10;testcase,13;testcase,17;
- 测试获取用户 admin 类型 all 排序 id_desc 不分页 的待评审类型。 @testcase,17;testcase,13;story,10;testcase,9;story,8;story,6;testcase,5;story,4;story,2;testcase,1;mr,1;
- 测试获取用户 admin 类型 all 排序 id_asc 不分页 的待评审类型。 @testcase,1;mr,1;story,2;story,4;testcase,5;story,6;story,8;testcase,9;story,10;testcase,13;testcase,17;
- 测试获取用户 admin 类型 all 排序 id_desc 获取前两个 的待评审类型。 @testcase,17;testcase,13;
- 测试获取用户 admin 类型 all 排序 id_asc 获取前两个 的待评审类型。 @testcase,1;story,2;
- 测试获取用户 admin 类型 all 排序 id_asc 获取前两个 的待评审类型。 @testcase,1;mr,1;
- 测试获取用户 admin 类型 createdbyme 排序 id_desc 不分页 的待评审类型。 @0
- 测试获取用户 admin 类型 createdbyme 排序 id_asc 不分页 的待评审类型。 @0
- 测试获取用户 admin 类型 createdbyme 排序 id_desc 获取前两个 的待评审类型。 @0
@@ -33,7 +30,7 @@ cid=1
*/
global $teser, $config, $app;
global $tester, $config, $app;
$app->rawModule = 'my';
$app->rawMethod = 'getReviewingList';
$config->edition = 'open';
@@ -50,10 +47,10 @@ $browseType = array('all', 'createdbyme');
$orderBy = array('id_desc', 'id_asc');
$pageList = array(null, $pager);
r($my->getReviewingListTest($account[0], $browseType[0], $orderBy[0], $pageList[0])) && p() && e('testcase,17;testcase,13;story,10;testcase,9;story,8;story,6;testcase,5;story,4;story,2;testcase,1;'); // 测试获取用户 admin 类型 all 排序 id_desc 不分页 的待评审类型。
r($my->getReviewingListTest($account[0], $browseType[0], $orderBy[1], $pageList[0])) && p() && e('testcase,1;story,2;story,4;testcase,5;story,6;story,8;testcase,9;story,10;testcase,13;testcase,17;'); // 测试获取用户 admin 类型 all 排序 id_asc 不分页 的待评审类型。
r($my->getReviewingListTest($account[0], $browseType[0], $orderBy[0], $pageList[0])) && p() && e('testcase,17;testcase,13;story,10;testcase,9;story,8;story,6;testcase,5;story,4;story,2;testcase,1;mr,1;'); // 测试获取用户 admin 类型 all 排序 id_desc 不分页 的待评审类型。
r($my->getReviewingListTest($account[0], $browseType[0], $orderBy[1], $pageList[0])) && p() && e('testcase,1;mr,1;story,2;story,4;testcase,5;story,6;story,8;testcase,9;story,10;testcase,13;testcase,17;'); // 测试获取用户 admin 类型 all 排序 id_asc 不分页 的待评审类型。
r($my->getReviewingListTest($account[0], $browseType[0], $orderBy[0], $pageList[1])) && p() && e('testcase,17;testcase,13;'); // 测试获取用户 admin 类型 all 排序 id_desc 获取前两个 的待评审类型。
r($my->getReviewingListTest($account[0], $browseType[0], $orderBy[1], $pageList[1])) && p() && e('testcase,1;story,2;'); // 测试获取用户 admin 类型 all 排序 id_asc 获取前两个 的待评审类型。
r($my->getReviewingListTest($account[0], $browseType[0], $orderBy[1], $pageList[1])) && p() && e('testcase,1;mr,1;'); // 测试获取用户 admin 类型 all 排序 id_asc 获取前两个 的待评审类型。
r($my->getReviewingListTest($account[0], $browseType[1], $orderBy[0], $pageList[0])) && p() && e('0'); // 测试获取用户 admin 类型 createdbyme 排序 id_desc 不分页 的待评审类型。
r($my->getReviewingListTest($account[0], $browseType[1], $orderBy[1], $pageList[0])) && p() && e('0'); // 测试获取用户 admin 类型 createdbyme 排序 id_asc 不分页 的待评审类型。
r($my->getReviewingListTest($account[0], $browseType[1], $orderBy[0], $pageList[1])) && p() && e('0'); // 测试获取用户 admin 类型 createdbyme 排序 id_desc 获取前两个 的待评审类型。
+32
View File
@@ -0,0 +1,32 @@
#!/usr/bin/env php
<?php
declare(strict_types=1);
include dirname(__FILE__, 5) . '/test/lib/init.php';
include dirname(__FILE__, 2) . '/lib/my.unittest.class.php';
zenData('mr')->loadYaml('mrreview')->gen('10');
zenData('user')->gen('5');
/**
title=测试 myModel->getReviewingStories();
timeout=0
cid=1
- 测试获取用户 account 排序 id_desc 的合并请求。
- 第0条的title属性 @Test MR9
- 第0条的id属性 @9
- 测试获取用户 account 排序 id_asc 的合并请求。
- 第0条的title属性 @Test MR3
- 第0条的id属性 @3
- 测试获取没有审批用户的数据。 @0
*/
$account = array('admin', 'user1');
$orderBy = array('id_desc', 'id_asc');
$checkExist = array(false, true);
$my = new myTest();
r($my->getReviewingMRsTest($account[0], $orderBy[0])) && p('9:title,id') && e('Test MR9,9'); // 测试获取用户 account 排序 id_desc 的合并请求。
r($my->getReviewingMRsTest($account[0], $orderBy[1])) && p('3:title,id') && e('Test MR3,3'); // 测试获取用户 account 排序 id_asc 的合并请求。
r($my->getReviewingMRsTest($account[1], $orderBy[0])) && p() && e('0'); // 测试获取没有审批用户的数据。
+16 -12
View File
@@ -10,6 +10,13 @@ zenData('case')->gen('20');
zenData('demand')->loadYaml('demand_reviewing')->gen('20');
zenData('demandreview')->gen('20');
zenData('user')->gen('10');
zenData('userview')->gen('0');
zenData('product')->gen('0');
zenData('testcase')->gen('0');
zenData('mr')->gen('0');
zenData('requirement')->gen('0');
zenData('workflow')->gen('0');
zenData('action')->gen('0');
/**
@@ -18,14 +25,10 @@ timeout=0
cid=1
- 测试获取用户 admin 的待评审类型。 @all,story,requirement,testcase
- 测试获取用户 user1 的待评审类型。 @all,testcase
- 测试获取用户 user2 的待评审类型。 @all,testcase
- 测试获取用户 user3 的待评审类型。 @all,testcase
- 测试获取用户 user4 的待评审类型。 @all,testcase
- 测试获取用户 user1 的待评审类型。 @all
- 测试获取用户 user2 的待评审类型。 @all
- 测试获取用户 user3 的待评审类型。 @all
- 测试获取用户 user4 的待评审类型。 @all
*/
@@ -34,10 +37,11 @@ $config->edition = 'open';
$my = new myTest();
su('admin');
$account = array('admin', 'user1', 'user2', 'user3', 'user4');
r($my->getReviewingTypeListTest($account[0])) && p() && e('all,story,requirement,testcase'); // 测试获取用户 admin 的待评审类型。
r($my->getReviewingTypeListTest($account[1])) && p() && e('all,testcase'); // 测试获取用户 user1 的待评审类型。
r($my->getReviewingTypeListTest($account[2])) && p() && e('all,testcase'); // 测试获取用户 user2 的待评审类型。
r($my->getReviewingTypeListTest($account[3])) && p() && e('all,testcase'); // 测试获取用户 user3 的待评审类型。
r($my->getReviewingTypeListTest($account[4])) && p() && e('all,testcase'); // 测试获取用户 user4 的待评审类型。
r($my->getReviewingTypeListTest($account[1])) && p() && e('all'); // 测试获取用户 user1 的待评审类型。
r($my->getReviewingTypeListTest($account[2])) && p() && e('all'); // 测试获取用户 user2 的待评审类型。
r($my->getReviewingTypeListTest($account[3])) && p() && e('all'); // 测试获取用户 user3 的待评审类型。
r($my->getReviewingTypeListTest($account[4])) && p() && e('all'); // 测试获取用户 user4 的待评审类型。
+40
View File
@@ -0,0 +1,40 @@
title: zt_mr
author: Yang LI
version: "1.0"
fields:
- field: id
range: 1-10
- field: hostID
range: 1
- field: sourceProject
range: 42
- field: sourceBranch
prefix: "branch-"
range: 1-10
- field: targetProject
range: 42
- field: targetBranch
range: master
- field: mriid
range: 1-10
- field: title
prefix: "Test MR"
range: 1-10
- field: status
range: opened,closed
- field: mergeStatus
range: can_be_merged
- field: approvalStatus
range: approved,rejected,[]
- field: repoID
range: 1
- field: jobID
range: 0
- field: compileID
range: 0
- field: createdBy
range: admin
- field: assignee
range: admin,user1
- field: deleted
range: 0
+14 -3
View File
@@ -28,9 +28,14 @@ foreach(array_keys($lang->my->featureBar['audit']) as $type)
$viewPrivs[$type] = $viewPriv;
}
}
$reviewPrivs['review'] = hasPriv('review', 'access');
$viewPrivs['review'] = hasPriv('review', 'view');
$viewPrivs['task'] = hasPriv('task', 'view');
$reviewPrivs['review'] = hasPriv('review', 'access');
$reviewPrivs['mr'] = hasPriv('mr', 'view');
$reviewPrivs['pullreq'] = hasPriv('pullreq', 'view');
$viewPrivs['review'] = hasPriv('review', 'view');
$viewPrivs['task'] = hasPriv('task', 'view');
$viewPrivs['mr'] = hasPriv('mr', 'view');
$viewPrivs['pullreq'] = hasPriv('pullreq', 'view');
jsVar('reviewLink', createLink('{module}', 'review', 'id={id}'));
jsVar('flowReviewLink', createLink('{module}', 'approvalreview', 'id={id}'));
@@ -103,6 +108,12 @@ foreach($reviewList as $review)
if(isset($flows[$review->type]) && $rawMethod != 'audit') $statusList = $lang->approval->statusList;
}
if($type == 'mr')
{
$this->app->loadLang('mr');
$statusList = $lang->mr->approvalStatusList;
}
$review->type = $typeName;
$review->status = zget($statusList, $review->status, '');
@@ -15,7 +15,7 @@ class editProductTester extends tester
$form->dom->editBtn->click();
if(isset($product->name)) $form->dom->name->setValue($product->name);
$form->dom->btn($this->lang->save)->click();
$form->wait(1);
$form->wait(2);
if($this->response('method') != 'view')
{
if($this->checkFormTips('product')) return $this->success('编辑产品表单提示信息正确');
+1 -1
View File
@@ -74,7 +74,7 @@ $release->gen(5);
$tester = new kanbanTester();
$tester->login();
r($tester->checkKanbanData('plan', '3')) && p('message,status') && e('未过期计划数正确,SUCCESS');//检查未过期计划数
r($tester->checkKanbanData('plan', '4')) && p('message,status') && e('未过期计划数正确,SUCCESS');//检查未过期计划数
r($tester->checkKanbanData('project', '2')) && p('message,status') && e('进行中的项目数正确,SUCCESS');//检查进行中的项目数
r($tester->checkKanbanData('execution', '1')) && p('message,status') && e('进行中的执行数正确,SUCCESS');//检查进行中的执行数
r($tester->checkKanbanData('release', '4')) && p('message,status') && e('正常发布数正确,SUCCESS');//检查正常发布数
+3 -3
View File
@@ -27,7 +27,7 @@ $fields->field('type')
$fields->field('code')->control('input');
$fields->field('PO')
->control(array('control' => 'remotepicker', 'params' => 'nodeleted|pofirst|noclosed'))
->control(array('control' => 'remotepicker', 'params' => 'nodeleted,pofirst,noclosed'))
->value(data('fields.PO.default'));
$fields->field('reviewer')
@@ -37,11 +37,11 @@ $fields->field('reviewer')
$fields->field('QD')
->wrapBefore()
->control(array('control' => 'remotepicker', 'params' => 'nodeleted|qdfirst|noclosed'))
->control(array('control' => 'remotepicker', 'params' => 'nodeleted,qdfirst,noclosed'))
->value(data('fields.QD.default'));
$fields->field('RD')
->control(array('control' => 'remotepicker', 'params' => 'nodeleted|devfirst|noclosed'))
->control(array('control' => 'remotepicker', 'params' => 'nodeleted,devfirst,noclosed'))
->value(data('fields.RD.default'));
$fields->field('desc')
@@ -69,5 +69,4 @@ $task = $tester->programplan->loadModel('task')->getById(4);
r((array)$tester->programplan->buildTaskDataForGantt($task, $dateLimit, 0)) && p('id,type,text,owner_id,start_date,parent') && e("4,task,<span class='pri-4 align-middle' title='4'>4</span> <span class='gantt_title'>开发任务14</span>,``,28-09-2023,0"); //检查构建分组Gantt数据。
$task = $tester->programplan->loadModel('task')->getById(5);
r((array)$tester->programplan->buildTaskDataForGantt($task, $dateLimit, 0)) && p('id,type,text,owner_id,start_date,parent') && e("5,task,<span class='pri-1 align-middle' title='1'>1</span> <span class='gantt_title'>开发任务15</span>,``,28-09-2023,0"); //检查构建分组Gantt数据。
r((array)$tester->programplan->buildTaskDataForGantt($task, $dateLimit, 0)) && p('id,type,text,owner_id,start_date,parent') && e("5,task,<span class='pri-1 align-middle' title='1'>1</span> <span class='gantt_title'>开发任务15</span>,``,28-09-2023,0"); //检查构建分组Gantt数据。
+3
View File
@@ -189,6 +189,9 @@ class projectModel extends model
$action = strtolower($action);
if($action == 'publishtemplate') return $project->status == 'wait' || $project->status == 'closed';
if($action == 'disabletemplate') return $project->status == 'doing';
if($action == 'close') return $project->status != 'closed';
if($action == 'group') return $project->model != 'kanban';
if($action == 'start') return $project->status == 'wait' || $project->status == 'suspended';
@@ -0,0 +1,91 @@
<?php
include dirname(__FILE__, 5) . '/test/lib/ui.php';
class editProjectLiteTester extends tester
{
/**
* 编辑项目时检查页面输入。
* Check the page input when editing the project.
*
* @param array $project
* @access public
* @return object
*/
public function checkInput($project = array())
{
$form = $this->loadPage('project', 'browse');
$this->switchVision('lite');
$form = $this->initForm('project', 'edit', array('projecID' => 1), 'appIframe-project');
if(isset($project['name'])) $form->dom->name->setValue($project['name']);
if(isset($project['begin'])) $form->dom->begin->datePicker($project['begin']);
if(isset($project['end'])) $form->dom->end->datePicker($project['end']);
if(isset($project['PM'])) $form->dom->PM->picker($project['PM']);
$form->dom->btn($this->lang->save)->click();
$form->wait(2);
return $this->checkResult($project);
}
/**
* 编辑项目后结果检查。
* Check the result after editing the project.
*
* @param array $project
* @access public
* @return object
*/
public function checkResult($project = array())
{
/* 检查编辑页面提示信息 */
$form = $this->loadPage('project', 'edit');
if($this->response('method') != 'view')
{
/* 检查项目名称不能为空 */
if($form->dom->nameTip && $project['name'] == '')
{
$nameTipText = $form->dom->nameTip->getText();
$nameTip = sprintf($this->lang->error->notempty, $this->lang->project->name);
return ($nameTipText == $nameTip) ? $this->success('编辑项目表单页提示信息正确') : $this->failed('编辑项目表单页提示信息不正确');
}
/* 检查项目名称唯一 */
if($form->dom->nameTip && $project['name'] != '')
{
$existName = '运营界面项目2';
$nameTipText = $form->dom->nameTip->getText();
$nameTip = sprintf($this->lang->error->unique, $this->lang->project->name, $existName);
return ($nameTipText == $nameTip) ? $this->success('编辑项目表单页提示信息正确') : $this->failed('编辑项目表单页提示信息不正确');
}
if($form->dom->endTip)
{
/* 检查结束日期不能为空 */
if($project['end'] == '')
{
$endTipText = $form->dom->endTip->getText();
$endTip = sprintf($this->lang->copyProject->endTip, '');
return ($endTipText == $endTip) ? $this->success('编辑项目表单页提示信息正确') : $this->failed('编辑项目表单页提示信息不正确');
}
/* 检查结束日期不能小于开始日期 */
if($project['begin'] > $project['end'])
{
$endTipText = $form->dom->endTip->getText();
$endTip = sprintf($this->lang->error->gt, $this->lang->project->end, $project['begin']);
return ($endTipText == $endTip) ? $this->success('编辑项目表单页提示信息正确') : $this->failed('编辑项目表单页提示信息不正确');
}
}
}
/* 跳转到项目列表页面,按照项目名称进行搜索 */
$browsePage = $this->initForm('project', 'browse');
$browsePage->dom->search($searchList = array("项目名称,包含,{$project['name']}"));
$browsePage->wait(2);
$browsePage->dom->projectNameLite->click();
/* 进入项目概况页面 */
$browsePage->dom->settings->click();
$viewPage = $this->loadPage('project', 'view');
$viewPage->wait(2);
/* 断言检查字段信息是否正确 */
if($viewPage->dom->projectNameLite->getText() != $project['name']) return $this->failed('名称错误');
if($viewPage->dom->aclLite->getText() != $this->lang->project->shortAclList->open) return $this->failed('权限错误');
return $this->success('编辑项目成功');
}
}
@@ -0,0 +1,24 @@
<?php
include dirname(__FILE__, 5) . '/test/lib/ui.php';
class teamTesterForLite extends tester
{
/**
* Check remove members for lite.
* 运营界面移除项目团队成员
*
* @access public
*/
public function removeMembers()
{
$this->switchVision('lite');
$form = $this->initForm('project', 'team', array('projectID' => '1'), 'appIframe-project');
$browseFirAccount1 = $form->dom->browseFirAccount->getText();
$form->dom->unlinkBtn->click();
$form->dom->confirmBtn->click();
$form->wait(2);
//添加断言,判断是否移除团队成员成功
if($form->dom->browseFirAccount->getText() == $browseFirAccount1) return $this->failed('项目团队成员移除失败');
return $this->success('项目团队成员移除成功');
}
}

Some files were not shown because too many files have changed in this diff Show More