---
title: 测试用例
order: 4
category: pro
---

# PisellBatchActionBar 测试用例文档

## 1. 测试概述

### 1.1 测试目标

- 验证组件的基础渲染功能
- 验证选择状态感知功能
- 验证默认操作按钮功能
- 验证自定义操作按钮功能
- 验证溢出处理功能
- 验证布局模式功能
- 验证事件回调机制
- 验证确认弹窗功能

### 1.2 测试范围

- 单元测试：组件功能测试
- 集成测试：与 Table/List 等组件的集成测试
- 快照测试：UI 结构测试
- 性能测试：大量选中项时的性能测试
- 响应式测试：不同布局模式下的表现

## 2. 基础功能测试

### 2.1 组件渲染测试

#### 测试用例 1: 默认渲染

**描述**：组件应该能够正常渲染，并显示默认配置

**测试步骤**：

1. 渲染组件，传递必填的 `selectedKeys` prop
2. 检查组件是否成功渲染
3. 检查是否显示选中数量提示
4. 检查是否显示操作按钮容器

**预期结果**：

- 组件成功渲染
- 显示选中数量提示（默认显示）
- 显示操作按钮容器

**测试代码**：

```
test('should render with default props', () => {
  const { container } = render(
    <PisellBatchActionBar selectedKeys={[]} />
  );
  expect(container.querySelector('.pisell-batch-action-bar')).toBeInTheDocument();
  expect(container.querySelector('.pisell-batch-action-bar__count')).toBeInTheDocument();
  expect(container.querySelector('.pisell-batch-action-bar__actions')).toBeInTheDocument();
});
```

#### 测试用例 2: 自定义类名和样式

**描述**：应该能够接受自定义类名和样式

**测试步骤**：

1. 传递 className 和 style props
2. 检查元素是否应用了自定义类名和样式

**预期结果**：

- 容器元素包含自定义类名
- 容器元素应用了自定义样式

**测试代码**：

```
test('should apply custom className and style', () => {
  const { container } = render(
    <PisellBatchActionBar
      selectedKeys={[]}
      className="custom-class"
      style={{ background: 'red' }}
    />
  );
  const element = container.querySelector('.pisell-batch-action-bar');
  expect(element).toHaveClass('custom-class');
  expect(element).toHaveStyle({ background: 'red' });
});
```

#### 测试用例 3: 隐藏所有默认按钮

**描述**：设置 `defaultActions.showDefaultActions={false}` 应该隐藏所有默认按钮

**测试步骤**：

1. 设置 `defaultActions.showDefaultActions={false}`
2. 检查所有默认按钮是否隐藏

**预期结果**：

- 全选、反选、取消选择按钮都不显示

**测试代码**：

```
test('should hide all default buttons when showDefaultActions is false', () => {
  const { container } = render(
    <PisellBatchActionBar
      selectedKeys={['1', '2']}
      defaultActions={{ showDefaultActions: false }}
    />
  );
  expect(container.querySelector('[key="select-all"]')).not.toBeInTheDocument();
  expect(container.querySelector('[key="invert"]')).not.toBeInTheDocument();
  expect(container.querySelector('[key="clear"]')).not.toBeInTheDocument();
});
```

## 3. 选择状态感知测试

### 3.1 选中数量计算

#### 测试用例 4: 选中数量显示

**描述**：应该正确显示选中数量

**测试步骤**：

1. 设置 `selectedKeys={['1', '2', '3']}`
2. 检查选中数量提示是否正确显示

**预期结果**：

- 显示 "已选择 3 项"

**测试代码**：

```
test('should display selected count correctly', () => {
  const { getByText } = render(
    <PisellBatchActionBar selectedKeys={['1', '2', '3']} />
  );
  expect(getByText('已选择 3 项')).toBeInTheDocument();
});
```

#### 测试用例 5: 自定义选中数量文案

**描述**：应该支持自定义选中数量文案

**测试步骤**：

1. 设置 `selectedCountText="共选择了 {count} 个项目"`
2. 检查文案是否正确替换

