- Fix iframe handling in login process - Use getByRole for more reliable element selection - Add forced login confirmation dialog handling - Refactor logout to support intelligent Frame detection - Replace || with ?? for proper null coalescing - Update tests to avoid global ENV mutations Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
90 lines
3.0 KiB
TypeScript
90 lines
3.0 KiB
TypeScript
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('退出登录');
|
||
});
|
||
}); |