import { describe, expect, it } from 'vitest' import { mount } from '@vue/test-utils' import type { DefineComponent } from 'vue' import { Icon as IconifyIcon } from '@iconify/vue' import Button from './Button.vue' type ButtonProps = { id?: string label?: string disabled?: boolean buttonClass?: string variant?: 'primary' | 'secondary' | 'tertiary' | 'danger' iconName?: string iconPosition?: 'left' | 'right' iconSize?: string | number } const ButtonForTest = Button as DefineComponent const mountComponent = (props: ButtonProps = {}, slots?: Record) => mount(ButtonForTest, { props, slots, global: { stubs: { IconifyIcon: { template: '', }, }, }, }) describe('MalioButton', () => { it('renders a button with label', () => { const wrapper = mountComponent({ label: 'Valider' }) expect(wrapper.find('button').exists()).toBe(true) expect(wrapper.text()).toContain('Valider') }) it('renders slot content over label prop', () => { const wrapper = mountComponent({ label: 'Prop' }, { default: 'Slot content' }) expect(wrapper.text()).toContain('Slot content') expect(wrapper.text()).not.toContain('Prop') }) it('uses provided id on button', () => { const wrapper = mountComponent({ id: 'custom-id' }) expect(wrapper.get('button').attributes('id')).toBe('custom-id') }) it('generates an id when missing', () => { const wrapper = mountComponent() const buttonId = wrapper.get('button').attributes('id') expect(buttonId?.startsWith('malio-button-')).toBe(true) }) it('sets type="button" on the button', () => { const wrapper = mountComponent() expect(wrapper.get('button').attributes('type')).toBe('button') }) it('emits click event when clicked', async () => { const wrapper = mountComponent() await wrapper.get('button').trigger('click') expect(wrapper.emitted('click')).toHaveLength(1) }) it('does not emit click when disabled', async () => { const wrapper = mountComponent({ disabled: true }) await wrapper.get('button').trigger('click') expect(wrapper.emitted('click')).toBeUndefined() }) it('sets disabled attribute when disabled', () => { const wrapper = mountComponent({ disabled: true }) expect(wrapper.get('button').attributes('disabled')).toBeDefined() }) // --- Variant: Primary (default) --- it('applies primary variant by default', () => { const wrapper = mountComponent() expect(wrapper.get('button').classes()).toContain('bg-m-btn-primary') expect(wrapper.get('button').classes()).toContain('text-white') expect(wrapper.get('button').classes()).toContain('cursor-pointer') }) it('applies primary disabled styles', () => { const wrapper = mountComponent({ disabled: true }) expect(wrapper.get('button').classes()).toContain('bg-m-disabled') expect(wrapper.get('button').classes()).toContain('text-white') expect(wrapper.get('button').classes()).toContain('cursor-not-allowed') }) // --- Variant: Secondary --- it('applies secondary variant', () => { const wrapper = mountComponent({ variant: 'secondary' }) expect(wrapper.get('button').classes()).toContain('bg-m-btn-secondary') expect(wrapper.get('button').classes()).toContain('text-white') }) it('applies secondary disabled styles', () => { const wrapper = mountComponent({ variant: 'secondary', disabled: true }) expect(wrapper.get('button').classes()).toContain('bg-m-disabled') expect(wrapper.get('button').classes()).toContain('cursor-not-allowed') }) // --- Variant: Tertiary --- it('applies tertiary variant with border and no background', () => { const wrapper = mountComponent({ variant: 'tertiary' }) expect(wrapper.get('button').classes()).toContain('border') expect(wrapper.get('button').classes()).toContain('border-m-btn-primary') expect(wrapper.get('button').classes()).toContain('text-m-btn-primary') expect(wrapper.get('button').classes()).toContain('bg-transparent') expect(wrapper.get('button').classes()).not.toContain('text-white') }) it('applies tertiary disabled styles with border', () => { const wrapper = mountComponent({ variant: 'tertiary', disabled: true }) expect(wrapper.get('button').classes()).toContain('border') expect(wrapper.get('button').classes()).toContain('border-m-disabled') expect(wrapper.get('button').classes()).toContain('text-m-disabled') expect(wrapper.get('button').classes()).toContain('bg-transparent') }) // --- Variant: Danger --- it('applies danger variant', () => { const wrapper = mountComponent({ variant: 'danger' }) expect(wrapper.get('button').classes()).toContain('bg-m-btn-danger') expect(wrapper.get('button').classes()).toContain('text-white') }) it('applies danger disabled styles', () => { const wrapper = mountComponent({ variant: 'danger', disabled: true }) expect(wrapper.get('button').classes()).toContain('bg-m-disabled') expect(wrapper.get('button').classes()).toContain('cursor-not-allowed') }) // --- Sizing --- it('applies correct dimensions', () => { const wrapper = mountComponent() expect(wrapper.get('button').classes()).toContain('w-[240px]') expect(wrapper.get('button').classes()).toContain('h-[40px]') }) it('applies font styles', () => { const wrapper = mountComponent() expect(wrapper.get('button').classes()).toContain('text-base') expect(wrapper.get('button').classes()).toContain('font-bold') }) // --- buttonClass override --- it('applies buttonClass', () => { const wrapper = mountComponent({ buttonClass: 'w-full rounded-full' }) expect(wrapper.get('button').classes()).toContain('w-full') expect(wrapper.get('button').classes()).toContain('rounded-full') }) // --- Icon --- it('renders icon on the right by default', () => { const wrapper = mountComponent({ iconName: 'mdi:arrow-right' }) expect(wrapper.find('[data-test="icon-right"]').exists()).toBe(true) expect(wrapper.find('[data-test="icon-left"]').exists()).toBe(false) }) it('renders icon on the left when specified', () => { const wrapper = mountComponent({ iconName: 'mdi:arrow-left', iconPosition: 'left' }) expect(wrapper.find('[data-test="icon-left"]').exists()).toBe(true) expect(wrapper.find('[data-test="icon-right"]').exists()).toBe(false) }) it('does not render icon when iconName is empty', () => { const wrapper = mountComponent() expect(wrapper.find('[data-test="icon-left"]').exists()).toBe(false) expect(wrapper.find('[data-test="icon-right"]').exists()).toBe(false) }) it('passes icon name and size to icon component', () => { const wrapper = mount(ButtonForTest, { props: { iconName: 'mdi:check', iconSize: 18 }, }) const iconComponent = wrapper.findComponent(IconifyIcon) expect(iconComponent.props('icon')).toBe('mdi:check') expect(iconComponent.props('width')).toBe(18) expect(iconComponent.props('height')).toBe(18) }) })