From 594ed07def32cb560143684cac25c3a0da4f0415 Mon Sep 17 00:00:00 2001 From: wanshan Date: Tue, 4 Nov 2025 09:02:24 +0800 Subject: [PATCH] + [task#151941,doing,4h] add ui test for user module execution --- .../user/test/lib/ui/execution.ui.class.php | 131 ++++++++++++++++++ module/user/test/ui/execution.php | 93 +++++++++++++ module/user/test/ui/page/execution.php | 33 +++++ 3 files changed, 257 insertions(+) create mode 100644 module/user/test/lib/ui/execution.ui.class.php create mode 100755 module/user/test/ui/execution.php create mode 100644 module/user/test/ui/page/execution.php diff --git a/module/user/test/lib/ui/execution.ui.class.php b/module/user/test/lib/ui/execution.ui.class.php new file mode 100644 index 0000000000..f5dc5267ac --- /dev/null +++ b/module/user/test/lib/ui/execution.ui.class.php @@ -0,0 +1,131 @@ +failed('用户列表不能为空'); + if(empty($executions)) return $this->failed('execution数据不能为空'); + if(!in_array($pageSize, PAGESIZE, true)) return $this->failed('pageSize无效'); + + $this->login(); + $form = $this->initForm('user', 'execution', 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(2); + if($form->dom->userPicker->getText() != $user->realname) return $this->failed('execution页面切换用户显示失败'); + } + + $userExecutions = isset($executions[$user->account]) ? $executions[$user->account] : array(); + + $ret = $this->verifyContent($form, $user, $userExecutions); + if($ret) return $ret; + + $items = $form->dom->getElementListByXpathKey('id'); + $ret = $this->verifyPagination($form, $items, $pageSize); + if($ret) return $ret; + } + + return $this->success("开源版m=user&f=execution测试成功"); + } + + /** + * 验证内容 + * verify content + * + * @param object $form 页面对象 + * @param object $user 用户对象 + * @param array $executions 过滤后的execution数据 + * @return object 成功返回null,失败返回对象 + */ + private function verifyContent($form, $user, $executions) + { + $currentPageSize = (int) filter_var($form->dom->pagerSizeMenu->getText(), FILTER_SANITIZE_NUMBER_INT); + + if(count($executions) > $currentPageSize) + { + $form->dom->pagerSizeMenu->click(); + $form->wait(1); + $form->dom->dropdownPicker(max(PAGESIZE)); + $form->wait(1); + } + + $display = array(); + foreach(EXECUTION_COLUMNS as $c) $display[$c] = $form->dom->getElementListByXpathKey($c, true); + + if(count($display['id']) != count($executions)) return $this->failed("用户'{$user->realname}'的执行数量不符: 期望" . count($executions) . "条,实际" . count($display['id']) . "条"); + + foreach($display['id'] as $i => $idText) + { + $id = (int)trim($idText); + if(!isset($executions[$id])) return $this->failed("用户'{$user->realname}'执行页面显示了不应该显示的ID: {$id}"); + $execution = $executions[$id]; + foreach(EXECUTION_COLUMNS as $column) + { + $displayValue = $display[$column][$i] ?? ''; + if($column == 'status') $expected = $this->lang->program->statusList->{$execution->status} ?? $execution->status; + else $expected = $execution->{$column} ?? ''; + if($displayValue != $expected) return $this->failed("用户'{$user->realname}'执行页面ID={$id} {$column}不匹配,期望'{$expected}',实际'{$displayValue}'"); + } + } + + return null; + } + + /** + * 验证分页 + * verify pagination + * + * @param object $form 页面对象 + * @param array $items 需求数据 + * @param int $pageSize 每页数量 + * @return object 成功返回null,失败返回对象 + */ + private function verifyPagination($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; + } +} diff --git a/module/user/test/ui/execution.php b/module/user/test/ui/execution.php new file mode 100755 index 0000000000..ef09e051f6 --- /dev/null +++ b/module/user/test/ui/execution.php @@ -0,0 +1,93 @@ +#!/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); + +$project = zenData('project'); +$project->id->range('1-2'); +$project->name->range('项目1,项目2'); +$project->model->range('scrum'); +$project->status->range('doing'); +$project->type->range('project'); +$project->gen(2); + +$execution = zenData('project'); +$execution->id->range('101-106'); +$execution->project->range('1{3},2{3}'); +$execution->type->range('sprint'); +$execution->name->range('101-106')->prefix('执行'); +$execution->status->range('wait,doing,suspended,closed'); +$execution->deleted->range('0'); +$execution->multiple->range('1'); +$execution->vision->range('rnd'); +$execution->gen(6, false); + +$team = zenData('team'); +$team->id->range('1-18'); +$team->root->range('101-106{3}'); +$team->type->range('execution'); +$team->account->range('admin,user1,user2'); +$team->role->range('项目经理,开发,测试'); +$team->join->range('`2023-01-01`,`2023-01-15`,`2023-02-01`'); +$team->hours->range('8,6,4'); +$team->gen(18); + +$task = zenData('task'); +$task->id->range('1-18'); +$task->execution->range('101-106{3}'); +$task->name->range('1-18')->prefix('任务'); +$task->assignedTo->range('admin{6},user1{6},user2{6}'); +$task->status->range('wait,doing,done'); +$task->deleted->range('0'); +$task->gen(18); + +global $uiTester; +$users = $uiTester->dao->select('*')->from('zt_user')->fetchAll(); + +// 按用户保存execution数据,包含team信息 +$executions = array(); +foreach($users as $user) +{ + $userExecutions = $uiTester->dao->select("e.*, t.account, t.role, t.join, t.hours") + ->from('zt_project')->alias('e') + ->leftJoin('zt_team')->alias('t')->on('e.id = t.root AND t.type = "execution"') + ->where('e.type')->eq('sprint') + ->andWhere('e.deleted')->eq('0') + ->andWhere('t.account')->eq($user->account) + ->fetchAll('id'); + + $executions[$user->account] = $userExecutions; +} + +$tester = new executionTester(); + +r($tester->verifyUserExecutionContentAndPagination($users, $executions, 5)) && p('status,message') && e('SUCCESS,开源版m=user&f=execution测试成功'); //开源版m=user&f=execution测试 + +$tester->closeBrowser(); diff --git a/module/user/test/ui/page/execution.php b/module/user/test/ui/page/execution.php new file mode 100644 index 0000000000..e835314ee6 --- /dev/null +++ b/module/user/test/ui/page/execution.php @@ -0,0 +1,33 @@ +xpath = array( + // 顶部用户选择器 + 'userPicker' => '//div[contains(@class, "picker-select-single")]', + + // 列选择器(与 dtable 的 data-col 对齐) + 'id' => "//div[@data-col='id' and @data-row!='HEADER']", + 'name' => "//div[@data-col='name' and @data-row!='HEADER']", + 'status' => "//div[@data-col='status' and @data-row!='HEADER']", + 'role' => "//div[@data-col='role' and @data-row!='HEADER']", + 'begin' => "//div[@data-col='begin' and @data-row!='HEADER']", + 'end' => "//div[@data-col='end' and @data-row!='HEADER']", + 'join' => "//div[@data-col='join' and @data-row!='HEADER']", + 'hours' => "//div[@data-col='hours' 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-execution']/div[3]/nav/a[1]", + 'prevPage' => "//*[@id='table-user-execution']/div[3]/nav/a[2]", + 'nextPage' => "//*[@id='table-user-execution']/div[3]/nav/a[3]", + 'lastPage' => "//*[@id='table-user-execution']/div[3]/nav/a[4]" + ); + + $this->dom->xpath = array_merge($this->dom->xpath, $this->xpath); + } +} \ No newline at end of file