# SKU Options Selection 测试配置指南

## 1. 基本信息

| 项目 | 内容 |
|------|------|
| 组件名称 | SKU Options Selection |
| 组件路径 | private-materials/src/plus/skuOptionsSelection |
| 组件简介 | 用于商品详情页面的 SKU 选择，支持单规格、组合规格、套餐三种类型 |
| 使用场景 | 商品详情页面的规格选择 |
| 测试目的 | 确保组件在商品详情页面中正确渲染和交互，支持三种规格类型的完整流程 |

## 1.2 测试范围

- ✅ **功能测试范围**：组件渲染、单规格选择、组合规格选择、套餐选择、价格计算、数量选择、校验
- ✅ **UI 渲染**：响应式布局适配、样式(悬停、禁用、选中)、价格显示、规格标题显示
- ✅ **交互行为**：点击选择、规格切换、数量调整、价格联动更新
- ✅ **边界条件**：空数据、单个选项、大量选项、极端值、异常输入
- ✅ **兼容性测试范围**：桌面端、平板端、移动端

## 1.3 测试策略

| 测试类型 | 工具 | 优先级 |
|---------|------|--------|
| 单元测试 | Vitest + RTL | P0 |
| 交互测试 | RTL + user-event | P1 |
| 可视化测试 | Storybook | P1 |
| E2E 测试 | Playwright/Cypress | P2 |

## 2. 测试数据准备

### 2.1 基础测试数据

#### 2.1.1 单规格数据集（option_group）

```typescript
// 最小单规格数据集（仅1个选项）
export const singleOptionGroup = {
  option_group: [
    {
      id: 2530,
      product_id: 61685,
      name: "仅1个选项",
      option_item: [
        {
          id: 9953,
          option_group_id: 2530,
          product_id: 61685,
          name: "选项1啊",
          add_price: "1.00",
          original_price: "1.00"
        }
      ]
    }
  ]
};

// 标准单规格数据集（多个选项）
export const standardOptionGroup = {
  option_group: [
    {
      id: 2530,
      product_id: 61685,
      name: "颜色",
      option_item: [
        {
          id: 9953,
          option_group_id: 2530,
          product_id: 61685,
          name: "红色",
          add_price: "0.00",
          original_price: "0.00"
        },
        {
          id: 9954,
          option_group_id: 2530,
          product_id: 61685,
          name: "蓝色",
          add_price: "5.00",
          original_price: "5.00"
        },
        {
          id: 9955,
          option_group_id: 2530,
          product_id: 61685,
          name: "绿色",
          add_price: "10.00",
          original_price: "10.00"
        }
      ]
    }
  ]
};

// 多组单规格数据集
export const multiOptionGroup = {
  option_group: [
    {
      id: 2530,
      product_id: 61685,
      name: "颜色",
      option_item: [
        {
          id: 9953,
          option_group_id: 2530,
          product_id: 61685,
          name: "红色",
          add_price: "0.00",
          original_price: "0.00"
        },
        {
          id: 9954,
          option_group_id: 2530,
          product_id: 61685,
          name: "蓝色",
          add_price: "5.00",
          original_price: "5.00"
        }
      ]
    },
    {
      id: 2531,
      product_id: 61685,
      name: "尺寸",
      option_item: [
        {
          id: 9956,
          option_group_id: 2531,
          product_id: 61685,
          name: "S",
          add_price: "0.00",
          original_price: "0.00"
        },
        {
          id: 9957,
          option_group_id: 2531,
          product_id: 61685,
          name: "M",
          add_price: "0.00",
          original_price: "0.00"
        }
      ]
    }
  ]
};
```

#### 2.1.2 组合规格数据集（variant_group + variant）