**预期结果**：

- 显示 "共选择了 3 个项目"

**测试代码**：

```
test('should support custom count text template', () => {
  const { getByText } = render(
    <PisellBatchActionBar
      selectedKeys={['1', '2', '3']}
      selectedCountText="共选择了 {count} 个项目"
    />
  );
  expect(getByText('共选择了 3 个项目')).toBeInTheDocument();
});
```

#### 测试用例 6: 函数形式的选中数量文案

**描述**：应该支持函数形式的选中数量文案

**测试步骤**：

1. 设置 `selectedCountText={(count) => `已选${count}项`}`
2. 检查文案是否正确显示

**预期结果**：

- 显示 "已选3项"

**测试代码**：

```
test('should support function count text', () => {
  const { getByText } = render(
    <PisellBatchActionBar
      selectedKeys={['1', '2', '3']}
      selectedCountText={(count) => `已选${count}项`}
    />
  );
  expect(getByText('已选3项')).toBeInTheDocument();
});
```

#### 测试用例 7: 隐藏选中数量提示

**描述**：设置 `showSelectedCount={false}` 应该隐藏数量提示

**测试步骤**：

1. 设置 `showSelectedCount={false}`
2. 检查数量提示是否隐藏

**预期结果**：

- 数量提示不显示

**测试代码**：

```
test('should hide count text when showSelectedCount is false', () => {
  const { container } = render(
    <PisellBatchActionBar
      selectedKeys={['1', '2']}
      showSelectedCount={false}
    />
  );
  expect(container.querySelector('.pisell-batch-action-bar__count')).not.toBeInTheDocument();
});
```

### 3.2 全选状态判断

#### 测试用例 8: 全选按钮显示

**描述**：全选按钮应该固定显示"全选"文案

**测试步骤**：

1. 设置 `defaultActions.showSelectAll={true}` 或使用默认值
2. 检查全选按钮是否显示

**预期结果**：

- 全选按钮显示"全选"文案

**测试代码**：

```
test('should show select all button', () => {
  const { getByText } = render(
    <PisellBatchActionBar
      selectedKeys={['1', '2']}
      defaultActions={{ showSelectAll: true }}
    />
  );
  expect(getByText('全选')).toBeInTheDocument();
});
```

#### 测试用例 9: 隐藏全选按钮

**描述**：设置 `defaultActions.showSelectAll={false}` 应该隐藏全选按钮

**测试步骤**：

1. 设置 `defaultActions.showSelectAll={false}`
2. 检查全选按钮是否隐藏

**预期结果**：

- 全选按钮不显示

**测试代码**：

```
test('should hide select all button when showSelectAll is false', () => {
  const { queryByText } = render(
    <PisellBatchActionBar
      selectedKeys={['1', '2']}
      defaultActions={{ showSelectAll: false }}
    />
  );
  expect(queryByText('全选')).not.toBeInTheDocument();
});
```

#### 测试用例 10: 反选按钮显示隐藏

**描述**：应该支持控制反选按钮的显示隐藏

**测试步骤**：

1. 设置 `defaultActions.showInvert={false}`
2. 检查反选按钮是否隐藏

**预期结果**：

- 反选按钮不显示

**测试代码**：

```
test('should hide invert button when showInvert is false', () => {
  const { queryByText } = render(
    <PisellBatchActionBar
      selectedKeys={['1', '2']}
      defaultActions={{ showInvert: false }}
    />
  );
  expect(queryByText('反选')).not.toBeInTheDocument();
});
```

#### 测试用例 11: 取消选择按钮显示隐藏

**描述**：应该支持控制取消选择按钮的显示隐藏

**测试步骤**：

1. 设置 `defaultActions.showClear={false}`
2. 检查取消选择按钮是否隐藏

**预期结果**：

- 取消选择按钮不显示

**测试代码**：

```
test('should hide clear button when showClear is false', () => {
  const { queryByText } = render(
    <PisellBatchActionBar
      selectedKeys={['1', '2']}
      defaultActions={{ showClear: false }}
    />
  );
  expect(queryByText('取消选择')).not.toBeInTheDocument();
});
```

