diff --git a/module/user/test/lib/ui/bug.ui.class.php b/module/user/test/lib/ui/bug.ui.class.php new file mode 100644 index 0000000000..c7906e4e46 --- /dev/null +++ b/module/user/test/lib/ui/bug.ui.class.php @@ -0,0 +1,210 @@ +failed('用户列表不能为空'); + if(empty($bugs)) return $this->failed('Bug列表不能为空'); + if(!in_array($pageSize, PAGESIZE, true)) return $this->failed('pageSize无效'); + + $this->login(); + $form = $this->initForm('user', 'bug', array('userID' => $users[0]->id), '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('bug页面切换用户显示失败'); + } + + foreach(BUG_MENUS as $menu) + { + $form->dom->{$menu}->click(); + $form->wait(1); + $ret = $this->verifyUserBugContentAndPagination($form, $user, $bugs, $menu, $pageSize); + if($ret) return $ret; + } + } + + return $this->success('开源版m=user&f=bug测试成功'); + } + + /** + * 检查具体子菜单的内容以及分页功能 + * Verify the content and pagination of a specific sub-menu in m=user&f=bug. + * + * @param object $form 表单对象 + * @param object $user 用户对象 + * @param array $bugs Bug数据 + * @param string $menu 子菜单名称 + * @param int $pageSize 每页大小 + * @return object 成功返回null,失败返回对象 + */ + private function verifyUserBugContentAndPagination($form, $user, $bugs = array(), $menu = 'assignedTo', $pageSize = 5) + { + $userBugs = $this->filterBugsByMenu($bugs, $user, $menu); + if(!count($userBugs)) return null; + $currentPageSize = (int) filter_var($form->dom->pagerSizeMenu->getText(), FILTER_SANITIZE_NUMBER_INT); + if(count($userBugs) > $currentPageSize) + { + $form->dom->pagerSizeMenu->click(); + $form->wait(1); + $form->dom->dropdownPicker(max(PAGESIZE)); + $form->wait(1); + } + + $ret = $this->verifyBugColumns($form, $userBugs); + if($ret) return $ret; + + $ret = $this->verifyBugPagination($form, $userBugs, $pageSize); + if($ret) return $ret; + + return null; + } + + /** + * 根据菜单过滤数据 + * Filter data based on menu + * @param array $bugs Bug数据 + * @param object $user 用户对象 + * @param string $menu 子菜单名称 + * @return array 过滤后的Bug数据 + */ + private function filterBugsByMenu($bugs, $user, $menu) + { + $account = is_object($user) ? ($user->account ?? '') : ($user['account'] ?? ''); + $filtered = array(); + + foreach($bugs as $bug) + { + $bid = is_object($bug) ? ($bug->id ?? null) : ($bug['id'] ?? null); + $status = is_object($bug) ? ($bug->status ?? '') : ($bug['status'] ?? ''); + $val = is_object($bug) ? ($bug->{$menu} ?? '') : ($bug[$menu] ?? ''); + if($val === $account) + { + if($menu != 'closedBy') $filtered[$bid] = $bug; + else if($status === 'closed') $filtered[$bid] = $bug; + } + } + return $filtered; + } + + /** + * 验证列值 + * Verify the values in bug columns + * + * @param object $form 表单对象 + * @param array $bugs Bug数据 + * @return object 成功返回null,失败返回对象 + */ + private function verifyBugColumns($form, $bugs) + { + $columns = BUG_COLUMNS; + + $display = array(); + foreach($columns as $col) + { + if($col != 'severity') $display[$col] = $form->dom->getElementListByXpathKey($col, true); + else $display[$col] = $form->dom->getElementListByXpathKey($col); + } + + if(count($display['id']) != count($bugs)) return $this->failed('bug页面数据数量与期望不一致'); + + foreach($display['id'] as $i => $id) + { + if(!isset($bugs[$id])) return $this->failed("bug{$id}不在当前用户视图范围内"); + $b = $bugs[$id]; + + foreach($columns as $c) + { + // Severity 列的值在 data-severity 属性中而不是在text里 + if($c == 'severity') $actual = $display[$c][$i]->getAttribute('data-severity'); + else $actual = $display[$c][$i]; + + switch ($c) + { + case 'severity': + $expected = $this->lang->bug->severityList->{$b->severity} ?? $b->severity; + break; + case 'pri': + $expected = (string)$b->pri; // pri 列显示为数字徽标 + break; + case 'type': + $expected = $this->lang->bug->typeList->{$b->type} ?? $b->type; + break; + case 'openedByName': + case 'resolvedByName': + // 页面使用 userMap 渲染真实姓名,测试数据中已提供 realname + $key = $c === 'openedByName' ? 'openedBy' : 'resolvedBy'; + $expected = $b->{$key . 'Name'} ?? $b->{$key}; + break; + case 'resolution': + $expected = $this->lang->bug->resolutionList->{$b->resolution} ?? $b->resolution; + break; + default: + $expected = $b->{$c}; + break; + } + + if($actual != $expected) return $this->failed("ID={$id} {$c}不匹配,期望'{$expected}',实际'{$actual}'"); + } + } + return null; + } + + /** + * 验证分页 + * Verify the pagination of bug items + * + * @param object $form 表单对象 + * @param array $items 项目数据 + * @param int $pageSize 每页项目数量 + * @return object 成功返回null,失败返回对象 + */ + private function verifyBugPagination($form, $items, $pageSize = 5) + { + $total = count($items); + $pages = (int) ceil($total / $pageSize); + + if($total > $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++) + { + $actual = count($form->dom->getElementListByXpathKey('id')); + $expected = ($pages === 1) ? $total : ($i < $pages ? $pageSize : ($total - $pageSize * ($pages - 1))); + if($actual !== $expected) return $this->failed("分页第{$i}页数量不匹配:期望{$expected},实际{$actual}"); + + if($i < $pages && $form->dom->nextPage) + { + $form->dom->nextPage->click(); + $form->wait(1); + } + } + return null; + } +} diff --git a/module/user/test/ui/bug.php b/module/user/test/ui/bug.php new file mode 100644 index 0000000000..2040eae82e --- /dev/null +++ b/module/user/test/ui/bug.php @@ -0,0 +1,62 @@ +#!/usr/bin/env php +id->range('1-3'); +$user->account->range('admin,user1,user2'); +$user->realname->range('管理员,用户1,用户2'); +$user->password->range($config->uitest->defaultPassword)->format('md5'); +$user->role->range('admin,dev,qa'); +$user->gender->range('f,m'); +$user->gen(3); + +$product = zenData('product'); +$product->id->range('1'); +$product->name->range('产品1'); +$product->type->range('normal'); +$product->status->range('normal'); +$product->gen(1); + +$bug = zenData('bug'); +$bug->id->range('1-18'); +$bug->product->range('1'); +$bug->title->range('1-18')->prefix('BUG'); +$bug->severity->range('1,2,3,4'); +$bug->pri->range('1,2,3,4'); +$bug->type->range('codeerror,install,performance,others'); +$bug->assignedTo->range('admin,user1,user2'); +$bug->openedBy->range('admin,user1,user2'); +$bug->resolvedBy->range('admin,user1,user2'); +$bug->closedBy->range('admin,user1,user2'); +$bug->resolution->range('fixed,duplicate,bydesign,'); +$bug->status->range('active,resolved,closed'); +$bug->gen(18); + +global $uiTester; +$users = $uiTester->dao->select('*')->from('zt_user')->fetchAll(); + +// 补充 openedByName/resolvedByName 字段用于比对(页面使用 userMap 渲染真实姓名) +$bugs = $uiTester->dao->select("b.*, u1.realname AS openedByName, u2.realname AS resolvedByName") + ->from('zt_bug')->alias('b') + ->leftJoin('zt_user')->alias('u1')->on('b.openedBy = u1.account') + ->leftJoin('zt_user')->alias('u2')->on('b.resolvedBy = u2.account') + ->fetchAll('id'); + +$tester = new bugTester(); + +r($tester->verifyUserBugMenus($users, $bugs, 5)) && p('status,message') && e('SUCCESS,开源版m=user&f=bug测试成功'); //开源版m=user&f=bug测试 + +$tester->closeBrowser(); \ No newline at end of file diff --git a/module/user/test/ui/page/bug.php b/module/user/test/ui/page/bug.php new file mode 100644 index 0000000000..50d61547b8 --- /dev/null +++ b/module/user/test/ui/page/bug.php @@ -0,0 +1,36 @@ +xpath = array( + // 顶部用户选择器 + 'userPicker' => '//div[contains(@class, "picker-select-single")]', + // 顶部子导航(AssignedTo/OpenedBy/ResolvedBy/ClosedBy) + 'assignedTo' => "//*[@id='mainContent']/div/menu/li[1]", + 'openedBy' => "//*[@id='mainContent']/div/menu/li[2]", + 'resolvedBy' => "//*[@id='mainContent']/div/menu/li[3]", + 'closedBy' => "//*[@id='mainContent']/div/menu/li[4]", + // 列选择器(与 dtable 的 data-col 对齐) + 'id' => "//div[@data-col='id' and @data-row!='HEADER']", + 'title' => "//div[@data-col='title' and @data-row!='HEADER']", + 'severity' => "//div[@data-col='severity' and @data-row!='HEADER']//span", + 'pri' => "//div[@data-col='pri' and @data-row!='HEADER']", + 'type' => "//div[@data-col='type' and @data-row!='HEADER']", + 'openedByName' => "//div[@data-col='openedBy' and @data-row!='HEADER']", + 'resolvedByName' => "//div[@data-col='resolvedBy' and @data-row!='HEADER']", + 'resolution' => "//div[@data-col='resolution' and @data-row!='HEADER']", + // 分页控件(table id 按模块约定) + 'pagerSizeMenu' => '//button[contains(@class, "pager-size-menu")]', + 'sizePerPage' => '//*[@class="menu-wrapper popup search-menu is-contextmenu"]', + 'firstPage' => '//*[@id="table-user-bug"]/div[3]/nav/a[1]', + 'prevPage' => '//*[@id="table-user-bug"]/div[3]/nav/a[2]', + 'nextPage' => '//*[@id="table-user-bug"]/div[3]/nav/a[3]', + 'lastPage' => '//*[@id="table-user-bug"]/div[3]/nav/a[4]' + ); + + $this->dom->xpath = array_merge($this->dom->xpath, $this->xpath); + } +} \ No newline at end of file