```typescript
// 最小组合规格数据集
export const singleVariantGroup = {
  variant_group: [
    {
      id: 7320,
      product_id: 61686,
      name: "尺码",
      variant_item: [
        {
          id: 20330,
          variant_group_id: 7320,
          product_id: 61686,
          name: "L"
        }
      ]
    }
  ],
  variant: [
    {
      id: 29288,
      product_id: 61686,
      sku_key: "4078424:583449",
      sku_ids: "7320:20330",
      name: {
        "4078424": "L"
      },
      price: "34.00"
    }
  ]
};

// 标准组合规格数据集（多个规格组）
export const standardVariantGroup = {
  variant_group: [
    {
      id: 7320,
      product_id: 61686,
      name: "尺码",
      variant_item: [
        {
          id: 20330,
          variant_group_id: 7320,
          product_id: 61686,
          name: "S"
        },
        {
          id: 20331,
          variant_group_id: 7320,
          product_id: 61686,
          name: "M"
        },
        {
          id: 20332,
          variant_group_id: 7320,
          product_id: 61686,
          name: "L"
        }
      ]
    },
    {
      id: 7321,
      product_id: 61686,
      name: "颜色",
      variant_item: [
        {
          id: 20333,
          variant_group_id: 7321,
          product_id: 61686,
          name: "红色"
        },
        {
          id: 20334,
          variant_group_id: 7321,
          product_id: 61686,
          name: "蓝色"
        }
      ]
    }
  ],
  variant: [
    {
      id: 29288,
      product_id: 61686,
      sku_key: "4078424:583449",
      sku_ids: "7320:20330,7321:20333",
      name: {
        "4078424": "S",
        "4078425": "红色"
      },
      price: "34.00"
    },
    {
      id: 29289,
      product_id: 61686,
      sku_key: "4078424:583450",
      sku_ids: "7320:20331,7321:20333",
      name: {
        "4078424": "M",
        "4078425": "红色"
      },
      price: "36.00"
    }
  ]
};
```

#### 2.1.3 套餐数据集（bundle_group）

```typescript
// 最小套餐数据集
export const singleBundleGroup = {
  bundle_group: [
    {
      id: 752,
      product_id: 61687,
      name: "普通",
      bundle_item: [
        {
          id: 1594,
          product_id: 61687,
          group_id: 752,
          bundle_product_id: 61667,
          bundle_variant_id: 0,
          extension_id: 55,
          extension_type: "product_voucher",
          price_type: "markup",
          price_type_ext: "",
          price: "0.00",
          product_price: "10.00",
          title: "测试代金券抵扣税费附加费1121_001"
        }
      ]
    }
  ]
};

// 标准套餐数据集（多个套餐组）
export const standardBundleGroup = {
  bundle_group: [
    {
      id: 752,
      product_id: 61687,
      name: "普通套餐",
      bundle_item: [
        {
          id: 1594,
          product_id: 61687,
          group_id: 752,
          bundle_product_id: 61667,
          bundle_variant_id: 0,
          extension_id: 55,
          extension_type: "product_voucher",
          price_type: "markup",
          price: "0.00",
          product_price: "10.00",
          title: "代金券"
        }
      ]
    },
    {
      id: 753,
      product_id: 61687,
      name: "豪华套餐",
      bundle_item: [
        {
          id: 1595,
          product_id: 61687,
          group_id: 753,
          bundle_product_id: 61668,
          bundle_variant_id: 0,
          extension_id: 56,
          extension_type: "product",
          price_type: "markup",
          price: "20.00",
          product_price: "50.00",
          title: "附加商品"
        }
      ]
    }
  ]
};

// 套餐项包含单规格选项的数据集（bundle_item 包含 option_group）
export const bundleWithOptionGroup = {
  bundle_group: [
    {
      id: 752,
      product_id: 61687,
      name: "普通",
      min_quantity: 1,
      max_quantity: 7,
      bundle_item: [
        {
          id: 1594,
          product_id: 61687,
          group_id: 752,
          bundle_product_id: 61667,
          bundle_variant_id: 0,
          extension_id: 55,
          extension_type: "product_voucher",
          price_type: "product_price",
          price: "0.00",
          product_price: "10.00",
          max_quantity: 4,
          title: "测试代金券抵扣税费附加费1121_001"
        },
        {
          id: 1596,
          product_id: 61687,
          group_id: 752,
          bundle_product_id: 61245, // 单规格商品ID
          bundle_variant_id: 0,
          extension_id: 0,
          extension_type: "product",
          price_type: "discount", // 减价
          price: "3.00",
          product_price: "0.00",
          max_quantity: 3,
          title: "单规格0703",
          // bundle_item 包含 option_group（单规格选项）
          option_group: [
            {
              id: 4078424,
              product_id: 61245,
              name: "Include",
              required: 1,
              option_item: [
                {
                  id: 583449,
                  option_group_id: 4078424,
                  product_id: 61245,
                  name: "选项1",
                  add_price: "0.00",
                  original_price: "0.00"
                },
                {
                  id: 583450,
                  option_group_id: 4078424,
                  product_id: 61245,
                  name: "选项2",
                  add_price: "5.00",
                  original_price: "5.00"
                }
              ]
            },
            {
              id: 4078425,
              product_id: 61245,
              name: "其他",
              required: 1,
              option_item: [
                {
                  id: 583451,
                  option_group_id: 4078425,
                  product_id: 61245,
                  name: "其他选项1",
                  add_price: "0.00",
                  original_price: "0.00"
                }
              ]
            },
            {
              id: 4078426,
              product_id: 61245,
              name: "Style",
              required: 1,
              option_item: [
                {
                  id: 583452,
                  option_group_id: 4078426,
                  product_id: 61245,
                  name: "样式1",
                  add_price: "0.00",
                  original_price: "0.00"
                },
                {
                  id: 583453,
                  option_group_id: 4078426,
                  product_id: 61245,
                  name: "样式2",
                  add_price: "10.00",
                  original_price: "10.00"
                }
              ]
            }
          ]
        }
      ]
    }
  ]
};
```

