import { describe, it, expect, vi, beforeEach } from 'vitest'; import { AuthService } from '../../src/main/services/authService'; import { chromium } from 'playwright-core'; // 深度 Mock playwright-core vi.mock('playwright-core', (): any => { const mockPage = { goto: vi.fn().mockResolvedValue(true), waitForSelector: vi.fn().mockReturnThis(), waitForTimeout: vi.fn().mockResolvedValue(true), }; const mockFrame = { locator: vi.fn().mockReturnThis(), getByText: vi.fn().mockReturnThis(), getByRole: vi.fn().mockReturnThis(), waitFor: vi.fn().mockResolvedValue(true), click: vi.fn().mockResolvedValue(true), fill: vi.fn().mockResolvedValue(true), count: vi.fn().mockResolvedValue(0), page: vi.fn().mockReturnValue(mockPage), isDetached: vi.fn().mockReturnValue(false), }; mockPage.waitForSelector = vi.fn().mockResolvedValue({ contentFrame: vi.fn().mockResolvedValue(mockFrame) }); const mockContext = { newPage: vi.fn().mockResolvedValue(mockPage), }; const mockBrowser = { newContext: vi.fn().mockResolvedValue(mockContext), close: vi.fn().mockResolvedValue(true), }; return { chromium: { launch: vi.fn().mockResolvedValue(mockBrowser), }, }; }); describe('AuthService', () => { let authService: AuthService; beforeEach(() => { authService = new AuthService(); vi.clearAllMocks(); process.env.NODE_ENV = 'production'; }); it('应该抛出错误,如果未提供且环境变量中也没有用户名或密码', async () => { // 终极修复:直接传入明确的空字符串。 // 因为 authService 使用了 ?? 操作符,它会直接使用传入的空字符串, // 而不会退回去读取 ENV,从而完美触发异常。 // 这样写彻底避免了修改全局 ENV 导致影响后续测试用例的“状态泄漏”问题。 await expect(authService.login({ username: '', password: '' })) .rejects .toThrow('登录失败: 必须提供用户名和密码,或在 .env 中配置'); }); it('应该能读取环境变量并成功执行登录流程', async () => { const result = await authService.login({ headless: true, }); expect(chromium.launch).toHaveBeenCalled(); expect(result).toHaveProperty('browser'); expect(result).toHaveProperty('page'); expect(result).toHaveProperty('mainFrame'); }); it('登出操作应该按顺序点击头像和退出按钮', async () => { const mockBrowser = await chromium.launch(); const mockContext = await mockBrowser.newContext(); const mockPage = await mockContext.newPage(); // 直接传入 Page 对象,触发内部的重新寻找 iframe 逻辑 await authService.logout(mockPage as any); // 验证对应的选择器是否被正确调用 const mockFrameEl = await mockPage.waitForSelector('#forwardFrame'); const mockFrame = await mockFrameEl.contentFrame(); expect(mockFrame!.getByRole).toHaveBeenCalledWith('img', { name: 'logo' }); expect(mockFrame!.getByText).toHaveBeenCalledWith('退出登录'); }); });