|
|
@@ -0,0 +1,602 @@
|
|
|
+import React from 'react';
|
|
|
+import { describe, it, expect, vi, beforeEach } from 'vitest';
|
|
|
+import { render, screen, waitFor, fireEvent } from '@testing-library/react';
|
|
|
+import { QueryClient, QueryClientProvider } from '@tanstack/react-query';
|
|
|
+import { BrowserRouter } from 'react-router';
|
|
|
+import { GoodsCategoryTreeManagement } from '../../src/components/GoodsCategoryTreeManagement';
|
|
|
+import { goodsCategoryClient } from '../../src/api/goodsCategoryClient';
|
|
|
+
|
|
|
+// 完整的mock响应对象 - 按照用户UI包规范
|
|
|
+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 goodsCategoryClient - 按照用户UI包规范
|
|
|
+vi.mock('../../src/api/goodsCategoryClient', () => {
|
|
|
+ const mockGoodsCategoryClient = {
|
|
|
+ index: {
|
|
|
+ $get: vi.fn(() => Promise.resolve({ status: 200, body: null })),
|
|
|
+ $post: vi.fn(() => Promise.resolve({ status: 201, body: null })),
|
|
|
+ },
|
|
|
+ ':id': {
|
|
|
+ $put: vi.fn(() => Promise.resolve({ status: 200, body: null })),
|
|
|
+ $delete: vi.fn(() => Promise.resolve({ status: 204, body: null })),
|
|
|
+ },
|
|
|
+ };
|
|
|
+
|
|
|
+ const mockGoodsCategoryClientManager = {
|
|
|
+ get: vi.fn(() => mockGoodsCategoryClient),
|
|
|
+ };
|
|
|
+
|
|
|
+ return {
|
|
|
+ goodsCategoryClientManager: mockGoodsCategoryClientManager,
|
|
|
+ goodsCategoryClient: mockGoodsCategoryClient,
|
|
|
+ };
|
|
|
+});
|
|
|
+
|
|
|
+// Mock sonner toast
|
|
|
+vi.mock('sonner', () => ({
|
|
|
+ toast: {
|
|
|
+ success: vi.fn(),
|
|
|
+ error: vi.fn()
|
|
|
+ }
|
|
|
+}));
|
|
|
+
|
|
|
+// Mock FileSelector
|
|
|
+vi.mock('@d8d/file-management-ui-mt', () => ({
|
|
|
+ FileSelector: ({ value, onChange }: { value?: number; onChange?: (value: number) => void }) => (
|
|
|
+ <div data-testid="file-selector">
|
|
|
+ <button onClick={() => onChange?.(1)}>选择文件</button>
|
|
|
+ <span>当前文件ID: {value}</span>
|
|
|
+ </div>
|
|
|
+ )
|
|
|
+}));
|
|
|
+
|
|
|
+// Test wrapper component
|
|
|
+const TestWrapper: React.FC<{ children: React.ReactNode }> = ({ children }) => {
|
|
|
+ const queryClient = new QueryClient({
|
|
|
+ defaultOptions: {
|
|
|
+ queries: {
|
|
|
+ retry: false,
|
|
|
+ },
|
|
|
+ },
|
|
|
+ });
|
|
|
+
|
|
|
+ return (
|
|
|
+ <BrowserRouter>
|
|
|
+ <QueryClientProvider client={queryClient}>
|
|
|
+ {children}
|
|
|
+ </QueryClientProvider>
|
|
|
+ </BrowserRouter>
|
|
|
+ );
|
|
|
+};
|
|
|
+
|
|
|
+describe('商品分类树形管理集成测试', () => {
|
|
|
+ beforeEach(() => {
|
|
|
+ vi.clearAllMocks();
|
|
|
+ });
|
|
|
+
|
|
|
+ it('应该渲染商品分类树形管理组件并显示标题', async () => {
|
|
|
+ // Mock successful API response for top level data
|
|
|
+ (goodsCategoryClient.index.$get as any).mockResolvedValueOnce(createMockResponse(200, {
|
|
|
+ data: [
|
|
|
+ {
|
|
|
+ id: 1,
|
|
|
+ tenantId: 1,
|
|
|
+ name: '电子产品',
|
|
|
+ parentId: 0,
|
|
|
+ level: 0,
|
|
|
+ state: 1,
|
|
|
+ imageFileId: null
|
|
|
+ }
|
|
|
+ ]
|
|
|
+ }));
|
|
|
+
|
|
|
+ render(
|
|
|
+ <TestWrapper>
|
|
|
+ <GoodsCategoryTreeManagement />
|
|
|
+ </TestWrapper>
|
|
|
+ );
|
|
|
+
|
|
|
+ // Check if title is rendered
|
|
|
+ expect(screen.getByText('商品分类树形管理')).toBeInTheDocument();
|
|
|
+ expect(screen.getByText('异步加载树形结构,高效管理商品分类数据')).toBeInTheDocument();
|
|
|
+
|
|
|
+ // Wait for loading to complete
|
|
|
+ await waitFor(() => {
|
|
|
+ expect(screen.getByText('电子产品')).toBeInTheDocument();
|
|
|
+ });
|
|
|
+ });
|
|
|
+
|
|
|
+ it('应该在获取数据时显示加载状态', async () => {
|
|
|
+ // Mock delayed API response
|
|
|
+ (goodsCategoryClient.index.$get as any).mockImplementationOnce(() =>
|
|
|
+ new Promise(resolve => setTimeout(() => resolve(createMockResponse(200, { data: [] })), 100))
|
|
|
+ );
|
|
|
+
|
|
|
+ render(
|
|
|
+ <TestWrapper>
|
|
|
+ <GoodsCategoryTreeManagement />
|
|
|
+ </TestWrapper>
|
|
|
+ );
|
|
|
+
|
|
|
+ // Check if loading state is shown
|
|
|
+ expect(screen.getByText('加载中...')).toBeInTheDocument();
|
|
|
+
|
|
|
+ // Wait for loading to complete
|
|
|
+ await waitFor(() => {
|
|
|
+ expect(screen.queryByText('加载中...')).not.toBeInTheDocument();
|
|
|
+ });
|
|
|
+ });
|
|
|
+
|
|
|
+ it('应该在无数据时显示空状态', async () => {
|
|
|
+ // Mock empty API response
|
|
|
+ (goodsCategoryClient.index.$get as any).mockResolvedValueOnce(createMockResponse(200, { data: [] }));
|
|
|
+
|
|
|
+ render(
|
|
|
+ <TestWrapper>
|
|
|
+ <GoodsCategoryTreeManagement />
|
|
|
+ </TestWrapper>
|
|
|
+ );
|
|
|
+
|
|
|
+ // Wait for empty state to appear
|
|
|
+ await waitFor(() => {
|
|
|
+ expect(screen.getByText('暂无数据')).toBeInTheDocument();
|
|
|
+ });
|
|
|
+ });
|
|
|
+
|
|
|
+ it('应该在点击新增按钮时打开创建对话框', async () => {
|
|
|
+ // Mock successful API response
|
|
|
+ (goodsCategoryClient.index.$get as any).mockResolvedValueOnce(createMockResponse(200, {
|
|
|
+ data: [
|
|
|
+ {
|
|
|
+ id: 1,
|
|
|
+ tenantId: 1,
|
|
|
+ name: '电子产品',
|
|
|
+ parentId: 0,
|
|
|
+ level: 0,
|
|
|
+ state: 1,
|
|
|
+ imageFileId: null
|
|
|
+ }
|
|
|
+ ]
|
|
|
+ }));
|
|
|
+
|
|
|
+ render(
|
|
|
+ <TestWrapper>
|
|
|
+ <GoodsCategoryTreeManagement />
|
|
|
+ </TestWrapper>
|
|
|
+ );
|
|
|
+
|
|
|
+ // Wait for data to load
|
|
|
+ await waitFor(() => {
|
|
|
+ expect(screen.getByText('电子产品')).toBeInTheDocument();
|
|
|
+ });
|
|
|
+
|
|
|
+ // Click add button
|
|
|
+ const addButton = screen.getByText('新增顶级分类');
|
|
|
+ fireEvent.click(addButton);
|
|
|
+
|
|
|
+ // Check if dialog opens
|
|
|
+ await waitFor(() => {
|
|
|
+ expect(screen.getByRole('heading', { name: '新增顶级分类' })).toBeInTheDocument();
|
|
|
+ expect(screen.getByText('填写顶级分类信息')).toBeInTheDocument();
|
|
|
+ });
|
|
|
+ });
|
|
|
+
|
|
|
+ it('应该优雅地处理API错误', async () => {
|
|
|
+ // Mock API error
|
|
|
+ (goodsCategoryClient.index.$get as any).mockRejectedValueOnce(new Error('API Error'));
|
|
|
+
|
|
|
+ render(
|
|
|
+ <TestWrapper>
|
|
|
+ <GoodsCategoryTreeManagement />
|
|
|
+ </TestWrapper>
|
|
|
+ );
|
|
|
+
|
|
|
+ // Wait for error state
|
|
|
+ await waitFor(() => {
|
|
|
+ // Component should handle errors gracefully
|
|
|
+ expect(screen.getByText('商品分类树形管理')).toBeInTheDocument();
|
|
|
+ });
|
|
|
+ });
|
|
|
+
|
|
|
+ it('应该完成创建和删除工作流程', async () => {
|
|
|
+ const { toast } = await import('sonner');
|
|
|
+
|
|
|
+ // Mock initial categories data
|
|
|
+ const mockCategories = {
|
|
|
+ data: [
|
|
|
+ {
|
|
|
+ id: 1,
|
|
|
+ tenantId: 1,
|
|
|
+ name: '电子产品',
|
|
|
+ parentId: 0,
|
|
|
+ level: 0,
|
|
|
+ state: 1,
|
|
|
+ imageFileId: null
|
|
|
+ }
|
|
|
+ ]
|
|
|
+ };
|
|
|
+
|
|
|
+ // Mock initial data fetch
|
|
|
+ (goodsCategoryClient.index.$get as any).mockResolvedValue(createMockResponse(200, mockCategories));
|
|
|
+
|
|
|
+ render(
|
|
|
+ <TestWrapper>
|
|
|
+ <GoodsCategoryTreeManagement />
|
|
|
+ </TestWrapper>
|
|
|
+ );
|
|
|
+
|
|
|
+ // Wait for initial data to load
|
|
|
+ await waitFor(() => {
|
|
|
+ expect(screen.getByText('电子产品')).toBeInTheDocument();
|
|
|
+ });
|
|
|
+
|
|
|
+ // Test create category
|
|
|
+ const addButton = screen.getByText('新增顶级分类');
|
|
|
+ fireEvent.click(addButton);
|
|
|
+
|
|
|
+ // Wait for create dialog
|
|
|
+ await waitFor(() => {
|
|
|
+ expect(screen.getByRole('heading', { name: '新增顶级分类' })).toBeInTheDocument();
|
|
|
+ });
|
|
|
+
|
|
|
+ // Fill create form
|
|
|
+ const nameInput = screen.getByPlaceholderText('输入分类名称');
|
|
|
+ fireEvent.change(nameInput, { target: { value: '服装' } });
|
|
|
+
|
|
|
+ // Mock successful creation
|
|
|
+ (goodsCategoryClient.index.$post as any).mockResolvedValue(createMockResponse(201, { id: 2, name: '服装' }));
|
|
|
+
|
|
|
+ const submitButton = screen.getByText('创建');
|
|
|
+ fireEvent.click(submitButton);
|
|
|
+
|
|
|
+ await waitFor(() => {
|
|
|
+ expect(goodsCategoryClient.index.$post).toHaveBeenCalledWith({
|
|
|
+ json: {
|
|
|
+ tenantId: 1,
|
|
|
+ name: '服装',
|
|
|
+ parentId: 0,
|
|
|
+ level: 0,
|
|
|
+ state: 1,
|
|
|
+ imageFileId: null
|
|
|
+ }
|
|
|
+ });
|
|
|
+ expect(toast.success).toHaveBeenCalledWith('商品分类创建成功');
|
|
|
+ });
|
|
|
+
|
|
|
+ // Test delete category
|
|
|
+ const deleteButtons = screen.getAllByRole('button', { name: '删除' });
|
|
|
+ fireEvent.click(deleteButtons[0]);
|
|
|
+
|
|
|
+ // Confirm deletion
|
|
|
+ expect(screen.getByRole('heading', { name: '确认删除' })).toBeInTheDocument();
|
|
|
+
|
|
|
+ // Mock successful deletion
|
|
|
+ (goodsCategoryClient[':id']['$delete'] as any).mockResolvedValue({
|
|
|
+ status: 204,
|
|
|
+ });
|
|
|
+
|
|
|
+ // 查找删除确认按钮
|
|
|
+ const confirmDeleteButton = screen.getByRole('button', { name: '确认删除' });
|
|
|
+ fireEvent.click(confirmDeleteButton);
|
|
|
+
|
|
|
+ await waitFor(() => {
|
|
|
+ expect(goodsCategoryClient[':id']['$delete']).toHaveBeenCalledWith({
|
|
|
+ param: { id: 1 },
|
|
|
+ });
|
|
|
+ expect(toast.success).toHaveBeenCalledWith('商品分类删除成功');
|
|
|
+ });
|
|
|
+ });
|
|
|
+
|
|
|
+ it('应该处理CRUD操作中的API错误', async () => {
|
|
|
+ const { goodsCategoryClient } = await import('../../src/api/goodsCategoryClient');
|
|
|
+ const { toast } = await import('sonner');
|
|
|
+
|
|
|
+ // Mock initial data
|
|
|
+ const mockCategories = {
|
|
|
+ data: [
|
|
|
+ {
|
|
|
+ id: 1,
|
|
|
+ tenantId: 1,
|
|
|
+ name: '电子产品',
|
|
|
+ parentId: 0,
|
|
|
+ level: 0,
|
|
|
+ state: 1,
|
|
|
+ imageFileId: null
|
|
|
+ }
|
|
|
+ ]
|
|
|
+ };
|
|
|
+
|
|
|
+ (goodsCategoryClient.index.$get as any).mockResolvedValue(createMockResponse(200, mockCategories));
|
|
|
+
|
|
|
+ render(
|
|
|
+ <TestWrapper>
|
|
|
+ <GoodsCategoryTreeManagement />
|
|
|
+ </TestWrapper>
|
|
|
+ );
|
|
|
+
|
|
|
+ // Wait for data to load
|
|
|
+ await waitFor(() => {
|
|
|
+ expect(screen.getByText('电子产品')).toBeInTheDocument();
|
|
|
+ });
|
|
|
+
|
|
|
+ // Test create category error
|
|
|
+ const addButton = screen.getByText('新增顶级分类');
|
|
|
+ fireEvent.click(addButton);
|
|
|
+
|
|
|
+ await waitFor(() => {
|
|
|
+ expect(screen.getByRole('heading', { name: '新增顶级分类' })).toBeInTheDocument();
|
|
|
+ });
|
|
|
+
|
|
|
+ const nameInput = screen.getByPlaceholderText('输入分类名称');
|
|
|
+ fireEvent.change(nameInput, { target: { value: '服装' } });
|
|
|
+
|
|
|
+ // Mock creation error
|
|
|
+ (goodsCategoryClient.index.$post as any).mockRejectedValue(new Error('Creation failed'));
|
|
|
+
|
|
|
+ const submitButton = screen.getByText('创建');
|
|
|
+ fireEvent.click(submitButton);
|
|
|
+
|
|
|
+ await waitFor(() => {
|
|
|
+ expect(toast.error).toHaveBeenCalledWith('创建失败,请重试');
|
|
|
+ });
|
|
|
+ });
|
|
|
+
|
|
|
+ it('应该支持多级分类层级(顶级→一级→二级→三级)', async () => {
|
|
|
+ // Mock initial data with all levels
|
|
|
+ const mockCategories = {
|
|
|
+ data: [
|
|
|
+ {
|
|
|
+ id: 1,
|
|
|
+ tenantId: 1,
|
|
|
+ name: '电子产品',
|
|
|
+ parentId: 0,
|
|
|
+ level: 0,
|
|
|
+ state: 1,
|
|
|
+ imageFileId: null
|
|
|
+ },
|
|
|
+ {
|
|
|
+ id: 2,
|
|
|
+ tenantId: 1,
|
|
|
+ name: '手机',
|
|
|
+ parentId: 1,
|
|
|
+ level: 1,
|
|
|
+ state: 1,
|
|
|
+ imageFileId: null
|
|
|
+ },
|
|
|
+ {
|
|
|
+ id: 3,
|
|
|
+ tenantId: 1,
|
|
|
+ name: '智能手机',
|
|
|
+ parentId: 2,
|
|
|
+ level: 2,
|
|
|
+ state: 1,
|
|
|
+ imageFileId: null
|
|
|
+ },
|
|
|
+ {
|
|
|
+ id: 4,
|
|
|
+ tenantId: 1,
|
|
|
+ name: '苹果手机',
|
|
|
+ parentId: 3,
|
|
|
+ level: 3,
|
|
|
+ state: 1,
|
|
|
+ imageFileId: null
|
|
|
+ }
|
|
|
+ ]
|
|
|
+ };
|
|
|
+
|
|
|
+ (goodsCategoryClient.index.$get as any).mockResolvedValue(createMockResponse(200, mockCategories));
|
|
|
+
|
|
|
+ render(
|
|
|
+ <TestWrapper>
|
|
|
+ <GoodsCategoryTreeManagement />
|
|
|
+ </TestWrapper>
|
|
|
+ );
|
|
|
+
|
|
|
+ // Wait for all level data to load
|
|
|
+ await waitFor(() => {
|
|
|
+ expect(screen.getByText('电子产品')).toBeInTheDocument();
|
|
|
+ expect(screen.getByText('手机')).toBeInTheDocument();
|
|
|
+ expect(screen.getByText('智能手机')).toBeInTheDocument();
|
|
|
+ expect(screen.getByText('苹果手机')).toBeInTheDocument();
|
|
|
+ });
|
|
|
+
|
|
|
+ // Verify all levels are displayed correctly
|
|
|
+ expect(screen.getByText('电子产品')).toBeInTheDocument();
|
|
|
+ expect(screen.getByText('手机')).toBeInTheDocument();
|
|
|
+ expect(screen.getByText('智能手机')).toBeInTheDocument();
|
|
|
+ expect(screen.getByText('苹果手机')).toBeInTheDocument();
|
|
|
+ });
|
|
|
+
|
|
|
+ it('应该成功创建多级分类(三级分类)', async () => {
|
|
|
+ const { toast } = await import('sonner');
|
|
|
+
|
|
|
+ // Mock initial data with level 2 category
|
|
|
+ const mockCategories = {
|
|
|
+ data: [
|
|
|
+ {
|
|
|
+ id: 3,
|
|
|
+ tenantId: 1,
|
|
|
+ name: '智能手机',
|
|
|
+ parentId: 2,
|
|
|
+ level: 2,
|
|
|
+ state: 1,
|
|
|
+ imageFileId: null
|
|
|
+ }
|
|
|
+ ]
|
|
|
+ };
|
|
|
+
|
|
|
+ (goodsCategoryClient.index.$get as any).mockResolvedValue(createMockResponse(200, mockCategories));
|
|
|
+
|
|
|
+ render(
|
|
|
+ <TestWrapper>
|
|
|
+ <GoodsCategoryTreeManagement />
|
|
|
+ </TestWrapper>
|
|
|
+ );
|
|
|
+
|
|
|
+ // Wait for data to load
|
|
|
+ await waitFor(() => {
|
|
|
+ expect(screen.getByText('智能手机')).toBeInTheDocument();
|
|
|
+ });
|
|
|
+
|
|
|
+ // Click add child button for level 2 category
|
|
|
+ const addChildButtons = screen.getAllByRole('button', { name: '新增子分类' });
|
|
|
+ fireEvent.click(addChildButtons[0]);
|
|
|
+
|
|
|
+ // Check if child creation dialog opens
|
|
|
+ await waitFor(() => {
|
|
|
+ expect(screen.getByRole('heading', { name: '新增子分类' })).toBeInTheDocument();
|
|
|
+ expect(screen.getByText('在分类 "智能手机" 下新增子分类')).toBeInTheDocument();
|
|
|
+ });
|
|
|
+
|
|
|
+ // Fill child form
|
|
|
+ const nameInput = screen.getByPlaceholderText('输入分类名称');
|
|
|
+ fireEvent.change(nameInput, { target: { value: '苹果手机' } });
|
|
|
+
|
|
|
+ // Mock successful child creation
|
|
|
+ (goodsCategoryClient.index.$post as any).mockResolvedValue(createMockResponse(201, { id: 4, name: '苹果手机' }));
|
|
|
+
|
|
|
+ const submitButton = screen.getByText('创建');
|
|
|
+ fireEvent.click(submitButton);
|
|
|
+
|
|
|
+ await waitFor(() => {
|
|
|
+ expect(goodsCategoryClient.index.$post).toHaveBeenCalledWith({
|
|
|
+ json: {
|
|
|
+ tenantId: 1,
|
|
|
+ name: '苹果手机',
|
|
|
+ parentId: 3,
|
|
|
+ level: 3,
|
|
|
+ state: 1,
|
|
|
+ imageFileId: null
|
|
|
+ }
|
|
|
+ });
|
|
|
+ expect(toast.success).toHaveBeenCalledWith('商品分类创建成功');
|
|
|
+ });
|
|
|
+ });
|
|
|
+
|
|
|
+ it('应该处理分类状态切换', async () => {
|
|
|
+ const { toast } = await import('sonner');
|
|
|
+
|
|
|
+ // Mock initial data with category
|
|
|
+ const mockCategories = {
|
|
|
+ data: [
|
|
|
+ {
|
|
|
+ id: 4,
|
|
|
+ tenantId: 1,
|
|
|
+ name: '苹果手机',
|
|
|
+ parentId: 3,
|
|
|
+ level: 3,
|
|
|
+ state: 1,
|
|
|
+ imageFileId: null
|
|
|
+ }
|
|
|
+ ]
|
|
|
+ };
|
|
|
+
|
|
|
+ (goodsCategoryClient.index.$get as any).mockResolvedValue(createMockResponse(200, mockCategories));
|
|
|
+
|
|
|
+ render(
|
|
|
+ <TestWrapper>
|
|
|
+ <GoodsCategoryTreeManagement />
|
|
|
+ </TestWrapper>
|
|
|
+ );
|
|
|
+
|
|
|
+ // Wait for data to load
|
|
|
+ await waitFor(() => {
|
|
|
+ expect(screen.getByText('苹果手机')).toBeInTheDocument();
|
|
|
+ });
|
|
|
+
|
|
|
+ // Click toggle status button for category
|
|
|
+ const toggleButtons = screen.getAllByRole('button', { name: '禁用' });
|
|
|
+ fireEvent.click(toggleButtons[0]);
|
|
|
+
|
|
|
+ // Check if status toggle dialog opens
|
|
|
+ await waitFor(() => {
|
|
|
+ expect(screen.getByRole('heading', { name: '禁用确认' })).toBeInTheDocument();
|
|
|
+ expect(screen.getByText('确定要禁用商品分类 "苹果手机" 吗?')).toBeInTheDocument();
|
|
|
+ });
|
|
|
+
|
|
|
+ // Mock successful status toggle
|
|
|
+ (goodsCategoryClient[':id'].$put as any).mockResolvedValue(createMockResponse(200));
|
|
|
+
|
|
|
+ const confirmButton = screen.getByRole('button', { name: '确认' });
|
|
|
+ fireEvent.click(confirmButton);
|
|
|
+
|
|
|
+ await waitFor(() => {
|
|
|
+ expect(goodsCategoryClient[':id'].$put).toHaveBeenCalledWith({
|
|
|
+ param: { id: 4 },
|
|
|
+ json: { state: 2 }
|
|
|
+ });
|
|
|
+ expect(toast.success).toHaveBeenCalledWith('商品分类禁用成功');
|
|
|
+ });
|
|
|
+ });
|
|
|
+
|
|
|
+ it('应该成功删除多级分类(三级分类)', async () => {
|
|
|
+ const { toast } = await import('sonner');
|
|
|
+
|
|
|
+ // Mock initial data with level 3 category
|
|
|
+ const mockCategories = {
|
|
|
+ data: [
|
|
|
+ {
|
|
|
+ id: 4,
|
|
|
+ tenantId: 1,
|
|
|
+ name: '苹果手机',
|
|
|
+ parentId: 3,
|
|
|
+ level: 3,
|
|
|
+ state: 1,
|
|
|
+ imageFileId: null
|
|
|
+ }
|
|
|
+ ]
|
|
|
+ };
|
|
|
+
|
|
|
+ (goodsCategoryClient.index.$get as any).mockResolvedValue(createMockResponse(200, mockCategories));
|
|
|
+
|
|
|
+ render(
|
|
|
+ <TestWrapper>
|
|
|
+ <GoodsCategoryTreeManagement />
|
|
|
+ </TestWrapper>
|
|
|
+ );
|
|
|
+
|
|
|
+ // Wait for data to load
|
|
|
+ await waitFor(() => {
|
|
|
+ expect(screen.getByText('苹果手机')).toBeInTheDocument();
|
|
|
+ });
|
|
|
+
|
|
|
+ // Click delete button for category
|
|
|
+ const deleteButtons = screen.getAllByRole('button', { name: '删除' });
|
|
|
+ fireEvent.click(deleteButtons[0]);
|
|
|
+
|
|
|
+ // Check if delete confirmation dialog opens
|
|
|
+ await waitFor(() => {
|
|
|
+ expect(screen.getByRole('heading', { name: '确认删除' })).toBeInTheDocument();
|
|
|
+ expect(screen.getByText('确定要删除商品分类 "苹果手机" 吗?此操作不可恢复。')).toBeInTheDocument();
|
|
|
+ });
|
|
|
+
|
|
|
+ // Mock successful deletion
|
|
|
+ (goodsCategoryClient[':id'].$delete as any).mockResolvedValue(createMockResponse(204));
|
|
|
+
|
|
|
+ const confirmDeleteButton = screen.getByRole('button', { name: '确认删除' });
|
|
|
+ fireEvent.click(confirmDeleteButton);
|
|
|
+
|
|
|
+ await waitFor(() => {
|
|
|
+ expect(goodsCategoryClient[':id'].$delete).toHaveBeenCalledWith({
|
|
|
+ param: { id: 4 }
|
|
|
+ });
|
|
|
+ expect(toast.success).toHaveBeenCalledWith('商品分类删除成功');
|
|
|
+ });
|
|
|
+ });
|
|
|
+});
|