#### 2.1.4 混合规格数据集（同时包含单规格、组合规格、套餐）

```typescript
export const mixedSpecData = {
  option_group: standardOptionGroup.option_group,
  variant_group: standardVariantGroup.variant_group,
  variant: standardVariantGroup.variant,
  bundle_group: standardBundleGroup.bundle_group
};
```

### 2.2 异常数据

```typescript
// 缺少必填字段的数据
export const invalidOptionGroup1 = {
  option_group: [
    {
      product_id: "61685",
      name: "仅1个选项",
      // 缺少 id 字段
    }
  ]
};

// 空选项列表的数据
export const invalidOptionGroup2 = {
  option_group: [
    {
      id: "2530",
      product_id: 61685,
      name: "颜色",
      option_item: [] // 空选项列表
    }
  ]
};

// 数据类型错误的数据
export const invalidOptionGroup3 = {
  option_group: [
    {
      id: "2530", // 应该是数字
      product_id: "61685", // 应该是数字
      name: 123, // 应该是字符串
      option_item: "invalid" // 应该是数组
    }
  ]
};
```

### 2.3 边界数据

#### 2.3.1 空数据

```typescript
export const emptyData = {
  option_group: [],
  variant_group: [],
  variant: [],
  bundle_group: []
};
```

#### 2.3.2 大量数据

```typescript
// 大量单规格数据（100个规格组）
export const largeOptionGroup = {
  option_group: Array.from({ length: 100 }, (_, i) => ({
    id: 2530 + i,
    product_id: 61685,
    name: `规格组 ${i + 1}`,
    option_item: Array.from({ length: 10 }, (_, j) => ({
      id: 9953 + j,
      option_group_id: 2530 + i,
      product_id: 61685,
      name: `选项 ${j + 1}`,
      add_price: `${j * 10}.00`,
      original_price: `${j * 10}.00`
    }))
  }))
};
```

#### 2.3.3 长文本数据

```typescript
export const longTextOptionGroup = {
  option_group: [
    {
      id: 2530,
      product_id: 61685,
      name: "这是一个非常长的规格组名称，用来测试组件在长文本情况下的显示效果和布局适配",
      option_item: [
        {
          id: 9953,
          option_group_id: 2530,
          product_id: 61685,
          name: "这是一个非常长的选项名称，用来测试组件在长文本情况下的显示效果和布局适配",
          add_price: "1999999.00",
          original_price: "1.00"
        }
      ]
    }
  ]
};
```

#### 2.3.4 极端价格数据

```typescript
export const extremePriceData = {
  option_group: [
    {
      id: 2530,
      product_id: 61685,
      name: "价格测试",
      option_item: [
        {
          id: 9953,
          option_group_id: 2530,
          product_id: 61685,
          name: "免费选项",
          add_price: "0.00",
          original_price: "0.00"
        },
        {
          id: 9954,
          option_group_id: 2530,
          product_id: 61685,
          name: "高价选项",
          add_price: "999999.99",
          original_price: "999999.99"
        },
        {
          id: 9955,
          option_group_id: 2530,
          product_id: 61685,
          name: "负价选项",
          add_price: "-10.00",
          original_price: "-10.00"
        }
      ]
    }
  ]
};
```

### 2.4 默认 Props

```typescript
export const defaultProps = {
  dataSource: {
    option_group: [],
    variant_group: [],
    variant: [],
    bundle_group: []
  },
  value: null,
  onChange: jest.fn(),
  disabled: false,
  className: '',
  style: {}
};
```

## 3. 功能测试用例

### 3.1 基础渲染测试

