feat: 添加管理后台页面和功能,优化测试和性能配置
refactor: 重构页面导航和滚动逻辑,提升用户体验 test: 更新测试配置和用例,增加覆盖率和稳定性 perf: 优化性能指标和阈值,适应开发环境需求 ci: 添加Lighthouse CI工作流,集成性能测试 docs: 更新API文档和健康检查端点 fix: 修复登录页面和表单提交问题 style: 调整响应式布局和可访问性改进 chore: 更新依赖项和脚本配置
This commit is contained in:
@@ -0,0 +1,134 @@
|
||||
import { renderHook, waitFor } from '@testing-library/react';
|
||||
import { useNews } from './use-news';
|
||||
import { contentService } from '@/lib/api/services';
|
||||
import { NewsItem } from '@/lib/api/types';
|
||||
|
||||
jest.mock('@/lib/api/services');
|
||||
|
||||
describe('useNews', () => {
|
||||
const mockNewsData: NewsItem[] = [
|
||||
{
|
||||
id: '1',
|
||||
title: '测试新闻1',
|
||||
excerpt: '测试摘要1',
|
||||
content: '测试内容1',
|
||||
date: '2024-01-01',
|
||||
category: '公司新闻',
|
||||
slug: 'test-news-1',
|
||||
},
|
||||
{
|
||||
id: '2',
|
||||
title: '测试新闻2',
|
||||
excerpt: '测试摘要2',
|
||||
content: '测试内容2',
|
||||
date: '2024-01-02',
|
||||
category: '产品发布',
|
||||
slug: 'test-news-2',
|
||||
},
|
||||
];
|
||||
|
||||
beforeEach(() => {
|
||||
jest.clearAllMocks();
|
||||
});
|
||||
|
||||
it('应该成功获取新闻数据', async () => {
|
||||
(contentService.getNews as jest.Mock).mockResolvedValue(mockNewsData);
|
||||
|
||||
const { result } = renderHook(() => useNews());
|
||||
|
||||
await waitFor(() => {
|
||||
expect(result.current.loading).toBe(false);
|
||||
});
|
||||
|
||||
expect(result.current.news).toEqual(mockNewsData);
|
||||
expect(result.current.error).toBeNull();
|
||||
expect(contentService.getNews).toHaveBeenCalledWith(undefined, undefined, 'desc');
|
||||
});
|
||||
|
||||
it('应该支持分类筛选', async () => {
|
||||
const categories = ['公司新闻'];
|
||||
(contentService.getNews as jest.Mock).mockResolvedValue([mockNewsData[0]]);
|
||||
|
||||
const { result } = renderHook(() => useNews(categories));
|
||||
|
||||
await waitFor(() => {
|
||||
expect(result.current.loading).toBe(false);
|
||||
});
|
||||
|
||||
expect(result.current.news).toEqual([mockNewsData[0]]);
|
||||
expect(contentService.getNews).toHaveBeenCalledWith(categories, undefined, 'desc');
|
||||
});
|
||||
|
||||
it('应该支持数量限制', async () => {
|
||||
const limit = 1;
|
||||
(contentService.getNews as jest.Mock).mockResolvedValue([mockNewsData[0]]);
|
||||
|
||||
const { result } = renderHook(() => useNews(undefined, limit));
|
||||
|
||||
await waitFor(() => {
|
||||
expect(result.current.loading).toBe(false);
|
||||
});
|
||||
|
||||
expect(result.current.news).toEqual([mockNewsData[0]]);
|
||||
expect(contentService.getNews).toHaveBeenCalledWith(undefined, limit, 'desc');
|
||||
});
|
||||
|
||||
it('应该支持排序', async () => {
|
||||
(contentService.getNews as jest.Mock).mockResolvedValue(mockNewsData);
|
||||
|
||||
const { result } = renderHook(() => useNews(undefined, undefined, 'asc'));
|
||||
|
||||
await waitFor(() => {
|
||||
expect(result.current.loading).toBe(false);
|
||||
});
|
||||
|
||||
expect(contentService.getNews).toHaveBeenCalledWith(undefined, undefined, 'asc');
|
||||
});
|
||||
|
||||
it('应该处理获取失败的情况', async () => {
|
||||
const error = new Error('获取新闻失败');
|
||||
(contentService.getNews as jest.Mock).mockRejectedValue(error);
|
||||
|
||||
const { result } = renderHook(() => useNews());
|
||||
|
||||
await waitFor(() => {
|
||||
expect(result.current.loading).toBe(false);
|
||||
});
|
||||
|
||||
expect(result.current.news).toEqual([]);
|
||||
expect(result.current.error).toEqual(error);
|
||||
});
|
||||
|
||||
it('应该在加载时设置loading状态', () => {
|
||||
(contentService.getNews as jest.Mock).mockImplementation(
|
||||
() => new Promise(() => {})
|
||||
);
|
||||
|
||||
const { result } = renderHook(() => useNews());
|
||||
|
||||
expect(result.current.loading).toBe(true);
|
||||
expect(result.current.news).toEqual([]);
|
||||
expect(result.current.error).toBeNull();
|
||||
});
|
||||
|
||||
it('应该在参数变化时重新获取数据', async () => {
|
||||
(contentService.getNews as jest.Mock).mockResolvedValue(mockNewsData);
|
||||
|
||||
const { result, rerender } = renderHook(
|
||||
({ categories }) => useNews(categories),
|
||||
{ initialProps: { categories: ['公司新闻'] } }
|
||||
);
|
||||
|
||||
await waitFor(() => {
|
||||
expect(result.current.loading).toBe(false);
|
||||
});
|
||||
|
||||
expect(contentService.getNews).toHaveBeenCalledTimes(1);
|
||||
|
||||
rerender({ categories: ['产品发布'] });
|
||||
|
||||
await waitFor(() => {
|
||||
expect(contentService.getNews).toHaveBeenCalledTimes(2);
|
||||
});
|
||||
});
|
||||
});
|
||||
@@ -0,0 +1,121 @@
|
||||
import { renderHook, waitFor } from '@testing-library/react';
|
||||
import { useProducts } from './use-products';
|
||||
import { contentService } from '@/lib/api/services';
|
||||
import { Product } from '@/lib/api/types';
|
||||
|
||||
jest.mock('@/lib/api/services');
|
||||
|
||||
describe('useProducts', () => {
|
||||
const mockProductsData: Product[] = [
|
||||
{
|
||||
id: '1',
|
||||
title: '测试产品1',
|
||||
description: '测试描述1',
|
||||
category: '软件产品',
|
||||
features: ['功能1', '功能2'],
|
||||
benefits: ['优势1', '优势2'],
|
||||
slug: 'test-product-1',
|
||||
},
|
||||
{
|
||||
id: '2',
|
||||
title: '测试产品2',
|
||||
description: '测试描述2',
|
||||
category: '云服务',
|
||||
features: ['功能3', '功能4'],
|
||||
benefits: ['优势3', '优势4'],
|
||||
slug: 'test-product-2',
|
||||
},
|
||||
];
|
||||
|
||||
beforeEach(() => {
|
||||
jest.clearAllMocks();
|
||||
});
|
||||
|
||||
it('应该成功获取产品数据', async () => {
|
||||
(contentService.getProducts as jest.Mock).mockResolvedValue(mockProductsData);
|
||||
|
||||
const { result } = renderHook(() => useProducts());
|
||||
|
||||
await waitFor(() => {
|
||||
expect(result.current.loading).toBe(false);
|
||||
});
|
||||
|
||||
expect(result.current.products).toEqual(mockProductsData);
|
||||
expect(result.current.error).toBeNull();
|
||||
expect(contentService.getProducts).toHaveBeenCalledWith(undefined);
|
||||
});
|
||||
|
||||
it('应该支持精选产品筛选', async () => {
|
||||
const featuredIds = ['1'];
|
||||
(contentService.getProducts as jest.Mock).mockResolvedValue([mockProductsData[0]]);
|
||||
|
||||
const { result } = renderHook(() => useProducts(featuredIds));
|
||||
|
||||
await waitFor(() => {
|
||||
expect(result.current.loading).toBe(false);
|
||||
});
|
||||
|
||||
expect(result.current.products).toEqual([mockProductsData[0]]);
|
||||
expect(contentService.getProducts).toHaveBeenCalledWith(featuredIds);
|
||||
});
|
||||
|
||||
it('应该处理获取失败的情况', async () => {
|
||||
const error = new Error('获取产品失败');
|
||||
(contentService.getProducts as jest.Mock).mockRejectedValue(error);
|
||||
|
||||
const { result } = renderHook(() => useProducts());
|
||||
|
||||
await waitFor(() => {
|
||||
expect(result.current.loading).toBe(false);
|
||||
});
|
||||
|
||||
expect(result.current.products).toEqual([]);
|
||||
expect(result.current.error).toEqual(error);
|
||||
});
|
||||
|
||||
it('应该在加载时设置loading状态', () => {
|
||||
(contentService.getProducts as jest.Mock).mockImplementation(
|
||||
() => new Promise(() => {})
|
||||
);
|
||||
|
||||
const { result } = renderHook(() => useProducts());
|
||||
|
||||
expect(result.current.loading).toBe(true);
|
||||
expect(result.current.products).toEqual([]);
|
||||
expect(result.current.error).toBeNull();
|
||||
});
|
||||
|
||||
it('应该在featuredIds变化时重新获取数据', async () => {
|
||||
(contentService.getProducts as jest.Mock).mockResolvedValue(mockProductsData);
|
||||
|
||||
const { result, rerender } = renderHook(
|
||||
({ featuredIds }) => useProducts(featuredIds),
|
||||
{ initialProps: { featuredIds: ['1'] } }
|
||||
);
|
||||
|
||||
await waitFor(() => {
|
||||
expect(result.current.loading).toBe(false);
|
||||
});
|
||||
|
||||
expect(contentService.getProducts).toHaveBeenCalledTimes(1);
|
||||
|
||||
rerender({ featuredIds: ['2'] });
|
||||
|
||||
await waitFor(() => {
|
||||
expect(contentService.getProducts).toHaveBeenCalledTimes(2);
|
||||
});
|
||||
});
|
||||
|
||||
it('应该处理空数组featuredIds', async () => {
|
||||
(contentService.getProducts as jest.Mock).mockResolvedValue(mockProductsData);
|
||||
|
||||
const { result } = renderHook(() => useProducts([]));
|
||||
|
||||
await waitFor(() => {
|
||||
expect(result.current.loading).toBe(false);
|
||||
});
|
||||
|
||||
expect(result.current.products).toEqual(mockProductsData);
|
||||
expect(contentService.getProducts).toHaveBeenCalledWith([]);
|
||||
});
|
||||
});
|
||||
@@ -0,0 +1,121 @@
|
||||
import { renderHook, waitFor } from '@testing-library/react';
|
||||
import { useServices } from './use-services';
|
||||
import { contentService } from '@/lib/api/services';
|
||||
import { Service } from '@/lib/api/types';
|
||||
|
||||
jest.mock('@/lib/api/services');
|
||||
|
||||
describe('useServices', () => {
|
||||
const mockServicesData: Service[] = [
|
||||
{
|
||||
id: '1',
|
||||
title: '测试服务1',
|
||||
description: '测试描述1',
|
||||
icon: 'Code',
|
||||
features: ['功能1', '功能2'],
|
||||
benefits: ['优势1', '优势2'],
|
||||
slug: 'test-service-1',
|
||||
},
|
||||
{
|
||||
id: '2',
|
||||
title: '测试服务2',
|
||||
description: '测试描述2',
|
||||
icon: 'Cloud',
|
||||
features: ['功能3', '功能4'],
|
||||
benefits: ['优势3', '优势4'],
|
||||
slug: 'test-service-2',
|
||||
},
|
||||
];
|
||||
|
||||
beforeEach(() => {
|
||||
jest.clearAllMocks();
|
||||
});
|
||||
|
||||
it('应该成功获取服务数据', async () => {
|
||||
(contentService.getServices as jest.Mock).mockResolvedValue(mockServicesData);
|
||||
|
||||
const { result } = renderHook(() => useServices());
|
||||
|
||||
await waitFor(() => {
|
||||
expect(result.current.loading).toBe(false);
|
||||
});
|
||||
|
||||
expect(result.current.services).toEqual(mockServicesData);
|
||||
expect(result.current.error).toBeNull();
|
||||
expect(contentService.getServices).toHaveBeenCalledWith(undefined);
|
||||
});
|
||||
|
||||
it('应该支持服务ID筛选', async () => {
|
||||
const ids = ['1'];
|
||||
(contentService.getServices as jest.Mock).mockResolvedValue([mockServicesData[0]]);
|
||||
|
||||
const { result } = renderHook(() => useServices(ids));
|
||||
|
||||
await waitFor(() => {
|
||||
expect(result.current.loading).toBe(false);
|
||||
});
|
||||
|
||||
expect(result.current.services).toEqual([mockServicesData[0]]);
|
||||
expect(contentService.getServices).toHaveBeenCalledWith(ids);
|
||||
});
|
||||
|
||||
it('应该处理获取失败的情况', async () => {
|
||||
const error = new Error('获取服务失败');
|
||||
(contentService.getServices as jest.Mock).mockRejectedValue(error);
|
||||
|
||||
const { result } = renderHook(() => useServices());
|
||||
|
||||
await waitFor(() => {
|
||||
expect(result.current.loading).toBe(false);
|
||||
});
|
||||
|
||||
expect(result.current.services).toEqual([]);
|
||||
expect(result.current.error).toEqual(error);
|
||||
});
|
||||
|
||||
it('应该在加载时设置loading状态', () => {
|
||||
(contentService.getServices as jest.Mock).mockImplementation(
|
||||
() => new Promise(() => {})
|
||||
);
|
||||
|
||||
const { result } = renderHook(() => useServices());
|
||||
|
||||
expect(result.current.loading).toBe(true);
|
||||
expect(result.current.services).toEqual([]);
|
||||
expect(result.current.error).toBeNull();
|
||||
});
|
||||
|
||||
it('应该在ids变化时重新获取数据', async () => {
|
||||
(contentService.getServices as jest.Mock).mockResolvedValue(mockServicesData);
|
||||
|
||||
const { result, rerender } = renderHook(
|
||||
({ ids }) => useServices(ids),
|
||||
{ initialProps: { ids: ['1'] } }
|
||||
);
|
||||
|
||||
await waitFor(() => {
|
||||
expect(result.current.loading).toBe(false);
|
||||
});
|
||||
|
||||
expect(contentService.getServices).toHaveBeenCalledTimes(1);
|
||||
|
||||
rerender({ ids: ['2'] });
|
||||
|
||||
await waitFor(() => {
|
||||
expect(contentService.getServices).toHaveBeenCalledTimes(2);
|
||||
});
|
||||
});
|
||||
|
||||
it('应该处理空数组ids', async () => {
|
||||
(contentService.getServices as jest.Mock).mockResolvedValue(mockServicesData);
|
||||
|
||||
const { result } = renderHook(() => useServices([]));
|
||||
|
||||
await waitFor(() => {
|
||||
expect(result.current.loading).toBe(false);
|
||||
});
|
||||
|
||||
expect(result.current.services).toEqual(mockServicesData);
|
||||
expect(contentService.getServices).toHaveBeenCalledWith([]);
|
||||
});
|
||||
});
|
||||
Reference in New Issue
Block a user