160 lines
5.5 KiB
TypeScript
160 lines
5.5 KiB
TypeScript
import { mount } from '@vue/test-utils';
|
|
import VButton from './VButton.vue';
|
|
import VIcon from './VIcon.vue'; // Import VIcon as it's a child component
|
|
import { describe, it, expect, vi } from 'vitest';
|
|
|
|
// Mock VIcon to simplify testing VButton in isolation if needed,
|
|
// or allow it to render if its behavior is simple and reliable.
|
|
// For now, we'll allow it to render as it's part of the visual output.
|
|
|
|
describe('VButton.vue', () => {
|
|
it('renders with default props', () => {
|
|
const wrapper = mount(VButton);
|
|
expect(wrapper.text()).toBe('Button');
|
|
expect(wrapper.classes()).toContain('btn');
|
|
expect(wrapper.classes()).toContain('btn-primary');
|
|
expect(wrapper.classes()).toContain('btn-md');
|
|
expect(wrapper.attributes('type')).toBe('button');
|
|
expect(wrapper.attributes('disabled')).toBeUndefined();
|
|
});
|
|
|
|
it('renders with specified label', () => {
|
|
const wrapper = mount(VButton, { props: { label: 'Click Me' } });
|
|
expect(wrapper.text()).toBe('Click Me');
|
|
});
|
|
|
|
it('renders with slot content', () => {
|
|
const wrapper = mount(VButton, {
|
|
slots: {
|
|
default: '<i>Slot Content</i>',
|
|
},
|
|
});
|
|
expect(wrapper.html()).toContain('<i>Slot Content</i>');
|
|
});
|
|
|
|
it('applies variant classes', () => {
|
|
const wrapper = mount(VButton, { props: { variant: 'secondary' } });
|
|
expect(wrapper.classes()).toContain('btn-secondary');
|
|
});
|
|
|
|
it('applies size classes', () => {
|
|
const wrapper = mount(VButton, { props: { size: 'sm' } });
|
|
expect(wrapper.classes()).toContain('btn-sm');
|
|
});
|
|
|
|
it('is disabled when disabled prop is true', () => {
|
|
const wrapper = mount(VButton, { props: { disabled: true } });
|
|
expect(wrapper.attributes('disabled')).toBeDefined();
|
|
expect(wrapper.classes()).toContain('btn-disabled');
|
|
});
|
|
|
|
it('emits click event when not disabled', async () => {
|
|
const handleClick = vi.fn();
|
|
const wrapper = mount(VButton, {
|
|
attrs: {
|
|
onClick: handleClick, // For native event handling by test runner
|
|
}
|
|
});
|
|
await wrapper.trigger('click');
|
|
expect(handleClick).toHaveBeenCalledTimes(1);
|
|
});
|
|
|
|
it('emits click event via emits options when not disabled', async () => {
|
|
const wrapper = mount(VButton);
|
|
await wrapper.trigger('click');
|
|
expect(wrapper.emitted().click).toBeTruthy();
|
|
expect(wrapper.emitted().click.length).toBe(1);
|
|
});
|
|
|
|
it('does not emit click event when disabled', async () => {
|
|
const handleClick = vi.fn();
|
|
const wrapper = mount(VButton, {
|
|
props: { disabled: true },
|
|
attrs: {
|
|
onClick: handleClick, // For native event handling by test runner
|
|
}
|
|
});
|
|
await wrapper.trigger('click');
|
|
expect(handleClick).not.toHaveBeenCalled();
|
|
|
|
// Check emitted events from component as well
|
|
const wrapperEmitted = mount(VButton, { props: { disabled: true } });
|
|
await wrapperEmitted.trigger('click');
|
|
expect(wrapperEmitted.emitted().click).toBeUndefined();
|
|
});
|
|
|
|
it('renders left icon', () => {
|
|
const wrapper = mount(VButton, {
|
|
props: { iconLeft: 'search' },
|
|
// Global stubs or components might be needed if VIcon isn't registered globally for tests
|
|
global: { components: { VIcon } }
|
|
});
|
|
const icon = wrapper.findComponent(VIcon);
|
|
expect(icon.exists()).toBe(true);
|
|
expect(icon.props('name')).toBe('search');
|
|
expect(wrapper.text()).toContain('Button'); // Label should still be there
|
|
});
|
|
|
|
it('renders right icon', () => {
|
|
const wrapper = mount(VButton, {
|
|
props: { iconRight: 'alert' },
|
|
global: { components: { VIcon } }
|
|
});
|
|
const icon = wrapper.findComponent(VIcon);
|
|
expect(icon.exists()).toBe(true);
|
|
expect(icon.props('name')).toBe('alert');
|
|
});
|
|
|
|
it('renders icon only button', () => {
|
|
const wrapper = mount(VButton, {
|
|
props: { iconLeft: 'close', iconOnly: true, label: 'Close' },
|
|
global: { components: { VIcon } }
|
|
});
|
|
const icon = wrapper.findComponent(VIcon);
|
|
expect(icon.exists()).toBe(true);
|
|
expect(icon.props('name')).toBe('close');
|
|
expect(wrapper.classes()).toContain('btn-icon-only');
|
|
// Label should be visually hidden but present for accessibility
|
|
const labelSpan = wrapper.find('span');
|
|
expect(labelSpan.exists()).toBe(true);
|
|
expect(labelSpan.classes()).toContain('sr-only');
|
|
expect(labelSpan.text()).toBe('Close');
|
|
});
|
|
|
|
it('renders icon only button with iconRight', () => {
|
|
const wrapper = mount(VButton, {
|
|
props: { iconRight: 'search', iconOnly: true, label: 'Search' },
|
|
global: { components: { VIcon } }
|
|
});
|
|
const icon = wrapper.findComponent(VIcon);
|
|
expect(icon.exists()).toBe(true);
|
|
expect(icon.props('name')).toBe('search');
|
|
expect(wrapper.classes()).toContain('btn-icon-only');
|
|
});
|
|
|
|
it('validates variant prop', () => {
|
|
const validator = VButton.props.variant.validator;
|
|
expect(validator('primary')).toBe(true);
|
|
expect(validator('secondary')).toBe(true);
|
|
expect(validator('neutral')).toBe(true);
|
|
expect(validator('danger')).toBe(true);
|
|
expect(validator('invalid-variant')).toBe(false);
|
|
});
|
|
|
|
it('validates size prop', () => {
|
|
const validator = VButton.props.size.validator;
|
|
expect(validator('sm')).toBe(true);
|
|
expect(validator('md')).toBe(true);
|
|
expect(validator('lg')).toBe(true);
|
|
expect(validator('xl')).toBe(false);
|
|
});
|
|
|
|
it('validates type prop', () => {
|
|
const validator = VButton.props.type.validator;
|
|
expect(validator('button')).toBe(true);
|
|
expect(validator('submit')).toBe(true);
|
|
expect(validator('reset')).toBe(true);
|
|
expect(validator('link')).toBe(false);
|
|
});
|
|
});
|