feat: 实现测试数据管理器
- 创建 TestDataManager 单例类 - 支持创建用户、角色等测试数据 - 实现测试数据跟踪机制 - 支持自动清理测试数据 - 添加完整的单元测试(6个测试用例全部通过)
This commit is contained in:
@@ -0,0 +1,117 @@
|
||||
import { describe, it, expect, beforeEach, vi } from 'vitest';
|
||||
import { TestDataManager, getTestDataManager } from '../test-data-manager';
|
||||
|
||||
global.fetch = vi.fn();
|
||||
|
||||
describe('TestDataManager', () => {
|
||||
let manager: TestDataManager;
|
||||
|
||||
beforeEach(() => {
|
||||
manager = TestDataManager.getInstance();
|
||||
manager.clearTracking();
|
||||
vi.clearAllMocks();
|
||||
});
|
||||
|
||||
it('should be a singleton', () => {
|
||||
const instance1 = getTestDataManager();
|
||||
const instance2 = getTestDataManager();
|
||||
expect(instance1).toBe(instance2);
|
||||
});
|
||||
|
||||
it('should create user and track it', async () => {
|
||||
const mockUserId = 'user-123';
|
||||
(global.fetch as any).mockResolvedValueOnce({
|
||||
ok: true,
|
||||
json: async () => ({ data: { id: mockUserId } })
|
||||
});
|
||||
|
||||
const userData = {
|
||||
username: 'testuser',
|
||||
password: 'Test@123',
|
||||
email: 'test@example.com',
|
||||
};
|
||||
|
||||
const result = await manager.createUser(userData);
|
||||
|
||||
expect(result.id).toBe(mockUserId);
|
||||
expect(result.type).toBe('user');
|
||||
expect(result.data.username).toBe('testuser');
|
||||
expect(manager.getCreatedData('user')).toHaveLength(1);
|
||||
});
|
||||
|
||||
it('should create role and track it', async () => {
|
||||
const mockRoleId = 'role-456';
|
||||
(global.fetch as any).mockResolvedValueOnce({
|
||||
ok: true,
|
||||
json: async () => ({ data: { id: mockRoleId } })
|
||||
});
|
||||
|
||||
const roleData = {
|
||||
roleName: '测试角色',
|
||||
roleKey: 'test_role',
|
||||
};
|
||||
|
||||
const result = await manager.createRole(roleData);
|
||||
|
||||
expect(result.id).toBe(mockRoleId);
|
||||
expect(result.type).toBe('role');
|
||||
expect(manager.getCreatedData('role')).toHaveLength(1);
|
||||
});
|
||||
|
||||
it('should cleanup created data', async () => {
|
||||
(global.fetch as any)
|
||||
.mockResolvedValueOnce({
|
||||
ok: true,
|
||||
json: async () => ({ data: { id: 'user-1' } })
|
||||
})
|
||||
.mockResolvedValueOnce({
|
||||
ok: true,
|
||||
json: async () => ({ data: { id: 'user-2' } })
|
||||
})
|
||||
.mockResolvedValueOnce({ ok: true })
|
||||
.mockResolvedValueOnce({ ok: true });
|
||||
|
||||
await manager.createUser({ username: 'user1', password: 'Test@123', email: 'user1@test.com' });
|
||||
await manager.createUser({ username: 'user2', password: 'Test@123', email: 'user2@test.com' });
|
||||
|
||||
expect(manager.getCreatedData('user')).toHaveLength(2);
|
||||
|
||||
await manager.cleanup('user');
|
||||
|
||||
expect(manager.getCreatedData('user')).toHaveLength(0);
|
||||
expect(global.fetch).toHaveBeenCalledTimes(4); // 2 creates + 2 deletes
|
||||
});
|
||||
|
||||
it('should cleanup all data types when no type specified', async () => {
|
||||
(global.fetch as any)
|
||||
.mockResolvedValueOnce({
|
||||
ok: true,
|
||||
json: async () => ({ data: { id: 'user-1' } })
|
||||
})
|
||||
.mockResolvedValueOnce({
|
||||
ok: true,
|
||||
json: async () => ({ data: { id: 'role-1' } })
|
||||
})
|
||||
.mockResolvedValueOnce({ ok: true })
|
||||
.mockResolvedValueOnce({ ok: true });
|
||||
|
||||
await manager.createUser({ username: 'user1', password: 'Test@123', email: 'user1@test.com' });
|
||||
await manager.createRole({ roleName: '角色1', roleKey: 'role1' });
|
||||
|
||||
await manager.cleanup();
|
||||
|
||||
expect(manager.getCreatedData('user')).toHaveLength(0);
|
||||
expect(manager.getCreatedData('role')).toHaveLength(0);
|
||||
});
|
||||
|
||||
it('should throw error on creation failure', async () => {
|
||||
(global.fetch as any).mockResolvedValueOnce({
|
||||
ok: false,
|
||||
statusText: 'Bad Request'
|
||||
});
|
||||
|
||||
await expect(
|
||||
manager.createUser({ username: 'test', password: 'Test@123', email: 'test@test.com' })
|
||||
).rejects.toThrow('Failed to create user');
|
||||
});
|
||||
});
|
||||
@@ -0,0 +1,146 @@
|
||||
import { Page } from '@playwright/test';
|
||||
|
||||
export interface TestData {
|
||||
id: string;
|
||||
type: string;
|
||||
data: Record<string, any>;
|
||||
createdAt: Date;
|
||||
}
|
||||
|
||||
export class TestDataManager {
|
||||
private static instance: TestDataManager;
|
||||
private createdData: Map<string, TestData[]> = new Map();
|
||||
private page: Page | null = null;
|
||||
private static readonly API_BASE_URL = process.env.VITE_API_BASE_URL || 'http://localhost:8084';
|
||||
|
||||
static getInstance(): TestDataManager {
|
||||
if (!TestDataManager.instance) {
|
||||
TestDataManager.instance = new TestDataManager();
|
||||
}
|
||||
return TestDataManager.instance;
|
||||
}
|
||||
|
||||
setPage(page: Page): void {
|
||||
this.page = page;
|
||||
}
|
||||
|
||||
async createUser(userData: {
|
||||
username: string;
|
||||
password: string;
|
||||
email: string;
|
||||
phone?: string;
|
||||
nickname?: string;
|
||||
}): Promise<TestData> {
|
||||
const response = await fetch(`${TestDataManager.API_BASE_URL}/api/users`, {
|
||||
method: 'POST',
|
||||
headers: {
|
||||
'Content-Type': 'application/json',
|
||||
},
|
||||
body: JSON.stringify({
|
||||
...userData,
|
||||
status: 1,
|
||||
}),
|
||||
});
|
||||
|
||||
if (!response.ok) {
|
||||
throw new Error(`Failed to create user: ${response.statusText}`);
|
||||
}
|
||||
|
||||
const result = await response.json();
|
||||
const testData: TestData = {
|
||||
id: result.data?.id || result.id,
|
||||
type: 'user',
|
||||
data: userData,
|
||||
createdAt: new Date(),
|
||||
};
|
||||
|
||||
this.trackData('user', testData);
|
||||
return testData;
|
||||
}
|
||||
|
||||
async createRole(roleData: {
|
||||
roleName: string;
|
||||
roleKey: string;
|
||||
roleSort?: number;
|
||||
}): Promise<TestData> {
|
||||
const response = await fetch(`${TestDataManager.API_BASE_URL}/api/roles`, {
|
||||
method: 'POST',
|
||||
headers: {
|
||||
'Content-Type': 'application/json',
|
||||
},
|
||||
body: JSON.stringify({
|
||||
...roleData,
|
||||
status: 1,
|
||||
}),
|
||||
});
|
||||
|
||||
if (!response.ok) {
|
||||
throw new Error(`Failed to create role: ${response.statusText}`);
|
||||
}
|
||||
|
||||
const result = await response.json();
|
||||
const testData: TestData = {
|
||||
id: result.data?.id || result.id,
|
||||
type: 'role',
|
||||
data: roleData,
|
||||
createdAt: new Date(),
|
||||
};
|
||||
|
||||
this.trackData('role', testData);
|
||||
return testData;
|
||||
}
|
||||
|
||||
async cleanup(type?: string): Promise<void> {
|
||||
const typesToClean = type ? [type] : Array.from(this.createdData.keys());
|
||||
|
||||
for (const dataType of typesToClean) {
|
||||
const items = this.createdData.get(dataType) || [];
|
||||
|
||||
for (const item of items.reverse()) {
|
||||
try {
|
||||
await this.deleteData(item);
|
||||
} catch (error) {
|
||||
console.error(`Failed to cleanup ${dataType} ${item.id}:`, error);
|
||||
}
|
||||
}
|
||||
|
||||
this.createdData.delete(dataType);
|
||||
}
|
||||
}
|
||||
|
||||
private async deleteData(data: TestData): Promise<void> {
|
||||
const endpoint = this.getEndpoint(data.type);
|
||||
await fetch(`${TestDataManager.API_BASE_URL}${endpoint}/${data.id}`, {
|
||||
method: 'DELETE',
|
||||
});
|
||||
}
|
||||
|
||||
private getEndpoint(type: string): string {
|
||||
const endpoints: Record<string, string> = {
|
||||
user: '/api/users',
|
||||
role: '/api/roles',
|
||||
menu: '/api/menus',
|
||||
config: '/api/configs',
|
||||
};
|
||||
return endpoints[type] || `/api/${type}s`;
|
||||
}
|
||||
|
||||
private trackData(type: string, data: TestData): void {
|
||||
if (!this.createdData.has(type)) {
|
||||
this.createdData.set(type, []);
|
||||
}
|
||||
this.createdData.get(type)!.push(data);
|
||||
}
|
||||
|
||||
getCreatedData(type: string): TestData[] {
|
||||
return this.createdData.get(type) || [];
|
||||
}
|
||||
|
||||
clearTracking(): void {
|
||||
this.createdData.clear();
|
||||
}
|
||||
}
|
||||
|
||||
export function getTestDataManager(): TestDataManager {
|
||||
return TestDataManager.getInstance();
|
||||
}
|
||||
Reference in New Issue
Block a user