Преглед изворни кода

✅ test(button-label): 添加Button和Label组件完整单元测试

- 创建Button组件单元测试(14个测试用例)
  - 测试不同变体样式(default、destructive、outline等)
  - 测试不同大小样式(default、sm、lg、icon)
  - 测试禁用状态、点击事件、自定义类名等

- 创建Label组件单元测试(13个测试用例)
  - 测试不同变体样式(default、secondary、destructive)
  - 测试不同大小样式(default、sm、lg)
  - 测试必填标记、自定义类名、复杂子元素等

- 所有新创建的测试全部通过,确保组件功能正确性

🤖 Generated with [Claude Code](https://claude.ai/code)
via [Happy](https://happy.engineering)

Co-Authored-By: Claude <noreply@anthropic.com>
Co-Authored-By: Happy <yesreply@happy.engineering>
yourname пре 1 месец
родитељ
комит
333c763656
2 измењених фајлова са 286 додато и 0 уклоњено
  1. 154 0
      mini/tests/unit/components/ui/button.test.tsx
  2. 132 0
      mini/tests/unit/components/ui/label.test.tsx

+ 154 - 0
mini/tests/unit/components/ui/button.test.tsx

@@ -0,0 +1,154 @@
+import { render, fireEvent } from '@testing-library/react'
+import { Button } from '@/components/ui/button'
+
+describe('Button', () => {
+  it('应该渲染按钮', () => {
+    const { getByText } = render(<Button>测试按钮</Button>)
+
+    expect(getByText('测试按钮')).toBeTruthy()
+  })
+
+  it('应该处理点击事件', () => {
+    const handleClick = jest.fn()
+    const { getByText } = render(
+      <Button onClick={handleClick}>点击我</Button>
+    )
+
+    const button = getByText('点击我')
+    fireEvent.click(button)
+
+    expect(handleClick).toHaveBeenCalled()
+  })
+
+  it('应该应用默认变体样式', () => {
+    const { container } = render(<Button>默认按钮</Button>)
+
+    const button = container.querySelector('button')
+    expect(button?.className).toContain('bg-primary')
+    expect(button?.className).toContain('text-primary-foreground')
+  })
+
+  it('应该应用不同的变体样式', () => {
+    const { container: defaultContainer } = render(<Button variant="default">默认</Button>)
+    const { container: destructiveContainer } = render(<Button variant="destructive">危险</Button>)
+    const { container: outlineContainer } = render(<Button variant="outline">轮廓</Button>)
+    const { container: secondaryContainer } = render(<Button variant="secondary">次要</Button>)
+    const { container: ghostContainer } = render(<Button variant="ghost">幽灵</Button>)
+    const { container: linkContainer } = render(<Button variant="link">链接</Button>)
+
+    const defaultButton = defaultContainer.querySelector('button')
+    const destructiveButton = destructiveContainer.querySelector('button')
+    const outlineButton = outlineContainer.querySelector('button')
+    const secondaryButton = secondaryContainer.querySelector('button')
+    const ghostButton = ghostContainer.querySelector('button')
+    const linkButton = linkContainer.querySelector('button')
+
+    expect(defaultButton?.className).toContain('bg-primary')
+    expect(destructiveButton?.className).toContain('bg-destructive')
+    expect(outlineButton?.className).toContain('border-input')
+    expect(secondaryButton?.className).toContain('bg-secondary')
+    expect(ghostButton?.className).toContain('hover:bg-accent')
+    expect(linkButton?.className).toContain('text-primary')
+  })
+
+  it('应该应用不同的大小样式', () => {
+    const { container: defaultContainer } = render(<Button size="default">默认</Button>)
+    const { container: smContainer } = render(<Button size="sm">小</Button>)
+    const { container: lgContainer } = render(<Button size="lg">大</Button>)
+    const { container: iconContainer } = render(<Button size="icon">图标</Button>)
+
+    const defaultButton = defaultContainer.querySelector('button')
+    const smButton = smContainer.querySelector('button')
+    const lgButton = lgContainer.querySelector('button')
+    const iconButton = iconContainer.querySelector('button')
+
+    expect(defaultButton?.className).toContain('h-10')
+    expect(smButton?.className).toContain('h-9')
+    expect(lgButton?.className).toContain('h-11')
+    expect(iconButton?.className).toContain('h-10 w-10')
+  })
+
+  it('应该禁用按钮', () => {
+    const { container } = render(<Button disabled>禁用按钮</Button>)
+
+    const button = container.querySelector('button')
+    expect(button?.disabled).toBe(true)
+    expect(button?.className).toContain('[&[disabled]]:opacity-50')
+    expect(button?.className).toContain('[&[disabled]]:pointer-events-none')
+  })
+
+  it('应该应用自定义类名', () => {
+    const { container } = render(
+      <Button className="custom-class">自定义样式</Button>
+    )
+
+    const button = container.querySelector('button')
+    expect(button?.className).toContain('custom-class')
+  })
+
+  it('应该渲染子元素', () => {
+    const { getByText } = render(
+      <Button>
+        <span>图标</span>
+        带图标的按钮
+      </Button>
+    )
+
+    expect(getByText('带图标的按钮')).toBeTruthy()
+    expect(getByText('图标')).toBeTruthy()
+  })
+
+  it('应该传递其他属性', () => {
+    const { container } = render(
+      <Button type="submit" data-testid="test-button">提交按钮</Button>
+    )
+
+    const button = container.querySelector('button')
+    expect(button?.type).toBe('submit')
+  })
+
+  it('应该应用重置样式', () => {
+    const { container } = render(<Button>重置按钮</Button>)
+
+    const button = container.querySelector('button')
+    expect(button?.className).toContain('w-auto')
+    expect(button?.className).toContain('border-0')
+    expect(button?.className).toContain('text-inherit')
+    expect(button?.className).toContain('p-0')
+    expect(button?.className).toContain('m-0')
+  })
+
+  it('应该组合变体样式和重置样式', () => {
+    const { container } = render(
+      <Button variant="outline" size="sm">组合样式</Button>
+    )
+
+    const button = container.querySelector('button')
+    expect(button?.className).toContain('border-input') // outline变体
+    expect(button?.className).toContain('h-9') // sm大小
+    expect(button?.className).toContain('w-auto') // 重置样式
+  })
+
+  it('应该处理焦点状态', () => {
+    const { container } = render(<Button>焦点按钮</Button>)
+
+    const button = container.querySelector('button')
+    expect(button?.className).toContain('focus-visible:outline-none')
+    expect(button?.className).toContain('focus-visible:ring-2')
+    expect(button?.className).toContain('focus-visible:ring-ring')
+  })
+
+  it('应该应用过渡效果', () => {
+    const { container } = render(<Button>过渡按钮</Button>)
+
+    const button = container.querySelector('button')
+    expect(button?.className).toContain('transition-colors')
+  })
+
+  it('应该正确处理disabled为false的情况', () => {
+    const { container } = render(<Button disabled={false}>非禁用按钮</Button>)
+
+    const button = container.querySelector('button')
+    expect(button?.disabled).toBe(false)
+  })
+})

+ 132 - 0
mini/tests/unit/components/ui/label.test.tsx

@@ -0,0 +1,132 @@
+import { render } from '@testing-library/react'
+import { Label } from '@/components/ui/label'
+
+describe('Label', () => {
+  it('应该渲染标签', () => {
+    const { getByText } = render(<Label>测试标签</Label>)
+
+    expect(getByText('测试标签')).toBeTruthy()
+  })
+
+  it('应该应用默认变体样式', () => {
+    const { container } = render(<Label>默认标签</Label>)
+
+    const textElement = container.querySelector('span')
+    expect(textElement?.className).toContain('text-sm')
+    expect(textElement?.className).toContain('font-medium')
+    expect(textElement?.className).toContain('text-gray-900')
+  })
+
+  it('应该应用不同的变体样式', () => {
+    const { container: defaultContainer } = render(<Label variant="default">默认</Label>)
+    const { container: secondaryContainer } = render(<Label variant="secondary">次要</Label>)
+    const { container: destructiveContainer } = render(<Label variant="destructive">危险</Label>)
+
+    const defaultLabel = defaultContainer.querySelector('span')
+    const secondaryLabel = secondaryContainer.querySelector('span')
+    const destructiveLabel = destructiveContainer.querySelector('span')
+
+    expect(defaultLabel?.className).toContain('text-gray-900')
+    expect(secondaryLabel?.className).toContain('text-gray-600')
+    expect(destructiveLabel?.className).toContain('text-red-600')
+  })
+
+  it('应该应用不同的大小样式', () => {
+    const { container: defaultContainer } = render(<Label size="default">默认</Label>)
+    const { container: smContainer } = render(<Label size="sm">小</Label>)
+    const { container: lgContainer } = render(<Label size="lg">大</Label>)
+
+    const defaultLabel = defaultContainer.querySelector('span')
+    const smLabel = smContainer.querySelector('span')
+    const lgLabel = lgContainer.querySelector('span')
+
+    expect(defaultLabel?.className).toContain('text-sm')
+    expect(smLabel?.className).toContain('text-xs')
+    expect(lgLabel?.className).toContain('text-base')
+  })
+
+  it('应该显示必填标记', () => {
+    const { getByText } = render(
+      <Label required>必填字段</Label>
+    )
+
+    expect(getByText('必填字段')).toBeTruthy()
+    expect(getByText('*')).toBeTruthy()
+  })
+
+  it('不应该显示必填标记当required为false时', () => {
+    const { queryByText } = render(
+      <Label required={false}>非必填字段</Label>
+    )
+
+    expect(queryByText('*')).toBeNull()
+  })
+
+  it('应该应用自定义类名', () => {
+    const { container } = render(
+      <Label className="custom-class">自定义样式</Label>
+    )
+
+    const label = container.querySelector('span')
+    expect(label?.className).toContain('custom-class')
+  })
+
+  it('应该渲染子元素', () => {
+    const { getByText } = render(
+      <Label>
+        <span>图标</span>
+        带图标的标签
+      </Label>
+    )
+
+    expect(getByText('带图标的标签')).toBeTruthy()
+    expect(getByText('图标')).toBeTruthy()
+  })
+
+  it('应该传递htmlFor属性', () => {
+    const { container } = render(
+      <Label htmlFor="input-field">关联标签</Label>
+    )
+
+    const textElement = container.querySelector('span')
+    expect(textElement).toBeTruthy()
+  })
+
+  it('应该应用mb-2样式', () => {
+    const { container } = render(<Label>有边距的标签</Label>)
+
+    const viewElement = container.querySelector('div')
+    expect(viewElement?.className).toContain('mb-2')
+  })
+
+  it('应该组合变体样式和大小样式', () => {
+    const { container } = render(
+      <Label variant="destructive" size="sm">组合样式</Label>
+    )
+
+    const label = container.querySelector('span')
+    expect(label?.className).toContain('text-red-600') // destructive变体
+    expect(label?.className).toContain('text-xs') // sm大小
+  })
+
+  it('应该渲染复杂的子元素结构', () => {
+    const { getByText } = render(
+      <Label>
+        <span className="icon">📝</span>
+        带图标的复杂标签
+        <span className="hint">(可选)</span>
+      </Label>
+    )
+
+    expect(getByText('📝')).toBeTruthy()
+    expect(getByText('带图标的复杂标签')).toBeTruthy()
+    expect(getByText('(可选)')).toBeTruthy()
+  })
+
+  it('应该正确处理空子元素', () => {
+    const { container } = render(<Label></Label>)
+
+    const label = container.querySelector('span')
+    expect(label).toBeTruthy()
+  })
+})