## 4. 默认操作按钮测试

### 4.1 全选按钮

#### 测试用例 12: 全选按钮点击

**描述**：点击全选按钮应该触发 `onSelectAll` 回调

**测试步骤**：

1. 设置 `defaultActions.showSelectAll={true}` 或使用默认值
2. 设置 `onSelectAll` mock 函数
3. 点击全选按钮
4. 检查回调是否被调用

**预期结果**：

- `onSelectAll` 回调被调用

**测试代码**：

```
test('should call onSelectAll when clicking select all button', () => {
  const onSelectAll = vi.fn();
  const { getByText } = render(
    <PisellBatchActionBar
      selectedKeys={[]}
      defaultActions={{ showSelectAll: true }}
      onSelectAll={onSelectAll}
    />
  );
  
  fireEvent.click(getByText('全选'));
  expect(onSelectAll).toHaveBeenCalled();
});
```

### 4.2 反选按钮

#### 测试用例 13: 反选按钮点击

**描述**：点击反选按钮应该触发 `onInvert` 回调

**测试步骤**：

1. 设置 `defaultActions.showInvert={true}` 或使用默认值
2. 设置 `onInvert` mock 函数
3. 点击反选按钮
4. 检查回调是否被调用

**预期结果**：

- `onInvert` 回调被调用

**测试代码**：

```
test('should call onInvert when clicking invert button', () => {
  const onInvert = vi.fn();
  const { getByText } = render(
    <PisellBatchActionBar
      selectedKeys={['1', '2']}
      defaultActions={{ showInvert: true }}
      onInvert={onInvert}
    />
  );
  
  fireEvent.click(getByText('反选'));
  expect(onInvert).toHaveBeenCalled();
});
```

### 4.3 取消选择按钮

#### 测试用例 14: 取消选择按钮点击

**描述**：点击取消选择按钮应该触发 `onClear` 回调

**测试步骤**：

1. 设置 `defaultActions.showClear={true}` 或使用默认值
2. 设置 `onClear` mock 函数
3. 点击取消选择按钮
4. 检查回调是否被调用

**预期结果**：

- `onClear` 回调被调用

**测试代码**：

```
test('should call onClear when clicking clear button', () => {
  const onClear = vi.fn();
  const { getByText } = render(
    <PisellBatchActionBar
      selectedKeys={['1', '2']}
      defaultActions={{ showClear: true }}
      onClear={onClear}
    />
  );
  
  fireEvent.click(getByText('取消选择'));
  expect(onClear).toHaveBeenCalled();
});
```

## 5. 自定义操作按钮测试

### 5.1 按钮渲染

#### 测试用例 15: 自定义操作按钮渲染

**描述**：应该正确渲染自定义操作按钮

**测试步骤**：

1. 设置 `actions` 数组
2. 检查按钮是否正确渲染

**预期结果**：

- 所有自定义按钮都正确渲染

**测试代码**：

```
test('should render custom action buttons', () => {
  const { getByText } = render(
    <PisellBatchActionBar
      selectedKeys={['1']}
      actions={[
        { key: 'delete', label: '删除' },
        { key: 'export', label: '导出' },
      ]}
    />
  );
  
  expect(getByText('删除')).toBeInTheDocument();
  expect(getByText('导出')).toBeInTheDocument();
});
```

#### 测试用例 16: 按钮排序

**描述**：应该支持通过 `actionOrder` 控制按钮顺序

**测试步骤**：

1. 设置 `actions` 和 `actionOrder={['export', 'delete']}`
2. 检查按钮顺序是否正确

**预期结果**：

- 按钮按照 `actionOrder` 的顺序排列

**测试类型**：自动化

### 5.2 按钮显隐控制

#### 测试用例 17: 基于函数的显隐控制

**描述**：应该支持函数形式的 `visible` 配置

**测试步骤**：

1. 设置 `visible={(keys) => keys.length > 0}`
2. 测试有选中项和无选中项时的显示状态

**预期结果**：

