import {describe, expect, it} from 'vitest' import {mount} from '@vue/test-utils' import type {DefineComponent} from 'vue' import Select from './Select.vue' type Option = { label: string value: string | number | null } type SelectProps = { modelValue?: string | number | null options?: Option[] emptyOptionLabel?: string label?: string hint?: string error?: string success?: string minWidth?: string maxWidth?: string textField?: string textValue?: string textLabel?: string rounded?: string disabled?: boolean } const SelectForTest = Select as DefineComponent const options: Option[] = [ {label: 'France', value: 'fr'}, {label: 'Belgique', value: 'be'}, {label: 'Canada', value: 'ca'}, ] describe('MalioSelect', () => { it('renders the label text', () => { const wrapper = mount(SelectForTest, { props: {modelValue: null, label: 'Country'}, }) expect(wrapper.get('label').text()).toBe('Country') }) it('generates button and listbox ids and links them together', async () => { const wrapper = mount(SelectForTest, { props: {modelValue: null, options}, }) const button = wrapper.get('button') expect(button.attributes('id')?.startsWith('custom-select-btn-')).toBe(true) expect(button.attributes('aria-controls')?.startsWith('custom-select-listbox-')).toBe(true) await button.trigger('click') expect(wrapper.get('ul').attributes('id')).toBe(button.attributes('aria-controls')) }) it('uses disabled styles and prevents opening when disabled', async () => { const wrapper = mount(SelectForTest, { props: {modelValue: null, disabled: true, options}, }) const button = wrapper.get('button') expect(button.attributes('disabled')).toBeDefined() expect(button.classes()).toContain('cursor-not-allowed') await button.trigger('click') expect(wrapper.find('ul').exists()).toBe(false) }) it('opens the list and rotates the icon on click', async () => { const wrapper = mount(SelectForTest, { props: {modelValue: null, options}, }) await wrapper.get('button').trigger('click') expect(wrapper.get('ul').exists()).toBe(true) expect(wrapper.get('button').attributes('aria-expanded')).toBe('true') expect(wrapper.get('svg').classes()).toContain('rotate-180') }) it('emits update:modelValue when selecting an option', async () => { const wrapper = mount(SelectForTest, { props: {modelValue: null, options}, }) await wrapper.get('button').trigger('click') await wrapper.findAll('li')[2].trigger('click') expect(wrapper.emitted('update:modelValue')?.[0]).toEqual(['be']) }) it('renders the empty option with muted text style', async () => { const wrapper = mount(SelectForTest, { props: { modelValue: null, options, emptyOptionLabel: 'Aucune selection', }, }) await wrapper.get('button').trigger('click') const firstOption = wrapper.findAll('li')[0] expect(firstOption.text()).toBe('Aucune selection') expect(firstOption.classes()).toContain('text-black/40') }) it('shows the selected value text when an option is selected', () => { const wrapper = mount(SelectForTest, { props: { options, modelValue: 'fr', }, }) expect(wrapper.text()).toContain('France') expect(wrapper.get('button').classes()).toContain('border-black') }) it('shows hint message in muted color', () => { const wrapper = mount(SelectForTest, { props: {modelValue: null, hint: 'Select a country'}, }) expect(wrapper.get('p.text-m-muted').text()).toBe('Select a country') }) it('shows error state on button, label and helper text', () => { const wrapper = mount(SelectForTest, { props: { modelValue: null, options, label: 'Country', error: 'Selection error', }, }) expect(wrapper.get('button').classes()).toContain('border-m-error') expect(wrapper.get('label').classes()).toContain('text-m-error') expect(wrapper.get('p.text-m-error').text()).toBe('Selection error') expect(wrapper.get('button').attributes('aria-invalid')).toBe('true') }) it('shows success state on button, label and helper text', () => { const wrapper = mount(SelectForTest, { props: { modelValue: null, options, label: 'Country', success: 'Selection success', }, }) expect(wrapper.get('button').classes()).toContain('border-m-success') expect(wrapper.get('label').classes()).toContain('text-m-success') expect(wrapper.get('p.text-m-success').text()).toBe('Selection success') }) it('prioritizes error over success', () => { const wrapper = mount(SelectForTest, { props: { modelValue: null, options, error: 'Selection error', success: 'Selection success', }, }) expect(wrapper.get('button').classes()).toContain('border-m-error') expect(wrapper.find('p.text-m-success').exists()).toBe(false) expect(wrapper.get('p.text-m-error').text()).toBe('Selection error') }) })