225 lines
8.8 KiB
PHP
225 lines
8.8 KiB
PHP
<?php
|
||
require_once dirname(__FILE__, 6) . '/test/lib/ui.php';
|
||
|
||
define('STORY_MENUS', array('assignedTo','openedBy','reviewedBy','closedBy'));
|
||
define('STORY_COLUMNS', array('id','title','pri','status','productTitle','planTitle','openedByName','estimate','stage'));
|
||
define('PAGESIZE', array(5,10,15,20,25,30,35,40,45,50,100,200,500,1000,2000));
|
||
|
||
class storyTester extends tester
|
||
{
|
||
/**
|
||
* 验证m=user&f=story的各个子菜单内容和分页功能
|
||
* Verify the content and pagination of each sub-menu in m=user&f=story.
|
||
*
|
||
* @param array $users
|
||
* @param array $stories
|
||
* @param string $storyType story|requirement
|
||
* @param int $pageSize
|
||
* @return object 成功或失败对象
|
||
*/
|
||
public function verifyUserStoryMenus($users = array(), $stories = array(), $storyType = 'story', $pageSize = 5)
|
||
{
|
||
if(empty($users)) return $this->failed('用户列表不能为空');
|
||
if(empty($stories)) return $this->failed('需求列表不能为空');
|
||
if(!in_array($pageSize, PAGESIZE, true)) return $this->failed('pageSize无效');
|
||
|
||
$this->login();
|
||
$form = $this->initForm('user', 'story', array('userID' => $users[0]->id, 'storyType' => $storyType), 'appIframe-system');
|
||
$form->wait(2);
|
||
|
||
foreach($users as $user)
|
||
{
|
||
if($user != $users[0])
|
||
{
|
||
$form->dom->userPicker->picker($user->realname);
|
||
$form->wait(1);
|
||
if($form->dom->userPicker->getText() != $user->realname) return $this->failed('task页面切换用户显示失败');
|
||
}
|
||
|
||
foreach(STORY_MENUS as $menu)
|
||
{
|
||
$form->dom->{$menu}->click();
|
||
$form->wait(1);
|
||
$ret = $this->verifyUserStoryContentAndPagination($form, $user, $stories, $storyType, $menu, $pageSize);
|
||
if($ret) return $ret;
|
||
}
|
||
}
|
||
|
||
return $this->success("开源版m=user&f=story&storyType={$storyType}测试成功");
|
||
}
|
||
|
||
/**
|
||
* 检查具体子菜单的内容以及分页功能
|
||
* Verify the content and pagination of a specific sub-menu in m=user&f=story.
|
||
*
|
||
* @param object $form 页面对象
|
||
* @param array $users 用户列表
|
||
* @param array $stories 研发/用户需求数据
|
||
* @param string $storyType story|requirement
|
||
* @param string $menu assignedTo|openedBy|reviewedBy|closedBy
|
||
* @param int $pageSize 每页大小
|
||
* @return object 成功返回null,失败返回对象
|
||
*/
|
||
public function verifyUserStoryContentAndPagination($form, $user, $stories = array(), $storyType = 'story', $menu = 'assignedTo', $pageSize = 5)
|
||
{
|
||
$userStories = $this->filterStoriesByMenu($stories, $user, $menu, $storyType);
|
||
|
||
$currentPageSize = (int) filter_var($form->dom->pagerSizeMenu->getText(), FILTER_SANITIZE_NUMBER_INT);
|
||
|
||
if(count($userStories) > $currentPageSize)
|
||
{
|
||
$form->dom->pagerSizeMenu->click();
|
||
$form->wait(1);
|
||
$form->dom->dropdownPicker(max(PAGESIZE));
|
||
$form->wait(1);
|
||
}
|
||
|
||
$ret = $this->verifyStoryColumns($form, $userStories, $storyType);
|
||
if($ret) return $ret;
|
||
|
||
$ret = $this->verifyStoryPagination($form, $userStories, $pageSize);
|
||
if($ret) return $ret;
|
||
|
||
return null;
|
||
}
|
||
|
||
/**
|
||
* 根据菜单过滤数据
|
||
* Filter data based on menu
|
||
*
|
||
* @param array $stories 需求数据
|
||
* @param object $user 用户对象
|
||
* @param string $menu 菜单
|
||
* @param string $storyType story|requirement
|
||
* @return array 过滤后的需求列表
|
||
*/
|
||
private function filterStoriesByMenu($stories, $user, $menu)
|
||
{
|
||
$account = is_object($user) ? ($user->account ?? '') : ($user['account'] ?? '');
|
||
$filtered = array();
|
||
|
||
foreach($stories as $story)
|
||
{
|
||
$sid = is_object($story) ? ($story->id ?? null) : ($story['id'] ?? null);
|
||
$status = is_object($story) ? ($story->status ?? '') : ($story['status'] ?? '');
|
||
|
||
$val = is_object($story) ? ($story->{$menu} ?? '') : ($story[$menu] ?? '');
|
||
if($val === $account)
|
||
{
|
||
if($menu != 'closedBy') $filtered[$sid] = $story;
|
||
else if($status === 'closed') $filtered[$sid] = $story;
|
||
}
|
||
}
|
||
return $filtered;
|
||
}
|
||
|
||
/**
|
||
* 验证列值
|
||
* Verify the values in columns
|
||
*
|
||
* @param object $form 页面对象
|
||
* @param array $stories 需求数据
|
||
* @param string $storyType story|requirement
|
||
* @return object 成功返回null,失败返回对象
|
||
*/
|
||
private function verifyStoryColumns($form, $stories, $storyType = 'story')
|
||
{
|
||
// Lite 版本或 requirement 类型会隐藏 plan;按是否有数据来判断并跳过对齐验证。
|
||
$columns = STORY_COLUMNS;
|
||
if($storyType === 'requirement' || ($this->config->vision ?? '') === 'lite') $columns = array_diff($columns, array('planTitle'));
|
||
if(($this->config->vision ?? '') === 'lite') $columns = array_diff($columns, array('stage'));
|
||
|
||
$display = array();
|
||
foreach($columns as $col) $display[$col] = $form->dom->getElementListByXpathKey($col, true);
|
||
|
||
if(count($display['id']) != count($stories)) return $this->failed('story页面数据数量与期望不一致');
|
||
|
||
foreach($display['id'] as $i => $idText)
|
||
{
|
||
$id = (int)trim($idText);
|
||
if(!isset($stories[$id])) return $this->failed("story{$id}不在当前用户视图范围内");
|
||
$s = $stories[$id];
|
||
|
||
// 先检查title
|
||
$itemTitle = $display['title'][$i] ?? '';
|
||
$itemTitle = str_replace("\xC2\xA0", ' ', $itemTitle);
|
||
$itemTitle = preg_replace('/\s+/u', ' ', trim($itemTitle));
|
||
// 名称类字段:页面可能在标题前显示“子”(childrenAB)或“SR/UR/undefined/UR”等前缀,进行规范化后再比较
|
||
foreach(array('SR', 'UR', 'undefined', 'UR', $this->lang->story->childrenAB) as $prefix)
|
||
{
|
||
if(str_starts_with($itemTitle, $prefix))
|
||
{
|
||
$itemTitle = trim(substr($itemTitle, strlen($prefix)));
|
||
break;
|
||
}
|
||
}
|
||
$expectTitle = str_replace("\xC2\xA0", ' ', $s->title);
|
||
$expectTitle = preg_replace('/\s+/u', ' ', trim($expectTitle));
|
||
if($itemTitle !== $expectTitle) return $this->failed("ID={$id}名称不匹配");
|
||
|
||
foreach($columns as $c)
|
||
{
|
||
switch ($c)
|
||
{
|
||
case 'id':
|
||
case 'title':
|
||
continue 2; // 名称在上面已单独验证
|
||
case 'status':
|
||
case 'stage':
|
||
// 根据语言获取对应文本
|
||
$expected = $this->lang->story->{$c . 'List'}->{$s->{$c}} ?? $s->{$c};
|
||
break;
|
||
case 'estimate':
|
||
// 根据语言添加后缀
|
||
$expected = $s->{$c} . $this->lang->task->suffixHour;
|
||
break;
|
||
default:
|
||
$expected = $s->{$c};
|
||
break;
|
||
}
|
||
if($display[$c][$i] != $expected) return $this->failed("ID={$id} {$c}不匹配,期望'{$expected}',实际'{$display[$c][$i]}'");
|
||
}
|
||
}
|
||
return null;
|
||
}
|
||
|
||
/**
|
||
* 验证分页
|
||
* verify pagination
|
||
*
|
||
* @param object $form 页面对象
|
||
* @param array $items 需求数据
|
||
* @param int $pageSize 每页数量
|
||
* @return object 成功返回null,失败返回对象
|
||
*/
|
||
private function verifyStoryPagination($form, $items, $pageSize = 5)
|
||
{
|
||
$totalTasks = count($items);
|
||
$pages = (int) ceil($totalTasks / $pageSize);
|
||
|
||
if(count($items) > $pageSize)
|
||
{
|
||
$form->dom->pagerSizeMenu->click();
|
||
$form->wait(1);
|
||
$form->dom->dropdownPicker($pageSize);
|
||
$form->dom->firstPage->click();
|
||
$form->wait(1);
|
||
}
|
||
|
||
for($i = 1; $i <= $pages; $i++)
|
||
{
|
||
$totalActual = count($form->dom->getElementListByXpathKey('id' ));
|
||
$totalExpected = ($pages === 1) ? $totalTasks : ($i < $pages ? $pageSize : ($totalTasks - $pageSize * ($pages - 1)));
|
||
|
||
if($totalActual !== $totalExpected) return $this->failed("分页第{$i}页数量不匹配:期望{$totalExpected},实际{$totalActual}");
|
||
|
||
if($i < $pages && $form->dom->nextPage)
|
||
{
|
||
$form->dom->nextPage->click();
|
||
$form->wait(1);
|
||
}
|
||
}
|
||
return null;
|
||
}
|
||
}
|