- 有选中项时显示，无选中项时隐藏

**测试代码**：

```
test('should control visibility with function', () => {
  const { rerender, queryByText } = render(
    <PisellBatchActionBar
      selectedKeys={[]}
      actions={[
        {
          key: 'delete',
          label: '删除',
          visible: (keys) => keys.length > 0,
        },
      ]}
    />
  );
  
  expect(queryByText('删除')).not.toBeInTheDocument();
  
  rerender(
    <PisellBatchActionBar
      selectedKeys={['1']}
      actions={[
        {
          key: 'delete',
          label: '删除',
          visible: (keys) => keys.length > 0,
        },
      ]}
    />
  );
  
  expect(queryByText('删除')).toBeInTheDocument();
});
```

#### 测试用例 18: 基于布尔值的显隐控制

**描述**：应该支持布尔值形式的 `visible` 配置

**测试步骤**：

1. 设置 `visible={false}`
2. 检查按钮是否隐藏

**预期结果**：

- 按钮不显示

**测试代码**：

```
test('should control visibility with boolean', () => {
  const { queryByText } = render(
    <PisellBatchActionBar
      selectedKeys={['1']}
      actions={[
        {
          key: 'delete',
          label: '删除',
          visible: false,
        },
      ]}
    />
  );
  
  expect(queryByText('删除')).not.toBeInTheDocument();
});
```

### 5.3 按钮禁用控制

#### 测试用例 19: 基于函数的禁用控制

**描述**：应该支持函数形式的 `disabled` 配置

**测试步骤**：

1. 设置 `disabled={(keys) => keys.length === 0}`
2. 测试有选中项和无选中项时的禁用状态

**预期结果**：

- 无选中项时禁用，有选中项时启用

**测试代码**：

```
test('should control disabled state with function', () => {
  const { rerender, getByText } = render(
    <PisellBatchActionBar
      selectedKeys={[]}
      actions={[
        {
          key: 'delete',
          label: '删除',
          disabled: (keys) => keys.length === 0,
        },
      ]}
    />
  );
  
  const button = getByText('删除').closest('.graphic-text-card');
  expect(button).toHaveAttribute('disabled');
  
  rerender(
    <PisellBatchActionBar
      selectedKeys={['1']}
      actions={[
        {
          key: 'delete',
          label: '删除',
          disabled: (keys) => keys.length === 0,
        },
      ]}
    />
  );
  
  const button2 = getByText('删除').closest('.graphic-text-card');
  expect(button2).not.toHaveAttribute('disabled');
});
```

### 5.4 按钮点击事件

#### 测试用例 20: action.onClick 优先

**描述**：如果 action 中配置了 `onClick`，应该优先使用 action.onClick

**测试步骤**：

1. 同时设置 `action.onClick` 和组件级别的 `onActionClick`
2. 点击按钮
3. 检查哪个回调被调用

**预期结果**：

- `action.onClick` 被调用，`onActionClick` 不被调用

**测试代码**：

```
test('should use action.onClick when provided', () => {
  const actionOnClick = vi.fn();
  const onActionClick = vi.fn();
  
  const { getByText } = render(
    <PisellBatchActionBar
      selectedKeys={['1']}
      actions={[
        {
          key: 'delete',
          label: '删除',
          onClick: actionOnClick,
        },
      ]}
      onActionClick={onActionClick}
    />
  );
  
  fireEvent.click(getByText('删除'));
  expect(actionOnClick).toHaveBeenCalledWith(['1'], undefined);
  expect(onActionClick).not.toHaveBeenCalled();
});
```

#### 测试用例 21: 组件级别 onActionClick

**描述**：如果 action 中没有 `onClick`，应该使用组件级别的 `onActionClick`

**测试步骤**：

1. 只设置组件级别的 `onActionClick`
2. 点击按钮
3. 检查回调是否被调用，参数是否正确

**预期结果**：

- `onActionClick` 被调用，参数包含 key、selectedKeys、selectedRows

**测试代码**：

