summaryrefslogtreecommitdiff
path: root/src/components/ui/button.test.tsx
blob: a15e0a0c8bef3b2484466e5d99beeb6eb2f68b42 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
import { describe, test, vi, expect } from 'vitest';
import { render, screen } from '@testing-library/react';
import { Button } from './button';

// Test Button component rendering
describe('Button', () => {
  // Test default button rendering
  test('renders default button with text', () => {
    render(<Button>Click Me</Button>);
    const button = screen.getByText('Click Me');
    expect(button).toBeInTheDocument();
    expect(button).toHaveAttribute('data-slot', 'button');
  });

  // Test buttons with different variants
  test('renders button with different variants', () => {
    const variants = [
      'default',
      'destructive',
      'outline',
      'secondary',
      'ghost',
      'link',
    ] as const;
    variants.forEach((variant) => {
      render(<Button variant={variant}>{variant} Variant</Button>);
      const button = screen.getByText(`${variant} Variant`);
      expect(button).toBeInTheDocument();
    });
  });

  // Test buttons with different sizes
  test('renders button with different sizes', () => {
    const sizes = ['default', 'sm', 'lg', 'icon'] as const;
    sizes.forEach((size) => {
      render(<Button size={size}>{size} Size</Button>);
      const button = screen.getByText(`${size} Size`);
      expect(button).toBeInTheDocument();
    });
  });

  // Test button click events
  test('handles click events', () => {
    const handleClick = vi.fn();
    render(<Button onClick={handleClick}>Click Test</Button>);
    const button = screen.getByText('Click Test');
    button.click();
    expect(handleClick).toHaveBeenCalledTimes(1);
  });

  // Test disabled state
  test('renders disabled button', () => {
    render(<Button disabled>Disabled Button</Button>);
    const button = screen.getByText('Disabled Button');
    expect(button).toBeDisabled();
  });

  // Test asChild property
  test('renders as a link when asChild is true', () => {
    render(
      <Button asChild>
        <a href="#">As Child Link</a>
      </Button>
    );
    const link = screen.getByText('As Child Link');
    expect(link).toBeInTheDocument();
    expect(link.tagName).toBe('A');
  });

  // Test custom className merging
    test('merges custom className correctly', () => {
      render(<Button className="my-custom-test-class">Custom Class</Button>);
      const button = screen.getByText('Custom Class');

      // It should have the newly added class
      expect(button).toHaveClass('my-custom-test-class');
      // It should also retain the base classes defined in cva
      expect(button).toHaveClass('inline-flex', 'items-center');
    });

  // Test native HTML props forwarding
    test('passes native HTML attributes correctly', () => {
      render(
        <Button type="submit" aria-label="Submit Form" data-testid="submit-btn">
          Submit
        </Button>
      );
      const button = screen.getByTestId('submit-btn');

      expect(button).toHaveAttribute('type', 'submit');
      expect(button).toHaveAttribute('aria-label', 'Submit Form');
    });

  // Optimized: Test buttons with different variants by checking classes
    test('applies specific classes for different variants', () => {
      render(<Button variant="destructive">Destructive</Button>);
      const destructiveButton = screen.getByText('Destructive');
      // Verify that the specific Tailwind class from cva is applied
      expect(destructiveButton).toHaveClass('bg-destructive', 'text-white');

      render(<Button variant="outline">Outline</Button>);
      const outlineButton = screen.getByText('Outline');
      expect(outlineButton).toHaveClass('border', 'bg-background');
    });
});