Files
everything-is-suitable/everything-is-suitable-test/e2e/core/mock-manager.ts
T
张翔 08ea5fbe98 feat(admin): 添加用户管理相关文件
添加用户管理视图、API和状态管理文件
2026-03-28 14:37:29 +08:00

304 lines
6.5 KiB
TypeScript

import { Page, Route } from '@playwright/test';
/**
* Mock管理器
* 提供统一的API Mock功能
*/
export interface MockConfig {
url: string | RegExp;
method?: 'GET' | 'POST' | 'PUT' | 'DELETE' | 'PATCH';
status?: number;
body?: unknown;
headers?: Record<string, string>;
delay?: number;
}
export class MockManager {
private page: Page;
private mocks: Map<string, MockConfig> = new Map();
private isEnabled: boolean = false;
constructor(page: Page) {
this.page = page;
}
/**
* 启用Mock
*/
enable(): void {
this.isEnabled = true;
}
/**
* 禁用Mock
*/
disable(): void {
this.isEnabled = false;
}
/**
* 检查Mock是否启用
*/
isMockEnabled(): boolean {
return this.isEnabled;
}
/**
* 添加Mock配置
*/
addMock(config: MockConfig): void {
const key = this.getMockKey(config.url, config.method || 'GET');
this.mocks.set(key, config);
}
/**
* 移除Mock配置
*/
removeMock(url: string | RegExp, method: string = 'GET'): void {
const key = this.getMockKey(url, method);
this.mocks.delete(key);
}
/**
* 清除所有Mock
*/
clearMocks(): void {
this.mocks.clear();
}
/**
* 应用所有Mock
*/
async applyMocks(): Promise<void> {
if (!this.isEnabled) {
return;
}
await this.page.route('**/*', async (route: Route) => {
const request = route.request();
const url = request.url();
const method = request.method();
// 查找匹配的Mock配置
for (const config of this.mocks.values()) {
if (this.matchesUrl(url, config.url) && method === (config.method || 'GET')) {
// 模拟延迟
if (config.delay) {
await new Promise(resolve => setTimeout(resolve, config.delay));
}
// 返回Mock响应
await route.fulfill({
status: config.status || 200,
contentType: 'application/json',
headers: config.headers || {},
body: JSON.stringify(config.body || {}),
});
return;
}
}
// 没有匹配的Mock,继续正常请求
await route.continue();
});
}
/**
* Mock登录API
*/
mockLogin(success: boolean = true, delay: number = 500): void {
this.addMock({
url: /.*\/auth\/login/,
method: 'POST',
status: success ? 200 : 401,
delay,
body: success
? {
token: 'mock-token-' + Date.now(),
refreshToken: 'mock-refresh-token',
user: {
id: 1,
username: 'admin',
realName: '管理员',
email: 'admin@example.com',
avatar: '',
status: 'active',
},
permissions: ['*'],
}
: {
message: '用户名或密码错误',
code: 401,
},
});
}
/**
* Mock用户列表API
*/
mockUserList(count: number = 10, delay: number = 300): void {
const users = Array.from({ length: count }, (_, i) => ({
id: i + 1,
username: `user${i + 1}`,
realName: `用户${i + 1}`,
email: `user${i + 1}@example.com`,
phone: `138001380${String(i).padStart(2, '0')}`,
status: i % 3 === 0 ? 'inactive' : 'active',
createTime: new Date().toISOString(),
}));
this.addMock({
url: /.*\/user\/list/,
method: 'GET',
status: 200,
delay,
body: {
data: users,
total: count,
page: 1,
pageSize: 10,
},
});
}
/**
* Mock角色列表API
*/
mockRoleList(delay: number = 300): void {
const roles = [
{ id: 1, roleName: '超级管理员', roleKey: 'admin', status: 'active' },
{ id: 2, roleName: '普通用户', roleKey: 'user', status: 'active' },
{ id: 3, roleName: '访客', roleKey: 'guest', status: 'inactive' },
];
this.addMock({
url: /.*\/role\/list/,
method: 'GET',
status: 200,
delay,
body: {
data: roles,
total: roles.length,
},
});
}
/**
* Mock菜单列表API
*/
mockMenuList(delay: number = 300): void {
const menus = [
{ id: 1, menuName: '仪表盘', path: '/dashboard', icon: 'DashboardOutlined', status: 'active' },
{ id: 2, menuName: '系统管理', path: '/sys', icon: 'SettingOutlined', status: 'active' },
{ id: 3, menuName: '用户管理', path: '/sys/user', parentId: 2, status: 'active' },
{ id: 4, menuName: '角色管理', path: '/sys/role', parentId: 2, status: 'active' },
];
this.addMock({
url: /.*\/menu\/list/,
method: 'GET',
status: 200,
delay,
body: {
data: menus,
total: menus.length,
},
});
}
/**
* Mock创建操作
*/
mockCreate(resource: string, delay: number = 500): void {
this.addMock({
url: new RegExp(`.*\\/${resource}$`),
method: 'POST',
status: 200,
delay,
body: {
message: '创建成功',
code: 200,
data: { id: Date.now() },
},
});
}
/**
* Mock更新操作
*/
mockUpdate(resource: string, delay: number = 500): void {
this.addMock({
url: new RegExp(`.*\\/${resource}\\/.*`),
method: 'PUT',
status: 200,
delay,
body: {
message: '更新成功',
code: 200,
},
});
}
/**
* Mock删除操作
*/
mockDelete(resource: string, delay: number = 500): void {
this.addMock({
url: new RegExp(`.*\\/${resource}\\/.*`),
method: 'DELETE',
status: 200,
delay,
body: {
message: '删除成功',
code: 200,
},
});
}
/**
* Mock错误响应
*/
mockError(url: string | RegExp, status: number = 500, message: string = '服务器错误'): void {
this.addMock({
url,
method: 'GET',
status,
body: {
message,
code: status,
},
});
}
/**
* Mock网络延迟
*/
mockDelay(url: string | RegExp, delay: number = 2000): void {
this.addMock({
url,
method: 'GET',
delay,
body: {},
});
}
/**
* 获取Mock键
*/
private getMockKey(url: string | RegExp, method: string): string {
const urlStr = url instanceof RegExp ? url.source : url;
return `${method}:${urlStr}`;
}
/**
* 检查URL是否匹配
*/
private matchesUrl(actualUrl: string, configUrl: string | RegExp): boolean {
if (configUrl instanceof RegExp) {
return configUrl.test(actualUrl);
}
return actualUrl.includes(configUrl);
}
}