```
test('should use onActionClick when action.onClick not provided', () => {
  const onActionClick = vi.fn();
  const selectedRows = [{ id: '1', name: 'Item 1' }];
  
  const { getByText } = render(
    <PisellBatchActionBar
      selectedKeys={['1']}
      selectedRows={selectedRows}
      actions={[
        {
          key: 'delete',
          label: '删除',
        },
      ]}
      onActionClick={onActionClick}
    />
  );
  
  fireEvent.click(getByText('删除'));
  expect(onActionClick).toHaveBeenCalledWith('delete', ['1'], selectedRows);
});
```

### 5.5 确认弹窗

#### 测试用例 22: 显示确认弹窗

**描述**：设置 `confirm={true}` 应该显示确认弹窗

**测试步骤**：

1. 设置 `confirm={true}`
2. 点击按钮
3. 检查是否显示确认弹窗

**预期结果**：

- 显示确认弹窗

**测试代码**：

```
test('should show confirm popup when confirm is true', async () => {
  const { getByText } = render(
    <PisellBatchActionBar
      selectedKeys={['1']}
      actions={[
        {
          key: 'delete',
          label: '删除',
          confirm: true,
        },
      ]}
    />
  );
  
  fireEvent.click(getByText('删除'));
  
  await waitFor(() => {
    expect(screen.getByText('确认操作')).toBeInTheDocument();
  });
});
```

#### 测试用例 23: 自定义确认弹窗配置

**描述**：应该支持自定义确认弹窗的标题和描述

**测试步骤**：

1. 设置 `confirm={{ title: '确认删除', description: '确定要删除吗？' }}`
2. 点击按钮
3. 检查弹窗内容是否正确

**预期结果**：

- 弹窗显示自定义标题和描述

**测试代码**：

```
test('should show custom confirm popup', async () => {
  const { getByText } = render(
    <PisellBatchActionBar
      selectedKeys={['1']}
      actions={[
        {
          key: 'delete',
          label: '删除',
          confirm: {
            title: '确认删除',
            description: '确定要删除选中的项目吗？',
          },
        },
      ]}
    />
  );
  
  fireEvent.click(getByText('删除'));
  
  await waitFor(() => {
    expect(screen.getByText('确认删除')).toBeInTheDocument();
    expect(screen.getByText('确定要删除选中的项目吗？')).toBeInTheDocument();
  });
});
```

#### 测试用例 24: 确认弹窗确认操作

**描述**：点击确认弹窗的确认按钮应该触发操作

**测试步骤**：

1. 设置确认弹窗和 `onClick` 回调
2. 点击按钮打开弹窗
3. 点击确认按钮
4. 检查回调是否被调用

**预期结果**：

- `onClick` 回调被调用

**测试代码**：

```
test('should trigger action when confirming in popup', async () => {
  const onClick = vi.fn();
  const { getByText } = render(
    <PisellBatchActionBar
      selectedKeys={['1']}
      actions={[
        {
          key: 'delete',
          label: '删除',
          confirm: true,
          onClick,
        },
      ]}
    />
  );
  
  fireEvent.click(getByText('删除'));
  
  await waitFor(() => {
    const confirmButton = screen.getByText('确定');
    fireEvent.click(confirmButton);
  });
  
  expect(onClick).toHaveBeenCalled();
});
```

### 5.6 Loading 状态

#### 测试用例 25: Loading 状态显示

**描述**：设置 `loading={true}` 应该显示 loading 状态

**测试步骤**：

1. 设置 `loading={true}`
2. 检查按钮是否显示 loading 状态

**预期结果**：

- 按钮显示 loading 状态（禁用点击）

**测试类型**：自动化

## 6. 溢出处理测试

### 6.1 溢出判断

#### 测试用例 26: 溢出判断

**描述**：当按钮数量超过 `overflowThreshold` 时应该显示更多按钮

**测试步骤**：

1. 设置 `overflowThreshold={3}` 和 5 个操作按钮
2. 检查是否显示"更多"按钮

**预期结果**：

- 显示"更多"按钮
- 前 3 个按钮可见，后 2 个按钮在更多菜单中