| 用例ID | 测试场景 | 测试步骤 | 预期结果 | 优先级 |
|--------|---------|---------|---------|--------|
| TC-F-001 | 组件正常渲染 | 渲染组件，传入基础 props | 组件成功渲染，无报错 | P0 |
| TC-F-002 | 单规格列表渲染 | 传入单规格数据，检查选项数量 | 所有单规格选项正确渲染 | P0 |
| TC-F-003 | 组合规格列表渲染 | 传入组合规格数据，检查规格组和选项 | 所有组合规格组和选项正确渲染 | P0 |
| TC-F-004 | 套餐列表渲染 | 传入套餐数据，检查套餐组和套餐项 | 所有套餐组和套餐项正确渲染 | P0 |
| TC-F-005 | 混合规格渲染 | 传入同时包含三种规格的数据 | 三种规格类型都正确渲染 | P1 |
| TC-F-006 | 空数据源渲染 | 传入空数据对象 | 显示空状态或空列表，无报错 | P1 |

**测试代码示例：**

```typescript
import { render, screen } from '@testing-library/react';
import { SKUOptionsSelection } from '@/plus/skuOptionsSelection';
import { singleOptionGroup, standardVariantGroup, singleBundleGroup } from './testData';

describe('基础渲染测试', () => {
  test('TC-F-001: 组件正常渲染', () => {
    const { container } = render(<SKUOptionsSelection dataSource={defaultProps.dataSource} />);
    expect(container).toBeInTheDocument();
  });

  test('TC-F-002: 单规格列表渲染', () => {
    const { getAllByText } = render(
      <SKUOptionsSelection dataSource={singleOptionGroup} />
    );
    // 检查规格组名称
    expect(getAllByText('仅1个选项')).toBeInTheDocument();
    // 检查选项名称
    expect(getAllByText('选项1啊')).toBeInTheDocument();
  });

  test('TC-F-003: 组合规格列表渲染', () => {
    const { getAllByText } = render(
      <SKUOptionsSelection dataSource={standardVariantGroup} />
    );
    // 检查规格组名称
    expect(getAllByText('尺码')).toBeInTheDocument();
    expect(getAllByText('颜色')).toBeInTheDocument();
    // 检查选项名称
    expect(getAllByText('S')).toBeInTheDocument();
    expect(getAllByText('红色')).toBeInTheDocument();
  });

  test('TC-F-004: 套餐列表渲染', () => {
    const { getAllByText } = render(
      <SKUOptionsSelection dataSource={singleBundleGroup} />
    );
    // 检查套餐组名称
    expect(getAllByText('普通')).toBeInTheDocument();
    // 检查套餐项名称
    expect(getAllByText('测试代金券抵扣税费附加费1121_001')).toBeInTheDocument();
  });
});
```

### 3.2 单规格选择测试

| 用例ID | 测试场景 | 测试步骤 | 预期结果 | 优先级 |
|--------|---------|---------|---------|--------|
| TC-F-007 | 单规格单选 | 点击一个单规格选项 | 该选项被选中，触发 onChange 事件 | P0 |
| TC-F-008 | 单规格切换 | 先选A，再选B | B被选中，A取消选中，价格正确更新 | P0 |
| TC-F-009 | 单规格多组选择 | 多个规格组，每组选择一个 | 每组都有选中的选项，价格累加正确 | P1 |
| TC-F-010 | 单规格禁用选项 | 点击禁用的选项 | 选项不可点击，状态不变 | P1 |
| TC-F-011 | 单规格价格显示 | 选择不同价格的选项 | 价格正确显示和更新 | P0 |

**测试代码示例：**

```typescript
describe('单规格选择测试', () => {
  test('TC-F-007: 单规格单选', () => {
    const onChange = jest.fn();
    const { getByText } = render(
      <SKUOptionsSelection 
        dataSource={standardOptionGroup}
        onChange={onChange}
      />
    );
    
    const redOption = getByText('红色');
    fireEvent.click(redOption);
    
    expect(redOption).toHaveClass('selected');
    expect(onChange).toHaveBeenCalledWith(
      expect.objectContaining({
        option: expect.objectContaining({ id: 9953 })
      })
    );
  });

  test('TC-F-011: 单规格价格显示', () => {
    const { getByText } = render(
      <SKUOptionsSelection dataSource={standardOptionGroup} />
    );
    
    const blueOption = getByText('蓝色');
    fireEvent.click(blueOption);
    
    // 检查价格是否正确显示
    expect(getByText(/5\.00/)).toBeInTheDocument();
  });
});
```

### 3.3 组合规格选择测试

| 用例ID | 测试场景 | 测试步骤 | 预期结果 | 优先级 |
|--------|---------|---------|---------|--------|
| TC-F-012 | 组合规格单选 | 选择一个组合规格选项 | 该选项被选中，对应的 variant 被选中 | P0 |
| TC-F-013 | 组合规格联动 | 选择尺码S，再选择颜色红色 | 对应的 variant 被选中，价格正确显示 | P0 |
| TC-F-014 | 组合规格不可用状态 | 选择不存在的组合（如S+蓝色不存在） | 对应选项显示为禁用状态 | P1 |
| TC-F-015 | 组合规格切换 | 先选S+红色，再选M+红色 | 新的组合被选中，价格正确更新 | P0 |
| TC-F-016 | 组合规格价格计算 | 选择不同的 variant 组合 | 价格根据 variant.price 正确显示 | P0 |

