import { describe, it, expect } from 'vitest' import { mount } from '@vue/test-utils' import ToggleSwitch from '~/components/UI/ToggleSwitch.vue' describe('ToggleSwitch', () => { describe('Rendering', () => { it('renders toggle switch', () => { const wrapper = mount(ToggleSwitch, { props: { modelValue: false, }, }) expect(wrapper.find('button[role="switch"]').exists()).toBe(true) }) it('renders with label when provided', () => { const wrapper = mount(ToggleSwitch, { props: { modelValue: false, label: 'Enable Feature', }, }) expect(wrapper.text()).toContain('Enable Feature') }) it('does not render label when not provided', () => { const wrapper = mount(ToggleSwitch, { props: { modelValue: false, }, }) expect(wrapper.find('label').exists()).toBe(false) }) }) describe('States', () => { it('shows off state when modelValue is false', () => { const wrapper = mount(ToggleSwitch, { props: { modelValue: false, }, }) const button = wrapper.find('button') expect(button.attributes('aria-checked')).toBe('false') }) it('shows on state when modelValue is true', () => { const wrapper = mount(ToggleSwitch, { props: { modelValue: true, }, }) const button = wrapper.find('button') expect(button.attributes('aria-checked')).toBe('true') }) it('applies green gradient when on', () => { const wrapper = mount(ToggleSwitch, { props: { modelValue: true, }, }) const track = wrapper.find('span[aria-hidden="true"]') expect(track.classes()).toContain('from-green-500') }) it('applies gray background when off', () => { const wrapper = mount(ToggleSwitch, { props: { modelValue: false, }, }) const track = wrapper.find('span[aria-hidden="true"]') expect(track.classes()).toContain('bg-gray-300') }) }) describe('Thumb Position', () => { it('positions thumb left when off', () => { const wrapper = mount(ToggleSwitch, { props: { modelValue: false, size: 'md', }, }) const thumb = wrapper.findAll('span[aria-hidden="true"]')[1] expect(thumb.classes()).toContain('translate-x-0.5') }) it('positions thumb right when on', () => { const wrapper = mount(ToggleSwitch, { props: { modelValue: true, size: 'md', }, }) const thumb = wrapper.findAll('span[aria-hidden="true"]')[1] expect(thumb.classes()).toContain('translate-x-5') }) }) describe('Sizes', () => { it('applies small size classes', () => { const wrapper = mount(ToggleSwitch, { props: { modelValue: false, size: 'sm', }, }) const button = wrapper.find('button') expect(button.classes()).toContain('h-5') expect(button.classes()).toContain('w-9') }) it('applies medium size classes', () => { const wrapper = mount(ToggleSwitch, { props: { modelValue: false, size: 'md', }, }) const button = wrapper.find('button') expect(button.classes()).toContain('h-6') expect(button.classes()).toContain('w-11') }) it('applies large size classes', () => { const wrapper = mount(ToggleSwitch, { props: { modelValue: false, size: 'lg', }, }) const button = wrapper.find('button') expect(button.classes()).toContain('h-7') expect(button.classes()).toContain('w-14') }) }) describe('Interactions', () => { it('emits update:modelValue with true when clicked while off', async () => { const wrapper = mount(ToggleSwitch, { props: { modelValue: false, }, }) await wrapper.find('button').trigger('click') expect(wrapper.emitted('update:modelValue')).toBeTruthy() expect(wrapper.emitted('update:modelValue')![0]).toEqual([true]) }) it('emits update:modelValue with false when clicked while on', async () => { const wrapper = mount(ToggleSwitch, { props: { modelValue: true, }, }) await wrapper.find('button').trigger('click') expect(wrapper.emitted('update:modelValue')).toBeTruthy() expect(wrapper.emitted('update:modelValue')![0]).toEqual([false]) }) it('clicking label toggles switch', async () => { const wrapper = mount(ToggleSwitch, { props: { modelValue: false, label: 'Test Label', }, }) await wrapper.find('label').trigger('click') expect(wrapper.emitted('update:modelValue')).toBeTruthy() expect(wrapper.emitted('update:modelValue')![0]).toEqual([true]) }) }) describe('Disabled State', () => { it('disables button when disabled prop is true', () => { const wrapper = mount(ToggleSwitch, { props: { modelValue: false, disabled: true, }, }) expect(wrapper.find('button').attributes('disabled')).toBe('') }) it('does not emit when disabled', async () => { const wrapper = mount(ToggleSwitch, { props: { modelValue: false, disabled: true, }, }) await wrapper.find('button').trigger('click') expect(wrapper.emitted('update:modelValue')).toBeFalsy() }) it('applies disabled styles to button', () => { const wrapper = mount(ToggleSwitch, { props: { modelValue: false, disabled: true, }, }) const button = wrapper.find('button') expect(button.classes()).toContain('disabled:opacity-50') expect(button.classes()).toContain('disabled:cursor-not-allowed') }) it('applies disabled styles to label', () => { const wrapper = mount(ToggleSwitch, { props: { modelValue: false, disabled: true, label: 'Test', }, }) const label = wrapper.find('label') expect(label.classes()).toContain('text-gray-400') expect(label.classes()).toContain('cursor-not-allowed') }) }) describe('Accessibility', () => { it('has correct role attribute', () => { const wrapper = mount(ToggleSwitch, { props: { modelValue: false, }, }) expect(wrapper.find('button').attributes('role')).toBe('switch') }) it('has correct aria-checked attribute', () => { const wrapper = mount(ToggleSwitch, { props: { modelValue: true, }, }) expect(wrapper.find('button').attributes('aria-checked')).toBe('true') }) it('updates aria-checked when value changes', async () => { const wrapper = mount(ToggleSwitch, { props: { modelValue: false, }, }) await wrapper.setProps({ modelValue: true }) expect(wrapper.find('button').attributes('aria-checked')).toBe('true') }) it('inner elements have aria-hidden', () => { const wrapper = mount(ToggleSwitch, { props: { modelValue: false, }, }) const innerElements = wrapper.findAll('span[aria-hidden="true"]') expect(innerElements.length).toBeGreaterThan(0) }) }) })