**测试类型**：人工测试（需要实际测量按钮宽度）

#### 测试用例 27: 禁用溢出处理

**描述**：设置 `enableOverflow={false}` 应该禁用溢出处理

**测试步骤**：

1. 设置 `enableOverflow={false}` 和多个按钮
2. 检查是否显示"更多"按钮

**预期结果**：

- 不显示"更多"按钮，所有按钮都显示

**测试类型**：自动化

### 6.2 更多操作菜单

#### 测试用例 28: 更多操作菜单展开

**描述**：点击"更多"按钮应该展开 MultilevelCard 的 panel

**测试步骤**：

1. 触发溢出状态
2. 点击"更多"按钮
3. 检查 panel 是否展开

**预期结果**：

- panel 展开，显示溢出的操作按钮

**测试类型**：人工测试

#### 测试用例 29: 更多操作菜单点击

**描述**：点击更多菜单中的按钮应该触发对应操作

**测试步骤**：

1. 展开更多菜单
2. 点击菜单中的按钮
3. 检查回调是否被调用

**预期结果**：

- 回调被正确调用

**测试类型**：人工测试

## 7. 布局模式测试

### 7.1 悬浮模式（默认）

#### 测试用例 30: 悬浮模式样式

**描述**：默认应该使用悬浮模式（float）

**测试步骤**：

1. 不设置 `position` prop
2. 检查容器样式

**预期结果**：

- 容器使用 `position: fixed`
- 容器居中显示（left: 50%, transform: translateX(-50%)）
- 有阴影效果

**测试代码**：

```
test('should use float position by default', () => {
  const { container } = render(
    <PisellBatchActionBar selectedKeys={[]} />
  );
  const element = container.querySelector('.pisell-batch-action-bar');
  const style = window.getComputedStyle(element!);
  expect(style.position).toBe('fixed');
});
```

### 7.2 吸顶模式

#### 测试用例 31: 吸顶模式样式

**描述**：设置 `position="sticky-top"` 应该使用吸顶模式

**测试步骤**：

1. 设置 `position="sticky-top"` 和 `offset={20}`
2. 检查容器样式

**预期结果**：

- 容器使用 `position: sticky`
- `top: 20px`
- `zIndex: 100`

**测试代码**：

```
test('should use sticky-top position', () => {
  const { container } = render(
    <PisellBatchActionBar
      selectedKeys={[]}
      position="sticky-top"
      offset={20}
    />
  );
  const element = container.querySelector('.pisell-batch-action-bar');
  expect(element).toHaveClass('pisell-batch-action-bar--sticky-top');
});
```

### 7.3 吸底模式

#### 测试用例 32: 吸底模式样式

**描述**：设置 `position="sticky-bottom"` 应该使用吸底模式

**测试步骤**：

1. 设置 `position="sticky-bottom"` 和 `offset={0}`
2. 检查容器样式

**预期结果**：

- 容器使用 `position: sticky`
- `bottom: 0px`
- `zIndex: 100`

**测试类型**：自动化

### 7.4 内嵌模式

#### 测试用例 33: 内嵌模式样式

**描述**：设置 `position="inline"` 应该使用内嵌模式

**测试步骤**：

1. 设置 `position="inline"`
2. 检查容器样式

**预期结果**：

- 容器使用 `position: relative`（默认）
- 无固定定位样式

**测试类型**：自动化

## 8. 事件回调测试

### 8.1 默认操作回调

#### 测试用例 34: 全选回调

**描述**：点击全选按钮应该触发 `onSelectAll` 回调

**测试类型**：自动化（已在测试用例 12 中覆盖）

#### 测试用例 35: 反选回调

**描述**：点击反选按钮应该触发 `onInvert` 回调

**测试类型**：自动化（已在测试用例 13 中覆盖）

#### 测试用例 36: 取消选择回调

**描述**：点击取消选择按钮应该触发 `onClear` 回调

**测试类型**：自动化（已在测试用例 14 中覆盖）

### 8.2 自定义操作回调