**测试代码示例：**

```typescript
describe('组合规格选择测试', () => {
  test('TC-F-013: 组合规格联动', () => {
    const onChange = jest.fn();
    const { getByText } = render(
      <SKUOptionsSelection 
        dataSource={standardVariantGroup}
        onChange={onChange}
      />
    );
    
    // 先选择尺码 S
    const sizeSOption = getByText('S');
    fireEvent.click(sizeSOption);
    
    // 再选择颜色 红色
    const redOption = getByText('红色');
    fireEvent.click(redOption);
    
    // 应该选中对应的 variant
    expect(onChange).toHaveBeenCalledWith(
      expect.objectContaining({
        variant: expect.objectContaining({ 
          sku_ids: "7320:20330,7321:20333" 
        })
      })
    );
  });
});
```

### 3.4 套餐选择测试

| 用例ID | 测试场景 | 测试步骤 | 预期结果 | 优先级 |
|--------|---------|---------|---------|--------|
| TC-F-017 | 套餐单选 | 选择一个套餐组 | 该套餐组被选中，显示套餐项 | P0 |
| TC-F-018 | 套餐切换 | 先选套餐A，再选套餐B | B被选中，A取消选中 | P0 |
| TC-F-019 | 套餐价格计算 | 选择套餐 | 价格根据套餐项价格正确计算 | P0 |
| TC-F-020 | 套餐多套餐项 | 套餐包含多个套餐项 | 所有套餐项正确显示 | P1 |
| TC-F-020-1 | 套餐项包含单规格选项 | 选择包含 option_group 的套餐项 | 显示 Options 按钮，可打开单规格选择界面 | P0 |
| TC-F-020-2 | 套餐项单规格选项选择 | 在套餐项中选择单规格选项 | 单规格选项被选中，价格正确累加 | P0 |
| TC-F-020-3 | 套餐项单规格必选校验 | 套餐项包含 required=1 的单规格选项 | 必须选择该选项才能继续 | P0 |
| TC-F-020-4 | 套餐项单规格价格计算 | 选择套餐项及其单规格选项 | 价格 = 套餐项价格 + 单规格选项价格 | P0 |

**测试代码示例：**

```typescript
import { bundleWithOptionGroup } from './testData';

describe('套餐项包含单规格选项测试', () => {
  test('TC-F-020-1: 套餐项包含单规格选项', () => {
    const { getByText } = render(
      <SKUOptionsSelection dataSource={bundleWithOptionGroup} />
    );
    
    // 检查包含单规格选项的套餐项
    expect(getByText('单规格0703')).toBeInTheDocument();
    // 检查 Options 按钮是否存在
    const optionsButton = getByText('Options');
    expect(optionsButton).toBeInTheDocument();
  });

  test('TC-F-020-2: 套餐项单规格选项选择', () => {
    const onChange = jest.fn();
    const { getByText } = render(
      <SKUOptionsSelection 
        dataSource={bundleWithOptionGroup}
        onChange={onChange}
      />
    );
    
    // 点击包含单规格的套餐项
    const singleSpecItem = getByText('单规格0703');
    fireEvent.click(singleSpecItem);
    
    // 点击 Options 按钮打开单规格选择界面
    const optionsButton = getByText('Options');
    fireEvent.click(optionsButton);
    
    // 选择单规格选项
    const option1 = getByText('选项1');
    fireEvent.click(option1);
    
    // 验证 onChange 被调用，包含套餐项和单规格选项信息
    expect(onChange).toHaveBeenCalledWith(
      expect.objectContaining({
        bundle_item: expect.objectContaining({ id: 1596 }),
        option: expect.objectContaining({ id: 583449 })
      })
    );
  });

  test('TC-F-020-4: 套餐项单规格价格计算', () => {
    const { getByText } = render(
      <SKUOptionsSelection dataSource={bundleWithOptionGroup} />
    );
    
    // 选择套餐项
    const bundleItem = getByText('单规格0703');
    fireEvent.click(bundleItem);
    
    // 打开单规格选择
    const optionsButton = getByText('Options');
    fireEvent.click(optionsButton);
    
    // 选择有加价的单规格选项
    const option2 = getByText('选项2'); // add_price: "5.00"
    fireEvent.click(option2);
    
    // 验证价格计算：套餐项价格（discount: -3.00）+ 单规格选项价格（5.00）
    // 需要根据实际价格计算逻辑验证
    expect(getByText(/2\.00/)).toBeInTheDocument(); // 假设基础价格为 0，-3 + 5 = 2
  });
});
```

