import { describe, it, expect } from 'vitest' import { mount } from '@vue/test-utils' import ButtonGroup from '~/components/UI/ButtonGroup.vue' import type { ButtonGroupOption } from '~/components/UI/ButtonGroup.vue' describe('ButtonGroup', () => { const mockOptions: ButtonGroupOption[] = [ { value: 'option1', label: 'Option 1' }, { value: 'option2', label: 'Option 2' }, { value: 'option3', label: 'Option 3' }, ] describe('Rendering', () => { it('renders all options', () => { const wrapper = mount(ButtonGroup, { props: { options: mockOptions, modelValue: 'option1', }, }) const buttons = wrapper.findAll('button') expect(buttons).toHaveLength(3) expect(buttons[0].text()).toContain('Option 1') expect(buttons[1].text()).toContain('Option 2') expect(buttons[2].text()).toContain('Option 3') }) it('renders with icons when provided', () => { const optionsWithIcons: ButtonGroupOption[] = [ { value: 'opt1', label: 'Option 1', icon: '🎯' }, { value: 'opt2', label: 'Option 2', icon: '⚡' }, ] const wrapper = mount(ButtonGroup, { props: { options: optionsWithIcons, modelValue: 'opt1', }, }) expect(wrapper.html()).toContain('🎯') expect(wrapper.html()).toContain('⚡') }) it('renders with badges when provided', () => { const optionsWithBadges: ButtonGroupOption[] = [ { value: 'opt1', label: 'Option 1', badge: '5' }, { value: 'opt2', label: 'Option 2', badge: 10 }, ] const wrapper = mount(ButtonGroup, { props: { options: optionsWithBadges, modelValue: 'opt1', }, }) expect(wrapper.text()).toContain('5') expect(wrapper.text()).toContain('10') }) }) describe('Selection', () => { it('applies selected styles to current value', () => { const wrapper = mount(ButtonGroup, { props: { options: mockOptions, modelValue: 'option2', }, }) const buttons = wrapper.findAll('button') expect(buttons[1].classes()).toContain('from-primary') expect(buttons[0].classes()).toContain('bg-white') expect(buttons[2].classes()).toContain('bg-white') }) it('emits update:modelValue when option is clicked', async () => { const wrapper = mount(ButtonGroup, { props: { options: mockOptions, modelValue: 'option1', }, }) await wrapper.findAll('button')[2].trigger('click') expect(wrapper.emitted('update:modelValue')).toBeTruthy() expect(wrapper.emitted('update:modelValue')![0]).toEqual(['option3']) }) it('does not emit when disabled', async () => { const wrapper = mount(ButtonGroup, { props: { options: mockOptions, modelValue: 'option1', disabled: true, }, }) await wrapper.findAll('button')[1].trigger('click') expect(wrapper.emitted('update:modelValue')).toBeFalsy() }) }) describe('Variants', () => { it('uses primary variant by default', () => { const wrapper = mount(ButtonGroup, { props: { options: mockOptions, modelValue: 'option1', variant: 'primary', }, }) const selectedButton = wrapper.findAll('button')[0] expect(selectedButton.classes()).toContain('from-primary') }) it('applies secondary variant when specified', () => { const wrapper = mount(ButtonGroup, { props: { options: mockOptions, modelValue: 'option1', variant: 'secondary', }, }) const selectedButton = wrapper.findAll('button')[0] expect(selectedButton.classes()).toContain('from-gray-600') }) }) describe('Sizes', () => { it('applies small size classes', () => { const wrapper = mount(ButtonGroup, { props: { options: mockOptions, modelValue: 'option1', size: 'sm', }, }) const button = wrapper.find('button') expect(button.classes()).toContain('px-3') expect(button.classes()).toContain('py-1.5') }) it('applies medium size classes', () => { const wrapper = mount(ButtonGroup, { props: { options: mockOptions, modelValue: 'option1', size: 'md', }, }) const button = wrapper.find('button') expect(button.classes()).toContain('px-4') expect(button.classes()).toContain('py-2.5') }) it('applies large size classes', () => { const wrapper = mount(ButtonGroup, { props: { options: mockOptions, modelValue: 'option1', size: 'lg', }, }) const button = wrapper.find('button') expect(button.classes()).toContain('px-5') expect(button.classes()).toContain('py-3') }) }) describe('Layout', () => { it('renders horizontal layout by default', () => { const wrapper = mount(ButtonGroup, { props: { options: mockOptions, modelValue: 'option1', }, }) expect(wrapper.find('div').classes()).toContain('flex-row') }) it('renders vertical layout when specified', () => { const wrapper = mount(ButtonGroup, { props: { options: mockOptions, modelValue: 'option1', vertical: true, }, }) expect(wrapper.find('div').classes()).toContain('flex-col') expect(wrapper.find('div').classes()).toContain('w-full') }) it('applies full width to buttons in vertical mode', () => { const wrapper = mount(ButtonGroup, { props: { options: mockOptions, modelValue: 'option1', vertical: true, }, }) const buttons = wrapper.findAll('button') buttons.forEach(button => { expect(button.classes()).toContain('w-full') }) }) }) describe('Border Radius', () => { it('rounds first button left in horizontal mode', () => { const wrapper = mount(ButtonGroup, { props: { options: mockOptions, modelValue: 'option1', vertical: false, }, }) const buttons = wrapper.findAll('button') expect(buttons[0].classes()).toContain('rounded-l-lg') }) it('rounds last button right in horizontal mode', () => { const wrapper = mount(ButtonGroup, { props: { options: mockOptions, modelValue: 'option1', vertical: false, }, }) const buttons = wrapper.findAll('button') expect(buttons[2].classes()).toContain('rounded-r-lg') }) it('rounds first button top in vertical mode', () => { const wrapper = mount(ButtonGroup, { props: { options: mockOptions, modelValue: 'option1', vertical: true, }, }) const buttons = wrapper.findAll('button') expect(buttons[0].classes()).toContain('rounded-t-lg') }) it('rounds last button bottom in vertical mode', () => { const wrapper = mount(ButtonGroup, { props: { options: mockOptions, modelValue: 'option1', vertical: true, }, }) const buttons = wrapper.findAll('button') expect(buttons[2].classes()).toContain('rounded-b-lg') }) }) describe('Disabled State', () => { it('disables all buttons when disabled prop is true', () => { const wrapper = mount(ButtonGroup, { props: { options: mockOptions, modelValue: 'option1', disabled: true, }, }) const buttons = wrapper.findAll('button') buttons.forEach(button => { expect(button.attributes('disabled')).toBe('') }) }) it('applies disabled styles', () => { const wrapper = mount(ButtonGroup, { props: { options: mockOptions, modelValue: 'option1', disabled: true, }, }) const buttons = wrapper.findAll('button') buttons.forEach(button => { expect(button.classes()).toContain('disabled:opacity-50') expect(button.classes()).toContain('disabled:cursor-not-allowed') }) }) }) describe('Type Attribute', () => { it('sets type to button by default', () => { const wrapper = mount(ButtonGroup, { props: { options: mockOptions, modelValue: 'option1', }, }) wrapper.findAll('button').forEach(button => { expect(button.attributes('type')).toBe('button') }) }) it('sets custom type when specified', () => { const wrapper = mount(ButtonGroup, { props: { options: mockOptions, modelValue: 'option1', type: 'submit', }, }) wrapper.findAll('button').forEach(button => { expect(button.attributes('type')).toBe('submit') }) }) }) })