Files
memory-infrastructure-palace/code/websites/pokedex.online/tests/unit/components/BaseButton.test.js

301 lines
8.2 KiB
JavaScript
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
import { describe, it, expect, vi } from 'vitest';
import { mount } from '@vue/test-utils';
import BaseButton from '../../../src/components/shared/BaseButton.vue';
describe('BaseButton', () => {
describe('Rendering', () => {
it('renders with default props', () => {
const wrapper = mount(BaseButton, {
slots: {
default: 'Click me'
}
});
expect(wrapper.find('button').exists()).toBe(true);
expect(wrapper.text()).toBe('Click me');
expect(wrapper.classes()).toContain('base-button--primary');
expect(wrapper.classes()).toContain('base-button--medium');
});
it('renders all button variants', () => {
const variants = ['primary', 'secondary', 'danger', 'ghost', 'icon-only'];
variants.forEach(variant => {
const wrapper = mount(BaseButton, {
props: { variant },
slots: { default: 'Button' }
});
expect(wrapper.classes()).toContain(`base-button--${variant}`);
});
});
it('renders all button sizes', () => {
const sizes = ['small', 'medium', 'large'];
sizes.forEach(size => {
const wrapper = mount(BaseButton, {
props: { size },
slots: { default: 'Button' }
});
expect(wrapper.classes()).toContain(`base-button--${size}`);
});
});
it('renders with icon on left', () => {
const wrapper = mount(BaseButton, {
props: {
icon: '🚀',
iconPosition: 'left'
},
slots: { default: 'Launch' }
});
const icons = wrapper.findAll('.icon');
expect(icons.length).toBe(1);
expect(icons[0].classes()).toContain('icon-left');
expect(icons[0].text()).toBe('🚀');
});
it('renders with icon on right', () => {
const wrapper = mount(BaseButton, {
props: {
icon: '→',
iconPosition: 'right'
},
slots: { default: 'Next' }
});
const icons = wrapper.findAll('.icon');
expect(icons.length).toBe(1);
expect(icons[0].classes()).toContain('icon-right');
expect(icons[0].text()).toBe('→');
});
it('renders as icon-only button when no slot content', () => {
const wrapper = mount(BaseButton, {
props: {
icon: '×'
}
});
expect(wrapper.classes()).toContain('base-button--icon-only');
expect(wrapper.text()).toBe('×');
});
it('renders as full width', () => {
const wrapper = mount(BaseButton, {
props: { fullWidth: true },
slots: { default: 'Button' }
});
expect(wrapper.classes()).toContain('base-button--full-width');
});
});
describe('Button Types', () => {
it('renders with button type by default', () => {
const wrapper = mount(BaseButton);
expect(wrapper.attributes('type')).toBe('button');
});
it('renders with submit type', () => {
const wrapper = mount(BaseButton, {
props: { type: 'submit' }
});
expect(wrapper.attributes('type')).toBe('submit');
});
it('renders with reset type', () => {
const wrapper = mount(BaseButton, {
props: { type: 'reset' }
});
expect(wrapper.attributes('type')).toBe('reset');
});
});
describe('Loading State', () => {
it('shows spinner when loading', () => {
const wrapper = mount(BaseButton, {
props: { loading: true },
slots: { default: 'Loading...' }
});
expect(wrapper.find('.spinner').exists()).toBe(true);
expect(wrapper.classes()).toContain('base-button--loading');
});
it('hides icon when loading', () => {
const wrapper = mount(BaseButton, {
props: {
loading: true,
icon: '🚀'
},
slots: { default: 'Launch' }
});
expect(wrapper.find('.icon').exists()).toBe(false);
expect(wrapper.find('.spinner').exists()).toBe(true);
});
it('makes button text screen-reader only when loading', () => {
const wrapper = mount(BaseButton, {
props: { loading: true },
slots: { default: 'Processing' }
});
const textSpan = wrapper.find('span:not(.spinner)');
expect(textSpan.classes()).toContain('sr-only');
});
it('does not emit click when loading', async () => {
const wrapper = mount(BaseButton, {
props: { loading: true },
slots: { default: 'Button' }
});
await wrapper.trigger('click');
expect(wrapper.emitted('click')).toBeUndefined();
});
});
describe('Disabled State', () => {
it('applies disabled class', () => {
const wrapper = mount(BaseButton, {
props: { disabled: true },
slots: { default: 'Button' }
});
expect(wrapper.classes()).toContain('base-button--disabled');
expect(wrapper.attributes('disabled')).toBeDefined();
});
it('does not emit click when disabled', async () => {
const wrapper = mount(BaseButton, {
props: { disabled: true },
slots: { default: 'Button' }
});
await wrapper.trigger('click');
expect(wrapper.emitted('click')).toBeUndefined();
});
});
describe('Click Events', () => {
it('emits click event when clicked', async () => {
const wrapper = mount(BaseButton, {
slots: { default: 'Button' }
});
await wrapper.trigger('click');
expect(wrapper.emitted('click')).toBeTruthy();
expect(wrapper.emitted('click')).toHaveLength(1);
});
it('passes event to click handler', async () => {
const wrapper = mount(BaseButton, {
slots: { default: 'Button' }
});
await wrapper.trigger('click');
const clickEvents = wrapper.emitted('click');
expect(clickEvents[0][0]).toBeInstanceOf(Event);
});
it('can be clicked multiple times', async () => {
const wrapper = mount(BaseButton, {
slots: { default: 'Button' }
});
await wrapper.trigger('click');
await wrapper.trigger('click');
await wrapper.trigger('click');
expect(wrapper.emitted('click')).toHaveLength(3);
});
});
describe('Accessibility', () => {
it('has proper focus-visible styling', () => {
const wrapper = mount(BaseButton, {
slots: { default: 'Button' }
});
// Check that focus-visible class exists in CSS (component has focus styling)
expect(wrapper.html()).toContain('button');
});
it('spinner has aria-hidden', () => {
const wrapper = mount(BaseButton, {
props: { loading: true },
slots: { default: 'Loading' }
});
const spinner = wrapper.find('.spinner');
expect(spinner.attributes('aria-hidden')).toBe('true');
});
it('maintains text for screen readers when loading', () => {
const wrapper = mount(BaseButton, {
props: { loading: true },
slots: { default: 'Submit Form' }
});
expect(wrapper.text()).toContain('Submit Form');
});
});
describe('Edge Cases', () => {
it('handles empty slot gracefully', () => {
const wrapper = mount(BaseButton);
expect(wrapper.find('button').exists()).toBe(true);
});
it('handles both loading and disabled', () => {
const wrapper = mount(BaseButton, {
props: {
loading: true,
disabled: true
},
slots: { default: 'Button' }
});
expect(wrapper.classes()).toContain('base-button--loading');
expect(wrapper.classes()).toContain('base-button--disabled');
expect(wrapper.attributes('disabled')).toBeDefined();
});
it('validates variant prop', () => {
// Valid variants should not throw
expect(() => {
mount(BaseButton, {
props: { variant: 'primary' }
});
}).not.toThrow();
// Invalid variant should fail validation (but mount won't throw, just warn)
const wrapper = mount(BaseButton, {
props: { variant: 'invalid' }
});
expect(wrapper.exists()).toBe(true);
});
it('validates size prop', () => {
expect(() => {
mount(BaseButton, {
props: { size: 'medium' }
});
}).not.toThrow();
});
it('validates type prop', () => {
expect(() => {
mount(BaseButton, {
props: { type: 'submit' }
});
}).not.toThrow();
});
});
});