|
|
@@ -1,6 +1,7 @@
|
|
|
import React from 'react';
|
|
|
import { describe, it, expect, vi, beforeEach } from 'vitest';
|
|
|
import { render, screen, fireEvent, waitFor } from '@testing-library/react';
|
|
|
+import userEvent from '@testing-library/user-event';
|
|
|
import { QueryClient, QueryClientProvider, useQueryClient } from '@tanstack/react-query';
|
|
|
import { toast } from 'sonner';
|
|
|
|
|
|
@@ -12,6 +13,9 @@ vi.mock('sonner', () => ({
|
|
|
}
|
|
|
}));
|
|
|
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
// Mock lucide-react icons used by the component
|
|
|
vi.mock('lucide-react', async () => {
|
|
|
const actual = await vi.importActual('lucide-react');
|
|
|
@@ -24,6 +28,8 @@ vi.mock('lucide-react', async () => {
|
|
|
};
|
|
|
});
|
|
|
|
|
|
+
|
|
|
+
|
|
|
// 创建模拟的rpcClient函数(根据API模拟规范)
|
|
|
// 符合测试策略文档的API模拟规范:统一模拟@d8d/shared-ui-components/utils/hc中的rpcClient函数
|
|
|
const mockRpcClient = vi.hoisted(() => vi.fn((aptBaseUrl: string) => {
|
|
|
@@ -234,7 +240,8 @@ describe('GoodsParentChildPanel', () => {
|
|
|
expect(toast.success).toHaveBeenCalledWith('已解除父子关系');
|
|
|
});
|
|
|
|
|
|
- it('应该切换到批量创建标签页', () => {
|
|
|
+ it('应该切换到批量创建标签页', async () => {
|
|
|
+ const user = userEvent.setup();
|
|
|
render(
|
|
|
<GoodsParentChildPanel
|
|
|
{...defaultProps}
|
|
|
@@ -246,13 +253,15 @@ describe('GoodsParentChildPanel', () => {
|
|
|
|
|
|
const batchCreateTabs = screen.getAllByText('批量创建');
|
|
|
expect(batchCreateTabs.length).toBeGreaterThan(0);
|
|
|
- fireEvent.click(batchCreateTabs[0]);
|
|
|
+ await user.click(batchCreateTabs[0]);
|
|
|
|
|
|
- expect(screen.getByText('批量创建子商品规格')).toBeInTheDocument();
|
|
|
- expect(screen.getByText('为父商品创建多个规格(如不同颜色、尺寸等)')).toBeInTheDocument();
|
|
|
+ // 等待组件更新,使用findByText等待元素出现
|
|
|
+ await screen.findByText('批量创建规格');
|
|
|
+ expect(screen.getByText('添加多个商品规格,创建后将作为子商品批量生成')).toBeInTheDocument();
|
|
|
});
|
|
|
|
|
|
- it('应该支持添加批量创建规格', () => {
|
|
|
+ it('应该支持添加批量创建规格', async () => {
|
|
|
+ const user = userEvent.setup();
|
|
|
render(
|
|
|
<GoodsParentChildPanel
|
|
|
{...defaultProps}
|
|
|
@@ -265,17 +274,23 @@ describe('GoodsParentChildPanel', () => {
|
|
|
// 切换到批量创建标签页(可能有多个,点击第一个)
|
|
|
const batchCreateTabs = screen.getAllByText('批量创建');
|
|
|
expect(batchCreateTabs.length).toBeGreaterThan(0);
|
|
|
- fireEvent.click(batchCreateTabs[0]);
|
|
|
+ await user.click(batchCreateTabs[0]);
|
|
|
+
|
|
|
+ // 等待BatchSpecCreatorInline组件渲染
|
|
|
+ await waitFor(() => {
|
|
|
+ expect(screen.getByText('批量创建规格')).toBeInTheDocument();
|
|
|
+ });
|
|
|
|
|
|
- const addSpecButtons = screen.getAllByText('添加规格');
|
|
|
+ const addSpecButtons = screen.getAllByText('添加');
|
|
|
expect(addSpecButtons.length).toBeGreaterThan(0);
|
|
|
- fireEvent.click(addSpecButtons[0]);
|
|
|
+ await user.click(addSpecButtons[0]);
|
|
|
|
|
|
// 应该显示规格输入字段
|
|
|
- expect(screen.getAllByPlaceholderText('如:红色、XL')).toHaveLength(1);
|
|
|
+ expect(screen.getAllByPlaceholderText('例如:红色、64GB、S码')).toHaveLength(1);
|
|
|
});
|
|
|
|
|
|
- it('应该支持管理子商品标签页(编辑模式)', () => {
|
|
|
+ it('应该支持管理子商品标签页(编辑模式)', async () => {
|
|
|
+ const user = userEvent.setup();
|
|
|
render(
|
|
|
<GoodsParentChildPanel
|
|
|
{...defaultProps}
|
|
|
@@ -289,19 +304,25 @@ describe('GoodsParentChildPanel', () => {
|
|
|
|
|
|
const manageChildrenTabs = screen.getAllByText('管理子商品');
|
|
|
expect(manageChildrenTabs.length).toBeGreaterThan(0);
|
|
|
- fireEvent.click(manageChildrenTabs[0]);
|
|
|
+ await user.click(manageChildrenTabs[0]);
|
|
|
+
|
|
|
+ // 等待ChildGoodsList组件渲染
|
|
|
+ await waitFor(() => {
|
|
|
+ expect(screen.getByText('查看和管理当前商品的子商品')).toBeInTheDocument();
|
|
|
+ });
|
|
|
|
|
|
// 可能有多个"管理子商品"元素,检查至少存在一个
|
|
|
const manageChildrenElements = screen.getAllByText('管理子商品');
|
|
|
expect(manageChildrenElements.length).toBeGreaterThan(0);
|
|
|
expect(manageChildrenElements[0]).toBeInTheDocument();
|
|
|
- expect(screen.getByText('查看和管理当前商品的子商品')).toBeInTheDocument();
|
|
|
});
|
|
|
|
|
|
it('应该禁用按钮当disabled为true', () => {
|
|
|
render(
|
|
|
<GoodsParentChildPanel
|
|
|
{...defaultProps}
|
|
|
+ spuId={-1}
|
|
|
+ spuName={null}
|
|
|
disabled={true}
|
|
|
/>,
|
|
|
{ wrapper: createWrapper() }
|
|
|
@@ -361,7 +382,8 @@ describe('GoodsParentChildPanel', () => {
|
|
|
});
|
|
|
});
|
|
|
|
|
|
- it('应该处理批量创建规格的更新', () => {
|
|
|
+ it('应该处理批量创建规格的更新', async () => {
|
|
|
+ const user = userEvent.setup();
|
|
|
const onDataChange = vi.fn();
|
|
|
render(
|
|
|
<GoodsParentChildPanel
|
|
|
@@ -376,21 +398,27 @@ describe('GoodsParentChildPanel', () => {
|
|
|
// 切换到批量创建标签页(可能有多个,点击第一个)
|
|
|
const batchCreateTabs = screen.getAllByText('批量创建');
|
|
|
expect(batchCreateTabs.length).toBeGreaterThan(0);
|
|
|
- fireEvent.click(batchCreateTabs[0]);
|
|
|
+ await user.click(batchCreateTabs[0]);
|
|
|
+
|
|
|
+ // 等待BatchSpecCreatorInline组件渲染
|
|
|
+ await waitFor(() => {
|
|
|
+ expect(screen.getByText('批量创建规格')).toBeInTheDocument();
|
|
|
+ });
|
|
|
|
|
|
- const addSpecButtons = screen.getAllByText('添加规格');
|
|
|
+ const addSpecButtons = screen.getAllByText('添加');
|
|
|
expect(addSpecButtons.length).toBeGreaterThan(0);
|
|
|
- fireEvent.click(addSpecButtons[0]);
|
|
|
+ await user.click(addSpecButtons[0]);
|
|
|
|
|
|
// 更新规格名称
|
|
|
- const nameInput = screen.getByPlaceholderText('如:红色、XL');
|
|
|
- fireEvent.change(nameInput, { target: { value: '红色' } });
|
|
|
+ const nameInput = screen.getByPlaceholderText('例如:红色、64GB、S码');
|
|
|
+ await user.type(nameInput, '红色');
|
|
|
|
|
|
// 应该调用onDataChange
|
|
|
expect(onDataChange).toHaveBeenCalled();
|
|
|
});
|
|
|
|
|
|
it('应该支持子商品删除功能', async () => {
|
|
|
+ const user = userEvent.setup();
|
|
|
// 模拟API响应
|
|
|
const mockGoodsDetail = { id: 789, spuId: 123, tenantId: 1 };
|
|
|
const mockDeleteResponse = { success: true };
|
|
|
@@ -420,14 +448,13 @@ describe('GoodsParentChildPanel', () => {
|
|
|
// 切换到管理子商品标签页(可能有多个,点击第一个)
|
|
|
const manageChildrenTabs = screen.getAllByText('管理子商品');
|
|
|
expect(manageChildrenTabs.length).toBeGreaterThan(0);
|
|
|
- fireEvent.click(manageChildrenTabs[0]);
|
|
|
-
|
|
|
- // 等待ChildGoodsList渲染(可能需要mock ChildGoodsList)
|
|
|
- // 由于ChildGoodsList被渲染,但我们需要模拟onDeleteChild回调
|
|
|
- // 简化测试:验证GoodsParentChildPanel正确处理删除逻辑
|
|
|
- // 我们可以直接测试handleDeleteChild函数,但需要访问组件实例
|
|
|
- // 对于单元测试,我们主要验证组件集成
|
|
|
- // 更详细的测试在ChildGoodsList测试中
|
|
|
+ await user.click(manageChildrenTabs[0]);
|
|
|
+
|
|
|
+ // 等待ChildGoodsList组件渲染
|
|
|
+ await waitFor(() => {
|
|
|
+ expect(screen.getByText('查看和管理当前商品的子商品')).toBeInTheDocument();
|
|
|
+ });
|
|
|
+
|
|
|
// 可能有多个"管理子商品"元素,检查至少存在一个
|
|
|
const manageChildrenElements = screen.getAllByText('管理子商品');
|
|
|
expect(manageChildrenElements.length).toBeGreaterThan(0);
|
|
|
@@ -435,10 +462,12 @@ describe('GoodsParentChildPanel', () => {
|
|
|
});
|
|
|
|
|
|
it('应该使查询失效当批量创建子商品成功', async () => {
|
|
|
+ const user = userEvent.setup();
|
|
|
// 模拟 queryClient
|
|
|
const mockQueryClient = {
|
|
|
invalidateQueries: vi.fn()
|
|
|
};
|
|
|
+ // 使用spyOn模拟useQueryClient的返回值(指定'get'访问器)
|
|
|
const useQueryClientSpy = vi.spyOn(require('@tanstack/react-query'), 'useQueryClient', 'get');
|
|
|
useQueryClientSpy.mockReturnValue(mockQueryClient);
|
|
|
|
|
|
@@ -464,25 +493,32 @@ describe('GoodsParentChildPanel', () => {
|
|
|
|
|
|
// 切换到批量创建标签页
|
|
|
const batchCreateTab = screen.getByText('批量创建');
|
|
|
- fireEvent.click(batchCreateTab);
|
|
|
+ await user.click(batchCreateTab);
|
|
|
+
|
|
|
+ // 等待BatchSpecCreatorInline组件渲染
|
|
|
+ await waitFor(() => {
|
|
|
+ expect(screen.getByText('批量创建规格')).toBeInTheDocument();
|
|
|
+ });
|
|
|
|
|
|
// 添加规格
|
|
|
- const addSpecButton = screen.getByText('添加规格');
|
|
|
- fireEvent.click(addSpecButton);
|
|
|
+ const addSpecButton = screen.getByText('添加');
|
|
|
+ await user.click(addSpecButton);
|
|
|
|
|
|
// 填写规格信息
|
|
|
- const nameInput = screen.getByPlaceholderText('如:红色、XL');
|
|
|
- fireEvent.change(nameInput, { target: { value: '红色' } });
|
|
|
+ const nameInput = screen.getByPlaceholderText('例如:红色、64GB、S码');
|
|
|
+ await user.type(nameInput, '红色');
|
|
|
|
|
|
const priceInput = screen.getAllByPlaceholderText('0.00')[0];
|
|
|
- fireEvent.change(priceInput, { target: { value: '100' } });
|
|
|
+ await user.clear(priceInput);
|
|
|
+ await user.type(priceInput, '100');
|
|
|
|
|
|
const stockInput = screen.getAllByPlaceholderText('0')[0];
|
|
|
- fireEvent.change(stockInput, { target: { value: '10' } });
|
|
|
+ await user.clear(stockInput);
|
|
|
+ await user.type(stockInput, '10');
|
|
|
|
|
|
// 点击批量创建按钮
|
|
|
const createButton = screen.getByText('批量创建子商品');
|
|
|
- fireEvent.click(createButton);
|
|
|
+ await user.click(createButton);
|
|
|
|
|
|
// 等待 mutation 完成
|
|
|
await waitFor(() => {
|
|
|
@@ -494,6 +530,9 @@ describe('GoodsParentChildPanel', () => {
|
|
|
});
|
|
|
expect(toast.success).toHaveBeenCalledWith('批量创建子商品成功');
|
|
|
});
|
|
|
+
|
|
|
+ // 恢复spy
|
|
|
+ useQueryClientSpy.mockRestore();
|
|
|
});
|
|
|
|
|
|
it('应该显示删除确认对话框当点击删除按钮', () => {
|