### 3.5 数量选择测试

| 用例ID | 测试场景 | 测试步骤 | 预期结果 | 优先级 |
|--------|---------|---------|---------|--------|
| TC-F-021 | 数量选择器显示 | 渲染组件 | 显示数量选择器 | P0 |
| TC-F-022 | 数量增加 | 点击增加按钮 | 数量增加1，总价正确更新 | P0 |
| TC-F-023 | 数量减少 | 点击减少按钮 | 数量减少1，最小为1，总价正确更新 | P0 |
| TC-F-024 | 数量输入 | 直接输入数量 | 数量更新为输入值，总价正确更新 | P1 |
| TC-F-025 | 数量边界 | 输入0或负数 | 数量保持为1或显示错误提示 | P1 |

### 3.6 价格计算测试

| 用例ID | 测试场景 | 测试步骤 | 预期结果 | 优先级 |
|--------|---------|---------|---------|--------|
| TC-F-026 | 单规格价格累加 | 选择多个单规格选项 | 价格正确累加 | P0 |
| TC-F-027 | 组合规格价格 | 选择组合规格 | 价格使用 variant.price | P0 |
| TC-F-028 | 套餐价格计算 | 选择套餐 | 价格根据套餐项价格类型正确计算 | P0 |
| TC-F-029 | 混合规格价格 | 同时选择单规格和套餐 | 价格正确累加 | P1 |
| TC-F-030 | 价格格式化 | 显示价格 | 价格格式正确（如 $10.00） | P1 |

### 3.7 校验规则测试

| 用例ID | 测试场景 | 测试步骤 | 预期结果 | 优先级 |
|--------|---------|---------|---------|--------|
| TC-F-031 | 必填校验 | required=true，不选择任何选项 | 显示必填提示 | P0 |
| TC-F-032 | 组合规格必选 | 组合规格必须选择完整 | 未选择完整时显示提示 | P0 |
| TC-F-033 | 数量校验 | 数量小于最小值 | 显示数量错误提示 | P1 |
| TC-F-034 | 库存校验 | 选择无库存的选项 | 显示库存不足提示 | P1 |

## 4. UI/交互测试用例

### 4.1 布局测试

| 用例ID | 测试场景 | 测试步骤 | 预期结果 | 优先级 |
|--------|---------|---------|---------|--------|
| TC-UI-001 | 规格组标题显示 | 渲染组件 | 显示所有规格组标题 | P0 |
| TC-UI-002 | 选项列表布局 | 渲染多个选项 | 选项按正确布局排列 | P0 |
| TC-UI-003 | 响应式布局 | 在不同屏幕尺寸下渲染 | 布局自适应屏幕尺寸 | P1 |
| TC-UI-004 | 价格显示位置 | 渲染组件 | 价格在正确位置显示 | P0 |

### 4.2 样式变体测试

| 用例ID | 测试场景 | 测试步骤 | 预期结果 | 优先级 |
|--------|---------|---------|---------|--------|
| TC-UI-005 | 选中状态样式 | 选中一个选项 | 显示选中状态样式 | P0 |
| TC-UI-006 | 禁用状态样式 | 渲染禁用选项 | 显示禁用状态样式 | P0 |
| TC-UI-007 | 悬停状态样式 | 鼠标悬停在选项上 | 显示悬停状态样式 | P1 |
| TC-UI-008 | 不可用状态样式 | 组合规格不可用选项 | 显示不可用状态样式 | P1 |

### 4.3 交互测试

| 用例ID | 测试场景 | 测试步骤 | 预期结果 | 优先级 |
|--------|---------|---------|---------|--------|
| TC-UI-009 | 点击选项 | 点击选项 | 触发 onChange 事件 | P0 |
| TC-UI-010 | 键盘导航 | 使用键盘方向键 | 选项可以键盘导航 | P1 |
| TC-UI-011 | 键盘选择 | 按 Enter 键 | 选中当前聚焦的选项 | P1 |
| TC-UI-012 | 加载状态 | 数据加载中 | 显示加载状态 | P1 |

## 5. 边界与异常测试用例

### 5.1 数据边界测试

