Commit 2900cc7caa1247b6e14f78879c9714ff4c048c8c

Authored by zichun
1 parent cd493340

test(usr): 用户列表 E2E 关键旅程 REQ-USR-003

frontend/tests/e2e/userlist.spec.ts 0 → 100644
  1 +import { test, expect, type Page } from '@playwright/test';
  2 +
  3 +// 桩用户列表响应工厂
  4 +function usersBody(records: unknown[], total: number, pageNum = 1, pageSize = 10) {
  5 + return JSON.stringify({
  6 + code: 0,
  7 + message: 'success',
  8 + data: { records, total, pageNum, pageSize },
  9 + });
  10 +}
  11 +
  12 +function makeUser(id: number, name: string, over: Record<string, unknown> = {}) {
  13 + return {
  14 + id,
  15 + sUserName: name,
  16 + 员工名: null,
  17 + sUserNo: `U00${id}`,
  18 + 部门: '技术部',
  19 + sUserType: '普通用户',
  20 + sLanguage: '中文',
  21 + iIsVoid: 0,
  22 + tLastLoginDate: null,
  23 + sCreator: 'admin',
  24 + tCreateDate: '2024-01-01T00:00:00',
  25 + ...over,
  26 + };
  27 +}
  28 +
  29 +// 桩登录 / 版本下拉(沿用 shell.spec.ts 模式)。用户列表由各用例单独桩。
  30 +async function stubAuth(page: Page) {
  31 + await page.route('**/api/usr/companies', async (route) => {
  32 + await route.fulfill({
  33 + status: 200,
  34 + contentType: 'application/json',
  35 + body: JSON.stringify({
  36 + code: 0,
  37 + message: 'success',
  38 + data: [{ id: 1, sCompanyName: '甲公司', sVersion: '标准版' }],
  39 + }),
  40 + });
  41 + });
  42 + await page.route('**/api/usr/login', async (route) => {
  43 + await route.fulfill({
  44 + status: 200,
  45 + contentType: 'application/json',
  46 + body: JSON.stringify({
  47 + code: 0,
  48 + message: 'success',
  49 + data: {
  50 + token: 'tk-e2e',
  51 + user: { id: 1, sUserName: '朱子纯', sUserType: '超级管理员', sLanguage: '中文' },
  52 + },
  53 + }),
  54 + });
  55 + });
  56 +}
  57 +
  58 +async function login(page: Page) {
  59 + await page.goto('/login');
  60 + await page.getByPlaceholder('请输入你的用户名').fill('admin');
  61 + await page.getByPlaceholder('请输入你的密码').fill('secret');
  62 + await expect(page.getByText('甲公司(标准版)')).toBeVisible();
  63 + await page.getByRole('button', { name: /登\s*录/ }).click();
  64 + await expect(page).toHaveURL(/\/$/);
  65 +}
  66 +
  67 +async function gotoUserList(page: Page) {
  68 + await page.getByRole('button', { name: '用户列表' }).click();
  69 + await expect(page).toHaveURL(/\/usr\/users$/);
  70 +}
  71 +
  72 +test.describe('用户列表关键旅程', () => {
  73 + test('enter user list renders rows', async ({ page }) => {
  74 + await stubAuth(page);
  75 + await page.route('**/api/usr/users**', async (route) => {
  76 + await route.fulfill({
  77 + status: 200,
  78 + contentType: 'application/json',
  79 + body: usersBody([makeUser(1, '李雷'), makeUser(2, '韩梅梅')], 2),
  80 + });
  81 + });
  82 + await login(page);
  83 + await gotoUserList(page);
  84 + await expect(page.getByText('李雷')).toBeVisible();
  85 + await expect(page.getByText('共 2 条记录')).toBeVisible();
  86 + });
  87 +
  88 + test('empty result shows 暂无匹配的用户', async ({ page }) => {
  89 + await stubAuth(page);
  90 + await page.route('**/api/usr/users**', async (route) => {
  91 + await route.fulfill({
  92 + status: 200,
  93 + contentType: 'application/json',
  94 + body: usersBody([], 0),
  95 + });
  96 + });
  97 + await login(page);
  98 + await gotoUserList(page);
  99 + await expect(page.getByText('暂无匹配的用户')).toBeVisible();
  100 + });
  101 +
  102 + test('search by value triggers query', async ({ page }) => {
  103 + await stubAuth(page);
  104 + await page.route('**/api/usr/users**', async (route) => {
  105 + await route.fulfill({
  106 + status: 200,
  107 + contentType: 'application/json',
  108 + body: usersBody([makeUser(3, '王搜索')], 1),
  109 + });
  110 + });
  111 + await login(page);
  112 + await gotoUserList(page);
  113 + const input = page.getByTestId('filter-query-value').locator('input');
  114 + await input.fill('王');
  115 + const reqPromise = page.waitForRequest((req) =>
  116 + req.url().includes('/api/usr/users') && req.url().includes('queryValue'),
  117 + );
  118 + await page.getByTestId('btn-search').click();
  119 + const req = await reqPromise;
  120 + expect(decodeURIComponent(req.url())).toContain('queryValue=王');
  121 + await expect(page.getByText('王搜索')).toBeVisible();
  122 + });
  123 +
  124 + test('pagination next page refetches with pageNum=2', async ({ page }) => {
  125 + await stubAuth(page);
  126 + await page.route('**/api/usr/users**', async (route) => {
  127 + const url = route.request().url();
  128 + const pageNum = url.includes('pageNum=2') ? 2 : 1;
  129 + await route.fulfill({
  130 + status: 200,
  131 + contentType: 'application/json',
  132 + body: usersBody([makeUser(pageNum, `行${pageNum}`)], 30, pageNum, 10),
  133 + });
  134 + });
  135 + await login(page);
  136 + await gotoUserList(page);
  137 + await expect(page.getByText('行1')).toBeVisible();
  138 + const reqPromise = page.waitForRequest((req) =>
  139 + req.url().includes('/api/usr/users') && req.url().includes('pageNum=2'),
  140 + );
  141 + await page.getByTitle('下一页').click();
  142 + await reqPromise;
  143 + await expect(page.getByText('行2')).toBeVisible();
  144 + });
  145 +
  146 + test('double click row navigates to user detail', async ({ page }) => {
  147 + await stubAuth(page);
  148 + await page.route('**/api/usr/users**', async (route) => {
  149 + await route.fulfill({
  150 + status: 200,
  151 + contentType: 'application/json',
  152 + body: usersBody([makeUser(88, '详情用户')], 1),
  153 + });
  154 + });
  155 + await login(page);
  156 + await gotoUserList(page);
  157 + await page.getByText('详情用户').dblclick();
  158 + await expect(page).toHaveURL(/\/usr\/users\/88$/);
  159 + });
  160 +
  161 + test('error response shows retry then recovers', async ({ page }) => {
  162 + await stubAuth(page);
  163 + let fail = true;
  164 + await page.route('**/api/usr/users**', async (route) => {
  165 + if (fail) {
  166 + await route.fulfill({ status: 500, contentType: 'application/json', body: '{}' });
  167 + } else {
  168 + await route.fulfill({
  169 + status: 200,
  170 + contentType: 'application/json',
  171 + body: usersBody([makeUser(1, '恢复用户')], 1),
  172 + });
  173 + }
  174 + });
  175 + await login(page);
  176 + await gotoUserList(page);
  177 + await expect(page.getByText('加载失败,点击重试')).toBeVisible();
  178 + fail = false;
  179 + await page.getByTestId('userlist-error').getByRole('button', { name: '点击重试' }).click();
  180 + await expect(page.getByText('恢复用户')).toBeVisible();
  181 + });
  182 +});