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