| 用例ID | 测试场景 | 测试步骤 | 预期结果 | 优先级 |
|--------|---------|---------|---------|--------|
| TC-B-001 | 空数据源 | 传入空数据对象 | 显示空状态，不报错 | P0 |
| TC-B-002 | 单个选项 | 传入只有一个选项的数据 | 正常渲染和交互 | P1 |
| TC-B-003 | 大量选项 | 传入100+选项 | 正常渲染，性能可接受 | P1 |
| TC-B-004 | 特殊字符 | 选项包含特殊字符 | 正确显示和转义 | P1 |
| TC-B-005 | 长文本 | 选项文本很长 | 文本正确显示或截断 | P2 |

### 5.2 值边界测试

| 用例ID | 测试场景 | 测试步骤 | 预期结果 | 优先级 |
|--------|---------|---------|---------|--------|
| TC-B-006 | 价格为0 | 传入价格为0的选项 | 正常处理，显示免费或0.00 | P1 |
| TC-B-007 | 价格为负数 | 传入价格为负数的选项 | 正常处理或显示错误 | P1 |
| TC-B-008 | 极高价格 | 传入极高价格的选项 | 价格正确显示 | P1 |
| TC-B-009 | 数量边界 | 输入极大数量 | 数量限制或显示错误 | P1 |

### 5.3 异常场景测试

| 用例ID | 测试场景 | 测试步骤 | 预期结果 | 优先级 |
|--------|---------|---------|---------|--------|
| TC-B-010 | 数据格式错误 | 传入格式错误的数据 | 显示错误提示，不崩溃 | P0 |
| TC-B-011 | 缺少必填字段 | 传入缺少必填字段的数据 | 显示错误提示，不崩溃 | P0 |
| TC-B-012 | 组合规格不匹配 | variant 与 variant_group 不匹配 | 正确处理，不报错 | P1 |
| TC-B-013 | 并发选择 | 快速连续点击多个选项 | 正确处理，不出现竞态条件 | P1 |
| TC-B-014 | 组件卸载 | 组件卸载时仍有异步操作 | 不报错，正确清理 | P1 |

## 6. 性能测试用例

### 6.1 渲染性能测试

| 用例ID | 测试场景 | 测试步骤 | 预期结果 | 优先级 |
|--------|---------|---------|---------|--------|
| TC-P-001 | 初始渲染性能 | 渲染100个选项 | 初始渲染时间 < 100ms | P1 |
| TC-P-002 | 更新性能 | 更新选中状态 | 更新响应时间 < 50ms | P1 |
| TC-P-003 | 大数据量渲染 | 渲染1000个选项 | 使用虚拟滚动或分页 | P2 |

### 6.2 内存测试

| 用例ID | 测试场景 | 测试步骤 | 预期结果 | 优先级 |
|--------|---------|---------|---------|--------|
| TC-P-004 | 内存泄漏 | 多次挂载卸载组件 | 无内存泄漏 | P1 |
| TC-P-005 | 事件监听器清理 | 组件卸载 | 所有事件监听器被清理 | P1 |

## 7. 可访问性测试用例

### 7.1 键盘导航测试

| 用例ID | 测试场景 | 测试步骤 | 预期结果 | 优先级 |
|--------|---------|---------|---------|--------|
| TC-A-001 | Tab 键导航 | 按 Tab 键 | 可以在选项间导航 | P0 |
| TC-A-002 | 方向键导航 | 使用方向键 | 可以在选项间移动焦点 | P0 |
| TC-A-003 | Enter 键选择 | 按 Enter 键 | 选中当前聚焦的选项 | P0 |
| TC-A-004 | Esc 键 | 按 Esc 键 | 取消选择或关闭 | P1 |

### 7.2 ARIA 与语义化测试

| 用例ID | 测试场景 | 测试步骤 | 预期结果 | 优先级 |
|--------|---------|---------|---------|--------|
| TC-A-005 | ARIA 标签 | 检查 ARIA 属性 | 包含正确的 ARIA 标签 | P0 |
| TC-A-006 | 语义化 HTML | 检查 HTML 结构 | 使用语义化标签 | P0 |
| TC-A-007 | 屏幕阅读器支持 | 使用屏幕阅读器 | 可以正确读取选项 | P1 |
| TC-A-008 | 焦点可见性 | 检查焦点样式 | 焦点清晰可见 | P0 |

## 8. 测试用例执行清单

### 8.1 测试用例分类统计

| 分类 | 用例数量 | 已完成 | 未完成 | 通过率 |
|------|---------|--------|--------|--------|
| 功能测试用例 | 34 | - | - | - |
| UI/交互测试用例 | 12 | - | - | - |
| 边界与异常测试用例 | 14 | - | - | - |
| 性能测试用例 | 5 | - | - | - |
| 可访问性测试用例 | 8 | - | - | - |
| **总计** | **73** | - | - | - |

