mitlist/fe/src/components/valerie/tabs/VTabs.spec.ts

136 lines
5.5 KiB
TypeScript

import { mount } from '@vue/test-utils';
import VTabs from './VTabs.vue';
import VTab from './VTab.vue';
import VTabList from './VTabList.vue';
import VTabPanel from './VTabPanel.vue';
import VTabPanels from './VTabPanels.vue';
import { TabsProviderKey } from './types';
import { nextTick, h } from 'vue';
// Helper to create a minimal tabs structure for testing VTabs logic
const createBasicTabsStructure = (activeTabId: string | null = 'tab1') => {
return {
components: { VTabs, VTabList, VTab, VTabPanels, VTabPanel },
template: `
<VTabs :modelValue="currentModelValue" @update:modelValue="val => currentModelValue = val" :initialTab="initialTabValue">
<VTabList>
<VTab id="tab1" title="Tab 1" />
<VTab id="tab2" title="Tab 2" />
</VTabList>
<VTabPanels>
<VTabPanel id="tab1"><p>Content 1</p></VTabPanel>
<VTabPanel id="tab2"><p>Content 2</p></VTabPanel>
</VTabPanels>
</VTabs>
`,
data() {
return {
currentModelValue: activeTabId,
initialTabValue: activeTabId, // Can be overridden in test
};
},
};
};
describe('VTabs.vue', () => {
it('initializes activeTabId with modelValue', () => {
const wrapper = mount(VTabs, {
props: { modelValue: 'second' },
slots: { default: '<VTabList><VTab id="first"/><VTab id="second"/></VTabList><VTabPanels><VTabPanel id="first"/><VTabPanel id="second"/></VTabPanels>' },
global: { components: { VTabList, VTab, VTabPanels, VTabPanel } } // Stubbing children
});
const context = wrapper.vm.$.provides[TabsProviderKey as any];
expect(context.activeTabId.value).toBe('second');
});
it('initializes activeTabId with initialTab if modelValue is not provided', () => {
const wrapper = mount(VTabs, {
props: { initialTab: 'third' },
slots: { default: '<VTabList><VTab id="first"/><VTab id="third"/></VTabList><VTabPanels><VTabPanel id="first"/><VTabPanel id="third"/></VTabPanels>' },
global: { components: { VTabList, VTab, VTabPanels, VTabPanel } }
});
const context = wrapper.vm.$.provides[TabsProviderKey as any];
expect(context.activeTabId.value).toBe('third');
});
it('updates activeTabId when modelValue prop changes', async () => {
const wrapper = mount(VTabs, {
props: { modelValue: 'one' },
slots: { default: '<VTabList><VTab id="one"/><VTab id="two"/></VTabList><VTabPanels><VTabPanel id="one"/><VTabPanel id="two"/></VTabPanels>' },
global: { components: { VTabList, VTab, VTabPanels, VTabPanel } }
});
const context = wrapper.vm.$.provides[TabsProviderKey as any];
expect(context.activeTabId.value).toBe('one');
await wrapper.setProps({ modelValue: 'two' });
expect(context.activeTabId.value).toBe('two');
});
it('emits update:modelValue when selectTab is called', async () => {
const wrapper = mount(VTabs, {
props: { modelValue: 'alpha' },
slots: { default: '<VTabList><VTab id="alpha"/><VTab id="beta"/></VTabList><VTabPanels><VTabPanel id="alpha"/><VTabPanel id="beta"/></VTabPanels>' },
global: { components: { VTabList, VTab, VTabPanels, VTabPanel } }
});
const context = wrapper.vm.$.provides[TabsProviderKey as any];
context.selectTab('beta');
await nextTick();
expect(wrapper.emitted()['update:modelValue']).toBeTruthy();
expect(wrapper.emitted()['update:modelValue'][0]).toEqual(['beta']);
expect(context.activeTabId.value).toBe('beta');
});
it('selects the first tab if no modelValue or initialTab is provided on mount', async () => {
// This test is more involved as it requires inspecting slot children
// We need to ensure VTab components are actually rendered within the slots
const TestComponent = {
components: { VTabs, VTabList, VTab, VTabPanels, VTabPanel },
template: `
<VTabs>
<VTabList>
<VTab id="firstMounted" title="First" />
<VTab id="secondMounted" title="Second" />
</VTabList>
<VTabPanels>
<VTabPanel id="firstMounted">Content First</VTabPanel>
<VTabPanel id="secondMounted">Content Second</VTabPanel>
</VTabPanels>
</VTabs>
`,
};
const wrapper = mount(TestComponent);
await nextTick(); // Wait for onMounted hook in VTabs
// Access VTabs instance to check its internal activeTabId via provided context
const vTabsInstance = wrapper.findComponent(VTabs);
const context = vTabsInstance.vm.$.provides[TabsProviderKey as any];
expect(context.activeTabId.value).toBe('firstMounted');
});
it('does not change activeTabId if modelValue is explicitly null and no initialTab', async () => {
const TestComponent = {
components: { VTabs, VTabList, VTab, VTabPanels, VTabPanel },
template: `
<VTabs :modelValue="null">
<VTabList> <VTab id="t1" /> </VTabList>
<VTabPanels> <VTabPanel id="t1" /> </VTabPanels>
</VTabs>
`,
};
const wrapper = mount(TestComponent);
await nextTick();
const vTabsInstance = wrapper.findComponent(VTabs);
const context = vTabsInstance.vm.$.provides[TabsProviderKey as any];
expect(context.activeTabId.value).toBeNull(); // Should remain null, not default to first tab
});
it('renders its default slot content', () => {
const wrapper = mount(VTabs, {
slots: { default: '<div class="test-slot-content">Hello</div>' },
});
expect(wrapper.find('.test-slot-content').exists()).toBe(true);
expect(wrapper.text()).toContain('Hello');
});
});