diff --git a/novalon-manage-web/e2e/role-based-tests/shared/__tests__/test-data-manager.test.ts b/novalon-manage-web/e2e/role-based-tests/shared/__tests__/test-data-manager.test.ts new file mode 100644 index 0000000..647e7fb --- /dev/null +++ b/novalon-manage-web/e2e/role-based-tests/shared/__tests__/test-data-manager.test.ts @@ -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'); + }); +}); diff --git a/novalon-manage-web/e2e/role-based-tests/shared/test-data-manager.ts b/novalon-manage-web/e2e/role-based-tests/shared/test-data-manager.ts new file mode 100644 index 0000000..82bbe68 --- /dev/null +++ b/novalon-manage-web/e2e/role-based-tests/shared/test-data-manager.ts @@ -0,0 +1,146 @@ +import { Page } from '@playwright/test'; + +export interface TestData { + id: string; + type: string; + data: Record; + createdAt: Date; +} + +export class TestDataManager { + private static instance: TestDataManager; + private createdData: Map = 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 { + 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 { + 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 { + 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 { + 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 = { + 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(); +}