### 8.2 测试执行优先级

**P0（必须执行）：**
- TC-F-001 至 TC-F-004（基础渲染）
- TC-F-007 至 TC-F-008（单规格选择）
- TC-F-011（单规格价格）
- TC-F-012 至 TC-F-013（组合规格选择）
- TC-F-016（组合规格价格）
- TC-F-017 至 TC-F-019（套餐选择）
- TC-F-021 至 TC-F-023（数量选择）
- TC-F-026 至 TC-F-028（价格计算）
- TC-F-031 至 TC-F-032（校验规则）
- TC-UI-001 至 TC-UI-002（布局）
- TC-UI-005 至 TC-UI-006（样式）
- TC-UI-009（交互）
- TC-B-001（空数据源）
- TC-B-010 至 TC-B-011（异常场景）
- TC-A-001 至 TC-A-003（键盘导航）
- TC-A-005 至 TC-A-006（ARIA）

**P1（重要）：**
- 其余功能测试用例
- 其余 UI/交互测试用例
- 边界测试用例
- 性能测试用例

**P2（可选）：**
- 高级功能测试用例
- 极端场景测试用例

## 9. 快速测试配置

### 9.1 单规格商品测试配置

```typescript
const singleOptionConfig = {
  dataSource: {
    option_group: [
      {
        id: 2530,
        product_id: 61685,
        name: "颜色",
        option_item: [
          { id: 9953, option_group_id: 2530, product_id: 61685, name: "红色", add_price: "0.00", original_price: "0.00" },
          { id: 9954, option_group_id: 2530, product_id: 61685, name: "蓝色", add_price: "5.00", original_price: "5.00" }
        ]
      }
    ],
    variant_group: [],
    variant: [],
    bundle_group: []
  }
};
```

### 9.2 组合规格商品测试配置

```typescript
const variantConfig = {
  dataSource: {
    option_group: [],
    variant_group: [
      {
        id: 7320,
        product_id: 61686,
        name: "尺码",
        variant_item: [
          { id: 20330, variant_group_id: 7320, product_id: 61686, name: "S" },
          { id: 20331, variant_group_id: 7320, product_id: 61686, name: "M" }
        ]
      },
      {
        id: 7321,
        product_id: 61686,
        name: "颜色",
        variant_item: [
          { id: 20333, variant_group_id: 7321, product_id: 61686, name: "红色" },
          { id: 20334, variant_group_id: 7321, product_id: 61686, name: "蓝色" }
        ]
      }
    ],
    variant: [
      {
        id: 29288,
        product_id: 61686,
        sku_key: "4078424:583449",
        sku_ids: "7320:20330,7321:20333",
        name: { "4078424": "S", "4078425": "红色" },
        price: "34.00"
      }
    ],
    bundle_group: []
  }
};
```

### 9.3 套餐商品测试配置

```typescript
const bundleConfig = {
  dataSource: {
    option_group: [],
    variant_group: [],
    variant: [],
    bundle_group: [
      {
        id: 752,
        product_id: 61687,
        name: "普通套餐",
        bundle_item: [
          {
            id: 1594,
            product_id: 61687,
            group_id: 752,
            bundle_product_id: 61667,
            bundle_variant_id: 0,
            extension_id: 55,
            extension_type: "product_voucher",
            price_type: "markup",
            price: "0.00",
            product_price: "10.00",
            title: "代金券"
          }
        ]
      }
    ]
  }
};
```

## 10. 常见问题

### Q1: 单规格选项不显示？
**检查：**
- `option_group` 数组是否正确配置
- 每个 `option_group` 是否包含 `option_item` 数组
- `option_item` 是否包含必填字段（id, name）

### Q2: 组合规格联动不生效？
**检查：**
- `variant_group` 和 `variant` 是否匹配
- `variant.sku_ids` 是否与 `variant_group` 的 id 对应
- 是否选择了完整的规格组合

### Q3: 套餐价格计算错误？
**检查：**
- `price_type` 是否正确（markup/fixed）
- `price` 和 `product_price` 是否正确
- 价格计算公式是否正确

### Q4: 数量选择器不显示？
**检查：**
- 是否配置了 `showQuantity` 属性
- 数量选择器相关 props 是否正确

## 11. 附录

### 11.1 相关文档
- [开发文档](https://studio.picoding-dev.com/plus-components/sku-options-selection?tab=dev)
- [设计文档](https://m1ed09stz4r.feishu.cn/wiki/B43ZwQ62hi8VHgkNIGmcbPDcnJd)
