[#MUI-11] Création d'un composant navigation par onglets (#16)
| Numéro du ticket | Titre du ticket | |------------------|-----------------| | | | ## Description de la PR ## Modification du .env ## Check list - [ ] Pas de régression - [ ] TU/TI/TF rédigée - [ ] TU/TI/TF OK - [ ] CHANGELOG modifié Reviewed-on: #16 Co-authored-by: tristan <tristan@yuno.malio.fr> Co-committed-by: tristan <tristan@yuno.malio.fr>
This commit was merged in pull request #16.
This commit is contained in:
137
app/components/malio/tab/TabList.test.ts
Normal file
137
app/components/malio/tab/TabList.test.ts
Normal file
@@ -0,0 +1,137 @@
|
||||
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 TabList from './TabList.vue'
|
||||
|
||||
type Tab = {
|
||||
key: string
|
||||
label: string
|
||||
icon?: string
|
||||
}
|
||||
|
||||
type TabListProps = {
|
||||
tabs: Tab[]
|
||||
modelValue?: string
|
||||
id?: string
|
||||
}
|
||||
|
||||
const TabListForTest = TabList as DefineComponent<TabListProps>
|
||||
|
||||
const tabs: Tab[] = [
|
||||
{key: 'home', label: 'Accueil', icon: 'mdi:home'},
|
||||
{key: 'settings', label: 'Paramètres'},
|
||||
{key: 'profile', label: 'Profil', icon: 'mdi:account'},
|
||||
]
|
||||
|
||||
function mountComponent(props: TabListProps, slots?: Record<string, string>) {
|
||||
return mount(TabListForTest, {
|
||||
props,
|
||||
slots,
|
||||
})
|
||||
}
|
||||
|
||||
describe('MalioTabList', () => {
|
||||
it('renders all tab buttons with correct labels', () => {
|
||||
const wrapper = mountComponent({tabs})
|
||||
const buttons = wrapper.findAll('[role="tab"]')
|
||||
expect(buttons).toHaveLength(3)
|
||||
expect(buttons[0].text()).toContain('Accueil')
|
||||
expect(buttons[1].text()).toContain('Paramètres')
|
||||
expect(buttons[2].text()).toContain('Profil')
|
||||
})
|
||||
|
||||
it('renders icons for tabs that have one', () => {
|
||||
const wrapper = mountComponent({tabs})
|
||||
const buttons = wrapper.findAll('[role="tab"]')
|
||||
expect(buttons[0].find('svg').exists()).toBe(true)
|
||||
expect(buttons[2].find('svg').exists()).toBe(true)
|
||||
})
|
||||
|
||||
it('does not render icon when tab has no icon', () => {
|
||||
const wrapper = mountComponent({tabs})
|
||||
const buttons = wrapper.findAll('[role="tab"]')
|
||||
expect(buttons[1].find('svg').exists()).toBe(false)
|
||||
})
|
||||
|
||||
it('first tab is active by default in uncontrolled mode', () => {
|
||||
const wrapper = mountComponent({tabs})
|
||||
const buttons = wrapper.findAll('[role="tab"]')
|
||||
expect(buttons[0].attributes('aria-selected')).toBe('true')
|
||||
expect(buttons[1].attributes('aria-selected')).toBe('false')
|
||||
expect(buttons[2].attributes('aria-selected')).toBe('false')
|
||||
})
|
||||
|
||||
it('shows the panel content for the active tab (v-show)', () => {
|
||||
const wrapper = mountComponent({tabs}, {
|
||||
home: '<p>Home content</p>',
|
||||
settings: '<p>Settings content</p>',
|
||||
profile: '<p>Profile content</p>',
|
||||
})
|
||||
const panels = wrapper.findAll('[role="tabpanel"]')
|
||||
expect(panels[0].attributes('style')).toBeUndefined()
|
||||
expect(panels[1].attributes('style')).toContain('display: none')
|
||||
expect(panels[2].attributes('style')).toContain('display: none')
|
||||
})
|
||||
|
||||
it('switches tab on click in uncontrolled mode', async () => {
|
||||
const wrapper = mountComponent({tabs})
|
||||
const buttons = wrapper.findAll('[role="tab"]')
|
||||
|
||||
await buttons[1].trigger('click')
|
||||
|
||||
expect(buttons[1].attributes('aria-selected')).toBe('true')
|
||||
expect(buttons[0].attributes('aria-selected')).toBe('false')
|
||||
})
|
||||
|
||||
it('emits update:modelValue on click in controlled mode', async () => {
|
||||
const wrapper = mountComponent({tabs, modelValue: 'home'})
|
||||
const buttons = wrapper.findAll('[role="tab"]')
|
||||
|
||||
await buttons[2].trigger('click')
|
||||
|
||||
expect(wrapper.emitted('update:modelValue')?.[0]).toEqual(['profile'])
|
||||
})
|
||||
|
||||
it('respects modelValue for active tab in controlled mode', () => {
|
||||
const wrapper = mountComponent({tabs, modelValue: 'settings'})
|
||||
const buttons = wrapper.findAll('[role="tab"]')
|
||||
expect(buttons[0].attributes('aria-selected')).toBe('false')
|
||||
expect(buttons[1].attributes('aria-selected')).toBe('true')
|
||||
expect(buttons[2].attributes('aria-selected')).toBe('false')
|
||||
})
|
||||
|
||||
it('sets correct aria-controls and aria-labelledby', () => {
|
||||
const wrapper = mountComponent({tabs, id: 'test'})
|
||||
const buttons = wrapper.findAll('[role="tab"]')
|
||||
const panels = wrapper.findAll('[role="tabpanel"]')
|
||||
|
||||
expect(buttons[0].attributes('aria-controls')).toBe('test-panel-home')
|
||||
expect(buttons[1].attributes('aria-controls')).toBe('test-panel-settings')
|
||||
expect(panels[0].attributes('aria-labelledby')).toBe('test-tab-home')
|
||||
expect(panels[1].attributes('aria-labelledby')).toBe('test-tab-settings')
|
||||
})
|
||||
|
||||
it('has role="tablist" on the tab container', () => {
|
||||
const wrapper = mountComponent({tabs})
|
||||
expect(wrapper.find('[role="tablist"]').exists()).toBe(true)
|
||||
})
|
||||
|
||||
it('active tab has tabindex 0, others have -1', () => {
|
||||
const wrapper = mountComponent({tabs})
|
||||
const buttons = wrapper.findAll('[role="tab"]')
|
||||
expect(buttons[0].attributes('tabindex')).toBe('0')
|
||||
expect(buttons[1].attributes('tabindex')).toBe('-1')
|
||||
expect(buttons[2].attributes('tabindex')).toBe('-1')
|
||||
})
|
||||
|
||||
it('renders icon props correctly via findComponent', () => {
|
||||
const wrapper = mount(TabListForTest, {
|
||||
props: {tabs},
|
||||
})
|
||||
const icons = wrapper.findAllComponents(IconifyIcon)
|
||||
expect(icons).toHaveLength(2)
|
||||
expect(icons[0].props('icon')).toBe('mdi:home')
|
||||
expect(icons[1].props('icon')).toBe('mdi:account')
|
||||
})
|
||||
})
|
||||
Reference in New Issue
Block a user