flnxi-Wu/app/activate/__tests__/page.test.tsx
2026-05-25 14:52:24 +08:00

520 lines
17 KiB
TypeScript
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

/**
* @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<string, any>) => {
(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(<ActivatePage />);
});
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' }),
});
});
});
});
});