#### 测试用例 38: 自定义操作回调

**描述**：点击自定义操作按钮应该触发 `onActionClick` 回调

**测试类型**：自动化（已在测试用例 20、21 中覆盖）

## 9. 边界条件测试

### 9.1 空数据处理

#### 测试用例 39: 空 selectedKeys

**描述**：`selectedKeys={[]}` 应该正常渲染

**测试步骤**：

1. 设置 `selectedKeys={[]}`
2. 检查组件是否正常渲染

**预期结果**：

- 组件正常渲染
- 显示 "已选择 0 项"

**测试类型**：自动化

#### 测试用例 40: 空 actions 数组

**描述**：`actions={[]}` 应该正常渲染

**测试步骤**：

1. 设置 `actions={[]}`
2. 检查组件是否正常渲染

**预期结果**：

- 组件正常渲染
- 只显示默认操作按钮（如果配置了）

**测试类型**：自动化

### 9.2 异常输入处理

#### 测试用例 41: selectedKeys 为 undefined

**描述**：`selectedKeys` 为 undefined 应该报错（因为必填）

**测试类型**：自动化（TypeScript 类型检查）

#### 测试用例 42: 大量选中项

**描述**：`selectedKeys` 包含 1000+ 项时应该正常渲染

**测试步骤**：

1. 设置包含 1000+ 项的 `selectedKeys`
2. 检查组件渲染性能

**预期结果**：

- 组件正常渲染
- 渲染时间可接受（< 1s）

**测试类型**：人工测试

## 10. 集成测试

### 10.1 与 Table 组件集成

#### 测试用例 43: 与 Table 集成

**描述**：应该能够与 Table 组件的 rowSelection 集成

**测试步骤**：

1. 创建 Table 组件，启用 rowSelection
2. 将 Table 的 selectedRowKeys 传递给 BatchActionBar
3. 测试批量操作功能

**预期结果**：

- 选中 Table 行时，BatchActionBar 显示选中数量
- 点击批量操作按钮，能够操作选中的行

**测试类型**：人工测试

### 10.2 与 List 组件集成

#### 测试用例 44: 与 List 集成

**描述**：应该能够与 List 组件的选择功能集成

**测试类型**：人工测试

### 10.3 与 Card 组件集成

#### 测试用例 45: 与 Card 集成

**描述**：应该能够与 Card 组件的选择功能集成

**测试类型**：人工测试

## 11. 性能测试

### 11.1 大量数据测试

#### 测试用例 46: 大量选中项性能

**描述**：选中 1000+ 项时，组件应该保持流畅

**测试步骤**：

1. 设置包含 1000+ 项的 `selectedKeys`
2. 测试组件渲染和交互性能

**预期结果**：

- 渲染时间 < 1s
- 交互无卡顿

**测试类型**：人工测试

#### 测试用例 47: 频繁更新 selectedKeys

**描述**：快速更新 `selectedKeys` 时应该保持流畅

**测试步骤**：

1. 快速切换 `selectedKeys`
2. 检查是否有性能问题

**预期结果**：

- 无明显卡顿
- 状态更新及时

**测试类型**：人工测试

## 12. 响应式测试

### 12.1 不同屏幕尺寸

#### 测试用例 48: 桌面端显示

**描述**：在桌面端（>= 1024px）应该正常显示

**测试类型**：人工测试

#### 测试用例 49: 平板端显示

**描述**：在平板端（768px - 1023px）应该正常显示

**测试类型**：人工测试

#### 测试用例 50: 移动端显示

**描述**：在移动端（< 768px）应该正常显示，按钮可能换行

**测试类型**：人工测试

## 13. 样式测试

### 13.1 容器样式

#### 测试用例 51: 默认容器样式

**描述**：容器应该有默认的背景色、边框、圆角、内边距

**测试类型**：人工测试

#### 测试用例 52: 自定义容器样式

**描述**：应该支持通过 `style` 自定义容器样式

**测试类型**：自动化

### 13.2 按钮样式

#### 测试用例 53: GraphicTextCard 按钮样式

