08ea5fbe98
添加用户管理视图、API和状态管理文件
506 lines
12 KiB
TypeScript
506 lines
12 KiB
TypeScript
import { Page } from '@playwright/test';
|
|
|
|
export interface MockConfig {
|
|
enabled: boolean;
|
|
mode: 'full' | 'partial' | 'none';
|
|
mockPaths: string[];
|
|
delay: number;
|
|
logCalls: boolean;
|
|
validateResponses: boolean;
|
|
dataSource: 'memory' | 'file' | 'database';
|
|
}
|
|
|
|
export interface MockStatus {
|
|
enabled: boolean;
|
|
mode: string;
|
|
activeMocks: string[];
|
|
callCount: number;
|
|
}
|
|
|
|
export interface MockData {
|
|
users?: any[];
|
|
roles?: any[];
|
|
menus?: any[];
|
|
operationLogs?: any[];
|
|
auth?: any;
|
|
}
|
|
|
|
export class MockManager {
|
|
private config: MockConfig;
|
|
private mockData: Map<string, any> = new Map();
|
|
private callHistory: Array<{ timestamp: number; url: string; method: string; response: any }> = [];
|
|
|
|
constructor(config: MockConfig) {
|
|
this.config = config;
|
|
}
|
|
|
|
enableMock(): void {
|
|
this.config.enabled = true;
|
|
console.log('✅ Mock已启用');
|
|
}
|
|
|
|
disableMock(): void {
|
|
this.config.enabled = false;
|
|
console.log('❌ Mock已禁用');
|
|
}
|
|
|
|
configureMock(config: Partial<MockConfig>): void {
|
|
this.config = { ...this.config, ...config };
|
|
console.log('⚙️ Mock配置已更新:', this.config);
|
|
}
|
|
|
|
resetMockData(): void {
|
|
this.mockData.clear();
|
|
this.callHistory = [];
|
|
console.log('🔄 Mock数据已重置');
|
|
}
|
|
|
|
presetTestData(data: MockData): void {
|
|
Object.entries(data).forEach(([key, value]) => {
|
|
this.mockData.set(key, value);
|
|
});
|
|
console.log('📦 测试数据已预设:', Object.keys(data));
|
|
}
|
|
|
|
clearPresets(): void {
|
|
this.mockData.clear();
|
|
console.log('🗑️ 预设数据已清除');
|
|
}
|
|
|
|
getMockStatus(): MockStatus {
|
|
return {
|
|
enabled: this.config.enabled,
|
|
mode: this.config.mode,
|
|
activeMocks: Array.from(this.mockData.keys()),
|
|
callCount: this.callHistory.length
|
|
};
|
|
}
|
|
|
|
getMockData(key: string): any {
|
|
return this.mockData.get(key);
|
|
}
|
|
|
|
recordCall(url: string, method: string, response: any): void {
|
|
if (this.config.logCalls) {
|
|
this.callHistory.push({
|
|
timestamp: Date.now(),
|
|
url,
|
|
method,
|
|
response
|
|
});
|
|
}
|
|
}
|
|
|
|
getCallHistory(): Array<{ timestamp: number; url: string; method: string; response: any }> {
|
|
return this.callHistory;
|
|
}
|
|
|
|
async interceptAPIRequest(page: Page): Promise<void> {
|
|
if (!this.config.enabled) {
|
|
return;
|
|
}
|
|
|
|
await page.route('**/sys/**', async (route) => {
|
|
const request = route.request();
|
|
const url = request.url();
|
|
const method = request.method();
|
|
|
|
const shouldMock = this.shouldMockRequest(url, method);
|
|
|
|
if (shouldMock) {
|
|
const mockResponse = await this.getMockResponse(url, method, request.postData());
|
|
|
|
if (mockResponse) {
|
|
this.recordCall(url, method, mockResponse);
|
|
|
|
await route.fulfill({
|
|
status: mockResponse.status || 200,
|
|
contentType: 'application/json',
|
|
body: JSON.stringify(mockResponse.body)
|
|
});
|
|
|
|
console.log(`🎭 Mock响应: ${method} ${url}`);
|
|
return;
|
|
}
|
|
}
|
|
|
|
await route.continue();
|
|
});
|
|
}
|
|
|
|
private shouldMockRequest(url: string, method: string): boolean {
|
|
if (!this.config.enabled) {
|
|
return false;
|
|
}
|
|
|
|
if (this.config.mode === 'full') {
|
|
return true;
|
|
}
|
|
|
|
if (this.config.mode === 'partial') {
|
|
return this.config.mockPaths.some(path => url.includes(path));
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
private async getMockResponse(url: string, method: string, postData?: string): Promise<any> {
|
|
if (this.config.delay > 0) {
|
|
await new Promise(resolve => setTimeout(resolve, this.config.delay));
|
|
}
|
|
|
|
if (url.includes('/sys/auth/login') && method === 'POST') {
|
|
const credentials = JSON.parse(postData || '{}');
|
|
|
|
if (credentials.username === 'admin' && credentials.password === 'admin123') {
|
|
return {
|
|
status: 200,
|
|
body: {
|
|
code: '200',
|
|
message: '登录成功',
|
|
data: {
|
|
token: 'mock-token-123456',
|
|
refreshToken: 'mock-refresh-token-789012',
|
|
user: {
|
|
id: 1,
|
|
username: 'admin',
|
|
email: 'admin@example.com',
|
|
phone: '13800138000',
|
|
status: 'active',
|
|
createBy: 'system',
|
|
updateBy: 'admin',
|
|
createdAt: '2024-01-01T00:00:00.000Z',
|
|
updatedAt: '2024-01-01T00:00:00.000Z'
|
|
},
|
|
permissions: ['dashboard:view', 'user:read', 'user:write', 'role:read', 'role:write', 'menu:read', 'menu:write', 'operationLog:read']
|
|
}
|
|
}
|
|
};
|
|
} else {
|
|
return {
|
|
status: 200,
|
|
body: {
|
|
code: '401',
|
|
message: '用户名或密码错误',
|
|
data: null
|
|
}
|
|
};
|
|
}
|
|
}
|
|
|
|
if (url.includes('/sys/auth/logout') && method === 'POST') {
|
|
return {
|
|
status: 200,
|
|
body: {
|
|
code: '200',
|
|
message: '登出成功',
|
|
data: null
|
|
}
|
|
};
|
|
}
|
|
|
|
if (url.includes('/sys/auth/refresh') && method === 'POST') {
|
|
return {
|
|
status: 200,
|
|
body: {
|
|
code: '200',
|
|
message: '刷新成功',
|
|
data: {
|
|
token: 'new-mock-token-123456',
|
|
refreshToken: 'new-mock-refresh-token-789012'
|
|
}
|
|
}
|
|
};
|
|
}
|
|
|
|
if (url.includes('/sys/user') && method === 'GET') {
|
|
const users = this.mockData.get('users') || [];
|
|
return {
|
|
status: 200,
|
|
body: {
|
|
code: '200',
|
|
message: '查询成功',
|
|
data: {
|
|
records: users,
|
|
total: users.length,
|
|
current: 1,
|
|
size: 10
|
|
}
|
|
}
|
|
};
|
|
}
|
|
|
|
if (url.includes('/sys/user') && method === 'POST') {
|
|
const newUser = JSON.parse(postData || '{}');
|
|
return {
|
|
status: 200,
|
|
body: {
|
|
code: '200',
|
|
message: '创建成功',
|
|
data: {
|
|
...newUser,
|
|
id: Math.floor(Math.random() * 10000) + 1,
|
|
createdAt: new Date().toISOString(),
|
|
updatedAt: new Date().toISOString()
|
|
}
|
|
}
|
|
};
|
|
}
|
|
|
|
if (url.includes('/sys/user/') && method === 'PUT') {
|
|
const updatedUser = JSON.parse(postData || '{}');
|
|
return {
|
|
status: 200,
|
|
body: {
|
|
code: '200',
|
|
message: '更新成功',
|
|
data: {
|
|
...updatedUser,
|
|
updatedAt: new Date().toISOString()
|
|
}
|
|
}
|
|
};
|
|
}
|
|
|
|
if (url.includes('/sys/user/') && method === 'DELETE') {
|
|
return {
|
|
status: 200,
|
|
body: {
|
|
code: '200',
|
|
message: '删除成功',
|
|
data: null
|
|
}
|
|
};
|
|
}
|
|
|
|
if (url.includes('/sys/role') && method === 'GET') {
|
|
const roles = this.mockData.get('roles') || [];
|
|
return {
|
|
status: 200,
|
|
body: {
|
|
code: '200',
|
|
message: '查询成功',
|
|
data: {
|
|
records: roles,
|
|
total: roles.length,
|
|
current: 1,
|
|
size: 10
|
|
}
|
|
}
|
|
};
|
|
}
|
|
|
|
if (url.includes('/sys/role') && method === 'POST') {
|
|
const newRole = JSON.parse(postData || '{}');
|
|
return {
|
|
status: 200,
|
|
body: {
|
|
code: '200',
|
|
message: '创建成功',
|
|
data: {
|
|
...newRole,
|
|
id: Math.floor(Math.random() * 1000) + 1,
|
|
createdAt: new Date().toISOString(),
|
|
updatedAt: new Date().toISOString()
|
|
}
|
|
}
|
|
};
|
|
}
|
|
|
|
if (url.includes('/sys/role/') && method === 'PUT') {
|
|
const updatedRole = JSON.parse(postData || '{}');
|
|
return {
|
|
status: 200,
|
|
body: {
|
|
code: '200',
|
|
message: '更新成功',
|
|
data: {
|
|
...updatedRole,
|
|
updatedAt: new Date().toISOString()
|
|
}
|
|
}
|
|
};
|
|
}
|
|
|
|
if (url.includes('/sys/role/') && method === 'DELETE') {
|
|
return {
|
|
status: 200,
|
|
body: {
|
|
code: '200',
|
|
message: '删除成功',
|
|
data: null
|
|
}
|
|
};
|
|
}
|
|
|
|
if (url.includes('/sys/menu') && method === 'GET') {
|
|
const menus = this.mockData.get('menus') || [];
|
|
return {
|
|
status: 200,
|
|
body: {
|
|
code: '200',
|
|
message: '查询成功',
|
|
data: menus
|
|
}
|
|
};
|
|
}
|
|
|
|
if (url.includes('/sys/menu/user/') && method === 'GET') {
|
|
const userIdMatch = url.match(/\/sys\/menu\/user\/(\d+)/);
|
|
const userId = userIdMatch ? parseInt(userIdMatch[1]) : 1;
|
|
|
|
const userMenus = [
|
|
{
|
|
id: 1,
|
|
code: 'dashboard',
|
|
name: '仪表盘',
|
|
path: '/dashboard',
|
|
icon: 'DashboardOutlined',
|
|
parentId: 0,
|
|
sortOrder: 1,
|
|
status: 'active',
|
|
createdAt: '2024-01-01T00:00:00.000Z',
|
|
updatedAt: '2024-01-01T00:00:00.000Z',
|
|
children: []
|
|
},
|
|
{
|
|
id: 2,
|
|
code: 'user',
|
|
name: '用户管理',
|
|
path: '/users',
|
|
icon: 'UserOutlined',
|
|
parentId: 0,
|
|
sortOrder: 2,
|
|
status: 'active',
|
|
createdAt: '2024-01-01T00:00:00.000Z',
|
|
updatedAt: '2024-01-01T00:00:00.000Z',
|
|
children: []
|
|
},
|
|
{
|
|
id: 3,
|
|
code: 'role',
|
|
name: '角色管理',
|
|
path: '/roles',
|
|
icon: 'TeamOutlined',
|
|
parentId: 0,
|
|
sortOrder: 3,
|
|
status: 'active',
|
|
createdAt: '2024-01-01T00:00:00.000Z',
|
|
updatedAt: '2024-01-01T00:00:00.000Z',
|
|
children: []
|
|
},
|
|
{
|
|
id: 4,
|
|
code: 'menu',
|
|
name: '菜单管理',
|
|
path: '/menus',
|
|
icon: 'MenuOutlined',
|
|
parentId: 0,
|
|
sortOrder: 4,
|
|
status: 'active',
|
|
createdAt: '2024-01-01T00:00:00.000Z',
|
|
updatedAt: '2024-01-01T00:00:00.000Z',
|
|
children: []
|
|
},
|
|
{
|
|
id: 5,
|
|
code: 'permission',
|
|
name: '权限管理',
|
|
path: '/permissions',
|
|
icon: 'SafetyOutlined',
|
|
parentId: 0,
|
|
sortOrder: 5,
|
|
status: 'active',
|
|
createdAt: '2024-01-01T00:00:00.000Z',
|
|
updatedAt: '2024-01-01T00:00:00.000Z',
|
|
children: []
|
|
},
|
|
{
|
|
id: 6,
|
|
code: 'operationLog',
|
|
name: '操作日志',
|
|
path: '/operation-logs',
|
|
icon: 'FileTextOutlined',
|
|
parentId: 0,
|
|
sortOrder: 6,
|
|
status: 'active',
|
|
createdAt: '2024-01-01T00:00:00.000Z',
|
|
updatedAt: '2024-01-01T00:00:00.000Z',
|
|
children: []
|
|
}
|
|
];
|
|
|
|
return {
|
|
status: 200,
|
|
body: {
|
|
code: '200',
|
|
message: '查询成功',
|
|
data: userMenus
|
|
}
|
|
};
|
|
}
|
|
|
|
if (url.includes('/sys/menu') && method === 'POST') {
|
|
const newMenu = JSON.parse(postData || '{}');
|
|
return {
|
|
status: 200,
|
|
body: {
|
|
code: '200',
|
|
message: '创建成功',
|
|
data: {
|
|
...newMenu,
|
|
id: Math.floor(Math.random() * 1000) + 1,
|
|
createdAt: new Date().toISOString(),
|
|
updatedAt: new Date().toISOString()
|
|
}
|
|
}
|
|
};
|
|
}
|
|
|
|
if (url.includes('/sys/menu/') && method === 'PUT') {
|
|
const updatedMenu = JSON.parse(postData || '{}');
|
|
return {
|
|
status: 200,
|
|
body: {
|
|
code: '200',
|
|
message: '更新成功',
|
|
data: {
|
|
...updatedMenu,
|
|
updatedAt: new Date().toISOString()
|
|
}
|
|
}
|
|
};
|
|
}
|
|
|
|
if (url.includes('/sys/menu/') && method === 'DELETE') {
|
|
return {
|
|
status: 200,
|
|
body: {
|
|
code: '200',
|
|
message: '删除成功',
|
|
data: null
|
|
}
|
|
};
|
|
}
|
|
|
|
if (url.includes('/sys/operationLog') && method === 'GET') {
|
|
const operationLogs = this.mockData.get('operationLogs') || [];
|
|
return {
|
|
status: 200,
|
|
body: {
|
|
code: '200',
|
|
message: '查询成功',
|
|
data: {
|
|
records: operationLogs,
|
|
total: operationLogs.length,
|
|
current: 1,
|
|
size: 10
|
|
}
|
|
}
|
|
};
|
|
}
|
|
|
|
return null;
|
|
}
|
|
}
|