| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348 |
- import { describe, it, expect, vi, beforeEach } from 'vitest';
- import { render, screen, fireEvent, waitFor } from '@testing-library/react';
- import { QueryClient, QueryClientProvider } from '@tanstack/react-query';
- import { AdvertisementManagement } from '../../src/components/AdvertisementManagement';
- import { advertisementClientManager } from '../../src/api/advertisementClient';
- // 完整的mock响应对象
- const createMockResponse = (status: number, data?: any) => ({
- status,
- ok: status >= 200 && status < 300,
- body: null,
- bodyUsed: false,
- statusText: status === 200 ? 'OK' : status === 201 ? 'Created' : status === 204 ? 'No Content' : 'Error',
- headers: new Headers(),
- url: '',
- redirected: false,
- type: 'basic' as ResponseType,
- json: async () => data || {},
- text: async () => '',
- blob: async () => new Blob(),
- arrayBuffer: async () => new ArrayBuffer(0),
- formData: async () => new FormData(),
- clone: function() { return this; }
- });
- // Mock API client
- vi.mock('../../src/api/advertisementClient', () => {
- const mockAdvertisementClient = {
- index: {
- $get: vi.fn(() => Promise.resolve(createMockResponse(200, {
- data: [
- {
- id: 1,
- title: '测试广告',
- alias: 'test-ad',
- typeId: 1,
- imageUrl: 'https://example.com/image.jpg',
- linkUrl: 'https://example.com',
- status: 1,
- sortOrder: 1,
- remark: '测试备注',
- createdAt: '2024-01-01T00:00:00Z',
- updatedAt: '2024-01-01T00:00:00Z',
- createdBy: 1,
- updatedBy: 1
- }
- ],
- pagination: {
- page: 1,
- pageSize: 10,
- total: 1,
- totalPages: 1
- }
- }))),
- $post: vi.fn(() => Promise.resolve(createMockResponse(201, { id: 2, title: '新广告' }))),
- },
- ':id': {
- $put: vi.fn(() => Promise.resolve(createMockResponse(200, { id: 1, title: '更新后的广告' }))),
- $delete: vi.fn(() => Promise.resolve(createMockResponse(204))),
- },
- };
- const mockAdvertisementClientManager = {
- get: vi.fn(() => mockAdvertisementClient),
- };
- return {
- advertisementClientManager: mockAdvertisementClientManager,
- advertisementClient: mockAdvertisementClient,
- };
- });
- // Mock toast
- vi.mock('sonner', () => ({
- toast: {
- success: vi.fn(() => {}),
- error: vi.fn(() => {}),
- },
- }));
- // Mock FileSelector
- vi.mock('@d8d/file-management-ui', () => ({
- FileSelector: ({ value, onChange, testId, maxSize, uploadPath, previewSize, filterType, ...props }: any) => (
- <div data-testid="file-selector">
- <input
- type="number"
- value={value || ''}
- onChange={(e) => onChange?.(parseInt(e.target.value))}
- data-testid="file-selector-input"
- {...props}
- />
- </div>
- ),
- }));
- // Mock AdvertisementTypeSelector
- vi.mock('@d8d/advertisement-type-management-ui', () => ({
- AdvertisementTypeSelector: ({ value, onChange, testId, ...props }: any) => (
- <div data-testid="advertisement-type-selector">
- <select
- value={value?.toString() || ''}
- onChange={(e) => onChange?.(parseInt(e.target.value))}
- data-testid="type-selector"
- {...props}
- >
- <option value="1">首页轮播</option>
- <option value="2">侧边栏广告</option>
- </select>
- </div>
- ),
- }));
- const createTestQueryClient = () =>
- new QueryClient({
- defaultOptions: {
- queries: {
- retry: false,
- },
- },
- });
- const renderWithProviders = (component: React.ReactElement) => {
- const queryClient = createTestQueryClient();
- return render(
- <QueryClientProvider client={queryClient}>
- {component as any}
- </QueryClientProvider>
- );
- };
- describe('广告管理集成测试', () => {
- beforeEach(() => {
- vi.clearAllMocks();
- });
- it('应该完成完整的广告CRUD流程', async () => {
- const mockAdvertisements = {
- data: [
- {
- id: 1,
- title: '测试广告',
- code: 'test-ad',
- typeId: 1,
- advertisementType: { id: 1, name: '首页轮播' },
- url: 'https://example.com',
- imageFileId: 1,
- imageFile: { id: 1, fullUrl: 'https://example.com/image.jpg' },
- sort: 1,
- status: 1,
- actionType: 1,
- createdAt: '2024-01-01T00:00:00Z',
- },
- ],
- pagination: {
- total: 1,
- page: 1,
- pageSize: 10,
- },
- };
- const { toast } = await import('sonner');
- // Mock initial advertisement list
- const client = advertisementClientManager.get();
- (client.index.$get as any).mockResolvedValue({
- ...createMockResponse(200),
- json: async () => mockAdvertisements
- });
- renderWithProviders(<AdvertisementManagement />);
- // Wait for initial data to load
- await waitFor(() => {
- expect(screen.getByText('测试广告')).toBeInTheDocument();
- });
- // Test create advertisement
- const createButton = screen.getByText('创建广告');
- fireEvent.click(createButton);
- // Fill create form
- const titleInput = screen.getByTestId('title-input');
- const codeInput = screen.getByTestId('code-input');
- const typeSelector = screen.getByTestId('type-selector');
- fireEvent.change(titleInput, { target: { value: '新广告' } });
- fireEvent.change(codeInput, { target: { value: 'new-ad' } });
- fireEvent.change(typeSelector, { target: { value: '1' } });
- // Mock successful creation
- (client.index.$post as any).mockResolvedValue(createMockResponse(201, { id: 2, title: '新广告' }));
- const submitButton = screen.getByTestId('create-submit-button');
- fireEvent.click(submitButton);
- await waitFor(() => {
- expect(client.index.$post).toHaveBeenCalledWith({
- json: {
- title: '新广告',
- code: 'new-ad',
- typeId: 1,
- url: '',
- imageFileId: undefined,
- sort: 0,
- status: 1,
- actionType: 1
- },
- });
- expect(toast.success).toHaveBeenCalledWith('广告创建成功');
- });
- // Test edit advertisement
- const editButton = screen.getByTestId('edit-button-1');
- fireEvent.click(editButton);
- // Verify edit form is populated
- await waitFor(() => {
- expect(screen.getByDisplayValue('测试广告')).toBeInTheDocument();
- });
- // Update advertisement
- const updateTitleInput = screen.getByDisplayValue('测试广告');
- fireEvent.change(updateTitleInput, { target: { value: '更新后的广告' } });
- // Mock successful update
- (client[':id']['$put'] as any).mockResolvedValue(createMockResponse(200));
- const updateButton = screen.getByTestId('update-submit-button');
- fireEvent.click(updateButton);
- await waitFor(() => {
- expect(client[':id']['$put']).toHaveBeenCalledWith({
- param: { id: 1 },
- json: {
- title: '更新后的广告',
- typeId: 1,
- code: 'test-ad',
- url: 'https://example.com',
- imageFileId: 1,
- sort: 1,
- status: 1,
- actionType: 1
- },
- });
- expect(toast.success).toHaveBeenCalledWith('广告更新成功');
- });
- // Test delete advertisement
- const deleteButton = screen.getByTestId('delete-button-1');
- fireEvent.click(deleteButton);
- // Confirm deletion
- expect(screen.getByText('确认删除')).toBeInTheDocument();
- // Mock successful deletion
- (client[':id']['$delete'] as any).mockResolvedValue({
- status: 204,
- ok: true,
- body: null,
- bodyUsed: false,
- statusText: 'No Content',
- headers: new Headers(),
- url: '',
- redirected: false,
- type: 'basic' as ResponseType,
- json: async () => ({}),
- text: async () => '',
- blob: async () => new Blob(),
- arrayBuffer: async () => new ArrayBuffer(0),
- formData: async () => new FormData(),
- clone: function() { return this; }
- });
- const confirmDeleteButton = screen.getByTestId('confirm-delete-button');
- fireEvent.click(confirmDeleteButton);
- await waitFor(() => {
- expect(client[':id']['$delete']).toHaveBeenCalledWith({
- param: { id: 1 },
- });
- expect(toast.success).toHaveBeenCalledWith('广告删除成功');
- });
- });
- it('应该优雅处理API错误', async () => {
- const client = advertisementClientManager.get();
- const { toast } = await import('sonner');
- // Mock API error
- (client.index.$get as any).mockRejectedValue(new Error('API Error'));
- renderWithProviders(<AdvertisementManagement />);
- // Should handle error without crashing
- await waitFor(() => {
- expect(screen.getByText('广告管理')).toBeInTheDocument();
- });
- // Test create advertisement error
- const createButton = screen.getByText('创建广告');
- fireEvent.click(createButton);
- const titleInput = screen.getByTestId('title-input');
- const codeInput = screen.getByTestId('code-input');
- fireEvent.change(titleInput, { target: { value: '测试广告' } });
- fireEvent.change(codeInput, { target: { value: 'test-ad' } });
- // Mock creation error
- (client.index.$post as any).mockRejectedValue(new Error('Creation failed'));
- const submitButton = screen.getByTestId('create-submit-button');
- fireEvent.click(submitButton);
- await waitFor(() => {
- expect(toast.error).toHaveBeenCalledWith('Creation failed');
- });
- });
- it('应该处理搜索功能', async () => {
- const client = advertisementClientManager.get();
- const mockAdvertisements = {
- data: [],
- pagination: { total: 0, page: 1, pageSize: 10 },
- };
- (client.index.$get as any).mockResolvedValue(createMockResponse(200, mockAdvertisements));
- renderWithProviders(<AdvertisementManagement />);
- // Test search
- const searchInput = screen.getByTestId('search-input');
- fireEvent.change(searchInput, { target: { value: '搜索关键词' } });
- const searchButton = screen.getByText('搜索');
- fireEvent.click(searchButton);
- await waitFor(() => {
- expect(client.index.$get).toHaveBeenCalledWith({
- query: {
- page: 1,
- pageSize: 10,
- keyword: '搜索关键词',
- },
- });
- });
- });
- });
|