**描述**：操作按钮应该使用 GraphicTextCard 组件，variant="horizontal"，size="small"

**测试类型**：人工测试

#### 测试用例 54: 按钮间距

**描述**：按钮之间应该有正确的间距（通过 PisellBasicGrid 的 gap 控制）

**测试类型**：人工测试

## 14. 快照测试

### 14.1 基础快照

#### 测试用例 55: 默认配置快照

**描述**：记录默认配置的渲染结果

**测试代码**：

```
test('should match snapshot with default props', () => {
  const { container } = render(
    <PisellBatchActionBar selectedKeys={[]} />
  );
  expect(container.firstChild).toMatchSnapshot();
});
```

#### 测试用例 56: 完整配置快照

**描述**：记录完整配置的渲染结果

**测试代码**：

```
test('should match snapshot with full props', () => {
  const { container } = render(
    <PisellBatchActionBar
      selectedKeys={['1', '2', '3']}
      selectedRows={[{ id: '1' }, { id: '2' }, { id: '3' }]}
      total={10}
      position="float"
      showSelectedCount={true}
      defaultActions={{
        showDefaultActions: true,
        showSelectAll: true,
        showInvert: true,
        showClear: true,
      }}
      actions={[
        { key: 'delete', label: '删除' },
        { key: 'export', label: '导出' },
      ]}
      onSelectAll={() => {}}
      onInvert={() => {}}
      onClear={() => {}}
    />
  );
  expect(container.firstChild).toMatchSnapshot();
});
```

## 15. 测试覆盖率目标

- 语句覆盖率：≥ 80%
- 分支覆盖率：≥ 75%
- 函数覆盖率：≥ 80%
- 行覆盖率：≥ 80%

## 16. 测试环境配置

```
{
  "testEnvironment": "jsdom",
  "setupFilesAfterEnv": ["<rootDir>/jest.setup.js"],
  "moduleNameMapper": {
    "\\.(css|less)$": "identity-obj-proxy"
  }
}
```

## 17. 测试注意事项

1. 所有异步操作使用 `waitFor` 等待
2. 使用 `fireEvent` 模拟用户交互
3. 使用 `vi.fn()` 创建 mock 函数（Vitest）
4. 测试后清理副作用
5. 使用 `data-testid` 便于定位元素（可选）
6. 溢出处理测试需要实际测量按钮宽度，建议使用人工测试
7. 大量数据测试需要关注性能，建议使用人工测试
8. 集成测试需要实际与其他组件配合，建议使用人工测试

## 18. Bug 汇总

### 18.1 待修复 Bug

| Bug 编号 | 发现时间 | 发现人 | 严重程度 | Bug 描述 | 复现步骤 | 预期结果 | 实际结果 | 状态 |
|--------|------|-----|------|--------|------|------|------|----|
| -      | -    | -   | -    | -      | -    | -    | -    | -  |

### 18.2 已修复 Bug

| Bug 编号  | 发现时间       | 修复时间       | 修复人         | Bug 描述               | 修复方案                                                                                   | 状态  |
|---------|------------|------------|-------------|----------------------|----------------------------------------------------------------------------------------|-----|
| BUG-001 | 2025-01-20 | 2025-01-20 | Pisell Team | 连续点击删除按钮会弹出多个确认弹窗    | 使用 useRef 添加防重复点击标记，在 Modal 打开时标记为 true，在 onCancel 和 afterClose 时重置为 false             | 已修复 |
| BUG-002 | 2025-01-20 | 2025-01-20 | Pisell Team | 点击"更多"按钮无反应，下拉面板无法展开 | 添加 moreExpanded 状态管理，通过 active 属性传递给 MultilevelCard，并在 GraphicTextCard 的 onClick 中切换状态 | 已修复 |

---

**说明**：

- 自动化测试：可通过单元测试自动执行的测试用例
- 人工测试：需要人工验证的测试用例（如溢出处理、性能测试、集成测试、响应式测试等）
- Bug 严重程度：P0（阻塞）、P1（严重）、P2（一般）、P3（轻微）
