import { render, screen } from '@testing-library/react';
import userEvent from '@testing-library/user-event';
import React from 'react';
import { describe, expect, it, vi } from 'vitest';
import { MdIconButton } from '../MdIconButton';
describe('MdIconButton', () => {
describe('rendering', () => {
it('renders a button', () => {
render(X);
expect(screen.getByRole('button')).toBeInTheDocument();
});
it('renders children (icon)', () => {
render(
X
,
);
expect(screen.getByTestId('icon')).toBeInTheDocument();
});
it('renders with aria-label from label prop', () => {
render(X);
expect(screen.getByRole('button')).toHaveAttribute('aria-label', 'Close dialog');
});
it('has type="button" by default', () => {
render(X);
expect(screen.getByRole('button')).toHaveAttribute('type', 'button');
});
});
describe('themes', () => {
it('applies filled theme by default', () => {
render(X);
const button = screen.getByRole('button');
expect(button).toHaveClass('md-icon-button');
expect(button).not.toHaveClass('md-icon-button--border');
expect(button).not.toHaveClass('md-icon-button--plain');
});
it('applies border theme', () => {
render(
X
,
);
expect(screen.getByRole('button')).toHaveClass('md-icon-button--border');
});
it('applies plain theme', () => {
render(
X
,
);
expect(screen.getByRole('button')).toHaveClass('md-icon-button--plain');
});
});
describe('states', () => {
it('can be disabled', () => {
render(
X
,
);
expect(screen.getByRole('button')).toBeDisabled();
});
it('shows loading spinner when loading', () => {
const { container } = render(
X
,
);
expect(container.querySelector('.md-loading-spinner')).toBeInTheDocument();
});
it('hides icon when loading', () => {
render(
X
,
);
expect(screen.queryByTestId('icon')).not.toBeInTheDocument();
});
});
describe('interactions', () => {
it('handles click events', async () => {
const user = userEvent.setup();
const onClick = vi.fn();
render(
X
,
);
await user.click(screen.getByRole('button'));
expect(onClick).toHaveBeenCalledTimes(1);
});
it('does not fire click when disabled', async () => {
const user = userEvent.setup();
const onClick = vi.fn();
render(
X
,
);
await user.click(screen.getByRole('button'));
expect(onClick).not.toHaveBeenCalled();
});
it('can be triggered with keyboard (Enter)', async () => {
const user = userEvent.setup();
const onClick = vi.fn();
render(
X
,
);
screen.getByRole('button').focus();
await user.keyboard('{Enter}');
expect(onClick).toHaveBeenCalledTimes(1);
});
it('can be triggered with keyboard (Space)', async () => {
const user = userEvent.setup();
const onClick = vi.fn();
render(
X
,
);
screen.getByRole('button').focus();
await user.keyboard(' ');
expect(onClick).toHaveBeenCalledTimes(1);
});
});
describe('tooltip', () => {
it('wraps button in tooltip when showTooltip is true', () => {
const { container } = render(
X
,
);
expect(container.querySelector('.md-tooltip__anchor')).toBeInTheDocument();
});
it('does not show tooltip when disabled', () => {
const { container } = render(
X
,
);
expect(container.querySelector('.md-tooltip__anchor')).not.toBeInTheDocument();
});
});
describe('props forwarding', () => {
it('forwards id', () => {
render(
X
,
);
expect(screen.getByRole('button')).toHaveAttribute('id', 'my-button');
});
it('forwards data-* attributes', () => {
render(
X
,
);
expect(screen.getByTestId('icon-btn')).toBeInTheDocument();
});
it('merges custom className', () => {
render(
X
,
);
const button = screen.getByRole('button');
expect(button).toHaveClass('md-icon-button');
expect(button).toHaveClass('custom-class');
});
it('forwards type attribute', () => {
render(
X
,
);
expect(screen.getByRole('button')).toHaveAttribute('type', 'submit');
});
});
describe('accessibility', () => {
it('has accessible name via aria-label', () => {
render(X);
expect(screen.getByRole('button', { name: 'Delete item' })).toBeInTheDocument();
});
it('icon is aria-hidden', () => {
const { container } = render(X);
expect(container.querySelector('.md-icon-button__icon')).toHaveAttribute('aria-hidden', 'true');
});
});
});