/** * @jest-environment jsdom */ /** * 激活页面测试 * 测试许可证激活和免费试用功能 */ import { render, screen, fireEvent, waitFor, act } from '@testing-library/react'; import ActivatePage from '../page'; // Mock next/navigation const mockPush = jest.fn(); jest.mock('next/navigation', () => ({ useRouter: () => ({ push: mockPush, }), })); // Mock fetch global.fetch = jest.fn(); const createJsonResponse = (data: any, init?: { ok?: boolean; status?: number; statusText?: string }) => ({ ok: init?.ok ?? true, status: init?.status ?? 200, statusText: init?.statusText ?? 'OK', json: () => Promise.resolve(data), text: () => Promise.resolve(JSON.stringify(data)), }); const mockFetchRoutes = (routes: Record) => { (global.fetch as jest.Mock).mockImplementation((input: RequestInfo | URL) => { const url = typeof input === 'string' ? input : input instanceof URL ? input.toString() : input.url; const handler = routes[url] ?? routes['*']; if (handler instanceof Error) { return Promise.reject(handler); } if (!handler) { throw new Error(`Unhandled fetch request: ${url}`); } return Promise.resolve(typeof handler === 'function' ? handler() : handler); }); }; const renderPage = async () => { await act(async () => { render(); }); await screen.findByText('FLNXI 素材系统'); }; describe('ActivatePage', () => { beforeEach(() => { jest.clearAllMocks(); (global.fetch as jest.Mock).mockReset(); mockFetchRoutes({ '/api/license/check': createJsonResponse({ activated: false }), '*': createJsonResponse({ success: true }), }); }); describe('渲染测试', () => { it('应该正确渲染页面标题和表单', async () => { await renderPage(); expect(screen.getByText('FLNXI 素材系统')).toBeInTheDocument(); expect(screen.getByText('请激活您的许可证')).toBeInTheDocument(); expect(screen.getByPlaceholderText('XXXX-XXXX-XXXX-XXXX')).toBeInTheDocument(); expect(screen.getByText('激活许可证')).toBeInTheDocument(); expect(screen.getByText('免费试用')).toBeInTheDocument(); }); it('应该显示免费版限制说明', async () => { await renderPage(); expect(screen.getByText('免费版限制:10 个用户、100 个素材')).toBeInTheDocument(); }); it('应该显示联系信息', async () => { await renderPage(); expect(screen.getByText('需要购买许可证?请联系我们')).toBeInTheDocument(); }); }); describe('许可证密钥格式化', () => { it('应该自动将输入转换为大写', async () => { await renderPage(); const input = screen.getByPlaceholderText('XXXX-XXXX-XXXX-XXXX') as HTMLInputElement; fireEvent.change(input, { target: { value: 'abcd' } }); expect(input.value).toBe('ABCD'); }); it('应该自动添加横杠分隔符', async () => { await renderPage(); const input = screen.getByPlaceholderText('XXXX-XXXX-XXXX-XXXX') as HTMLInputElement; fireEvent.change(input, { target: { value: 'abcd1234' } }); expect(input.value).toBe('ABCD-1234'); }); it('应该过滤非字母数字字符', async () => { await renderPage(); const input = screen.getByPlaceholderText('XXXX-XXXX-XXXX-XXXX') as HTMLInputElement; fireEvent.change(input, { target: { value: 'ab!@#cd12' } }); expect(input.value).toBe('ABCD-12'); }); it('应该限制最大长度为19个字符(含横杠)', async () => { await renderPage(); const input = screen.getByPlaceholderText('XXXX-XXXX-XXXX-XXXX') as HTMLInputElement; fireEvent.change(input, { target: { value: 'ABCD1234EFGH5678EXTRA' } }); expect(input.value).toBe('ABCD-1234-EFGH-5678'); }); }); describe('激活按钮状态', () => { it('密钥不完整时激活按钮应该禁用', async () => { await renderPage(); const button = screen.getByText('激活许可证'); expect(button).toBeDisabled(); }); it('密钥完整时激活按钮应该启用', async () => { await renderPage(); const input = screen.getByPlaceholderText('XXXX-XXXX-XXXX-XXXX'); fireEvent.change(input, { target: { value: 'ABCD1234EFGH5678' } }); const button = screen.getByText('激活许可证'); expect(button).not.toBeDisabled(); }); }); describe('许可证激活', () => { it('激活成功后应该显示成功页面', async () => { mockFetchRoutes({ '/api/license/check': createJsonResponse({ activated: false }), '/api/license/activate': createJsonResponse({ success: true, initialAdmin: { email: 'admin@local.flnxi', username: 'admin', password: 'Generated-Password-123', mustChangePassword: true, }, }), }); await renderPage(); const input = screen.getByPlaceholderText('XXXX-XXXX-XXXX-XXXX'); fireEvent.change(input, { target: { value: 'ABCD1234EFGH5678' } }); const button = screen.getByText('激活许可证'); await act(async () => { fireEvent.click(button); }); await waitFor(() => { expect(screen.getByText('激活成功')).toBeInTheDocument(); expect(screen.getByText('系统已准备就绪')).toBeInTheDocument(); expect(screen.getByText('初始管理员账号')).toBeInTheDocument(); expect(screen.getByText('admin@local.flnxi')).toBeInTheDocument(); expect(screen.getByText('Generated-Password-123')).toBeInTheDocument(); expect(screen.getByText('前往登录')).toBeInTheDocument(); }); }); it('点击前往登录按钮应该跳转到登录页', async () => { mockFetchRoutes({ '/api/license/check': createJsonResponse({ activated: false }), '/api/license/activate': createJsonResponse({ success: true, initialAdmin: { email: 'admin@local.flnxi', username: 'admin', password: 'Generated-Password-123', mustChangePassword: true, }, }), }); await renderPage(); const input = screen.getByPlaceholderText('XXXX-XXXX-XXXX-XXXX'); fireEvent.change(input, { target: { value: 'ABCD1234EFGH5678' } }); const activateButton = screen.getByText('激活许可证'); await act(async () => { fireEvent.click(activateButton); }); await waitFor(() => { expect(screen.getByText('前往登录')).toBeInTheDocument(); }); const loginButton = screen.getByText('前往登录'); await act(async () => { fireEvent.click(loginButton); }); expect(mockPush).toHaveBeenCalledWith('/login'); }); it('激活失败应该显示错误信息', async () => { mockFetchRoutes({ '/api/license/check': createJsonResponse({ activated: false }), '/api/license/activate': createJsonResponse({ success: false, error: '许可证无效' }), }); await renderPage(); const input = screen.getByPlaceholderText('XXXX-XXXX-XXXX-XXXX'); fireEvent.change(input, { target: { value: 'ABCD1234EFGH5678' } }); const button = screen.getByText('激活许可证'); await act(async () => { fireEvent.click(button); }); await waitFor(() => { expect(screen.getByText('许可证无效')).toBeInTheDocument(); }); }); it('网络错误应该显示网络错误提示', async () => { mockFetchRoutes({ '/api/license/check': createJsonResponse({ activated: false }), '/api/license/activate': new Error('Network error'), }); await renderPage(); const input = screen.getByPlaceholderText('XXXX-XXXX-XXXX-XXXX'); fireEvent.change(input, { target: { value: 'ABCD1234EFGH5678' } }); const button = screen.getByText('激活许可证'); await act(async () => { fireEvent.click(button); }); await waitFor(() => { expect(screen.getByText('网络错误,请检查网络连接')).toBeInTheDocument(); }); }); it('激活时应该显示加载状态', async () => { let resolvePromise: (value: unknown) => void; mockFetchRoutes({ '/api/license/check': createJsonResponse({ activated: false }), '/api/license/activate': () => new Promise((resolve) => { resolvePromise = resolve; }), }); await renderPage(); const input = screen.getByPlaceholderText('XXXX-XXXX-XXXX-XXXX'); fireEvent.change(input, { target: { value: 'ABCD1234EFGH5678' } }); const button = screen.getByText('激活许可证'); await act(async () => { fireEvent.click(button); }); expect(screen.getByText('激活中...')).toBeInTheDocument(); // 清理 await act(async () => { resolvePromise!(createJsonResponse({ success: true, initialAdmin: { email: 'admin@local.flnxi', username: 'admin', password: 'Generated-Password-123', mustChangePassword: true, }, })); }); }); }); describe('免费试用', () => { it('点击免费试用按钮应该显示客户名称输入弹窗', async () => { await renderPage(); const button = screen.getByText('免费试用'); await act(async () => { fireEvent.click(button); }); expect(screen.getByText('申请免费试用')).toBeInTheDocument(); expect(screen.getByPlaceholderText('请输入您的公司或个人名称')).toBeInTheDocument(); expect(screen.getByText('确认申请')).toBeInTheDocument(); expect(screen.getByText('取消')).toBeInTheDocument(); }); it('未输入客户名称时确认申请按钮应该禁用', async () => { await renderPage(); const freeTrialButton = screen.getByText('免费试用'); await act(async () => { fireEvent.click(freeTrialButton); }); const confirmButton = screen.getByText('确认申请'); expect(confirmButton).toBeDisabled(); }); it('只输入客户名称未输入联系方式时确认申请按钮应该禁用', async () => { await renderPage(); const freeTrialButton = screen.getByText('免费试用'); await act(async () => { fireEvent.click(freeTrialButton); }); const nameInput = screen.getByPlaceholderText('请输入您的公司或个人名称'); fireEvent.change(nameInput, { target: { value: '测试公司' } }); const confirmButton = screen.getByText('确认申请'); expect(confirmButton).toBeDisabled(); }); it('输入客户名称和联系方式后确认申请按钮应该启用', async () => { await renderPage(); const freeTrialButton = screen.getByText('免费试用'); await act(async () => { fireEvent.click(freeTrialButton); }); const nameInput = screen.getByPlaceholderText('请输入您的公司或个人名称'); const contactInput = screen.getByPlaceholderText('手机号/微信/邮箱'); fireEvent.change(nameInput, { target: { value: '测试公司' } }); fireEvent.change(contactInput, { target: { value: '13800138000' } }); const confirmButton = screen.getByText('确认申请'); expect(confirmButton).not.toBeDisabled(); }); it('点击取消按钮应该关闭弹窗', async () => { await renderPage(); const freeTrialButton = screen.getByText('免费试用'); await act(async () => { fireEvent.click(freeTrialButton); }); expect(screen.getByText('申请免费试用')).toBeInTheDocument(); const cancelButton = screen.getByText('取消'); await act(async () => { fireEvent.click(cancelButton); }); expect(screen.queryByText('申请免费试用')).not.toBeInTheDocument(); }); it('免费试用成功后应该显示成功页面', async () => { mockFetchRoutes({ '/api/license/check': createJsonResponse({ activated: false }), '/api/license/activate-free': createJsonResponse({ success: true, initialAdmin: { email: 'admin@local.flnxi', username: 'admin', password: 'Generated-Password-123', mustChangePassword: true, }, }), }); await renderPage(); // 点击免费试用按钮打开弹窗 const freeTrialButton = screen.getByText('免费试用'); await act(async () => { fireEvent.click(freeTrialButton); }); // 输入客户名称和联系方式 const nameInput = screen.getByPlaceholderText('请输入您的公司或个人名称'); const contactInput = screen.getByPlaceholderText('手机号/微信/邮箱'); fireEvent.change(nameInput, { target: { value: '测试公司' } }); fireEvent.change(contactInput, { target: { value: '13800138000' } }); // 点击确认申请 const confirmButton = screen.getByText('确认申请'); await act(async () => { fireEvent.click(confirmButton); }); await waitFor(() => { expect(screen.getByText('激活成功')).toBeInTheDocument(); expect(screen.getByText('前往登录')).toBeInTheDocument(); }); expect(global.fetch).toHaveBeenCalledWith('/api/license/activate-free', { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ customerName: '测试公司', contactInfo: '13800138000' }), }); }); it('免费试用失败应该显示错误信息', async () => { mockFetchRoutes({ '/api/license/check': createJsonResponse({ activated: false }), '/api/license/activate-free': createJsonResponse({ success: false, error: '试用已过期' }), }); await renderPage(); // 点击免费试用按钮打开弹窗 const freeTrialButton = screen.getByText('免费试用'); await act(async () => { fireEvent.click(freeTrialButton); }); // 输入客户名称和联系方式 const nameInput = screen.getByPlaceholderText('请输入您的公司或个人名称'); const contactInput = screen.getByPlaceholderText('手机号/微信/邮箱'); fireEvent.change(nameInput, { target: { value: '测试公司' } }); fireEvent.change(contactInput, { target: { value: '13800138000' } }); // 点击确认申请 const confirmButton = screen.getByText('确认申请'); await act(async () => { fireEvent.click(confirmButton); }); await waitFor(() => { expect(screen.getByText('试用已过期')).toBeInTheDocument(); }); }); it('网络错误应该显示网络错误提示', async () => { mockFetchRoutes({ '/api/license/check': createJsonResponse({ activated: false }), '/api/license/activate-free': new Error('Network error'), }); await renderPage(); // 点击免费试用按钮打开弹窗 const freeTrialButton = screen.getByText('免费试用'); await act(async () => { fireEvent.click(freeTrialButton); }); // 输入客户名称和联系方式 const nameInput = screen.getByPlaceholderText('请输入您的公司或个人名称'); const contactInput = screen.getByPlaceholderText('手机号/微信/邮箱'); fireEvent.change(nameInput, { target: { value: '测试公司' } }); fireEvent.change(contactInput, { target: { value: '13800138000' } }); // 点击确认申请 const confirmButton = screen.getByText('确认申请'); await act(async () => { fireEvent.click(confirmButton); }); await waitFor(() => { expect(screen.getByText('网络错误,请检查网络连接')).toBeInTheDocument(); }); }); it('应该显示联系方式输入框', async () => { await renderPage(); const freeTrialButton = screen.getByText('免费试用'); await act(async () => { fireEvent.click(freeTrialButton); }); expect(screen.getByPlaceholderText('手机号/微信/邮箱')).toBeInTheDocument(); expect(screen.getByText('联系方式')).toBeInTheDocument(); }); }); describe('API 调用', () => { it('激活时应该发送正确的请求', async () => { mockFetchRoutes({ '/api/license/check': createJsonResponse({ activated: false }), '/api/license/activate': createJsonResponse({ success: true, initialAdmin: { email: 'admin@local.flnxi', username: 'admin', password: 'Generated-Password-123', mustChangePassword: true, }, }), }); await renderPage(); const input = screen.getByPlaceholderText('XXXX-XXXX-XXXX-XXXX'); fireEvent.change(input, { target: { value: 'ABCD1234EFGH5678' } }); const button = screen.getByText('激活许可证'); await act(async () => { fireEvent.click(button); }); await waitFor(() => { expect(global.fetch).toHaveBeenCalledWith('/api/license/activate', { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ licenseKey: 'ABCD-1234-EFGH-5678' }), }); }); }); }); });