/** * Unit tests for HelpButton component * * Tests cover: * - Component rendering * - Link navigation * - Icon display * - Text display and responsive behavior * - Accessibility attributes * - Custom className prop * - Internationalization (i18n) */ import { describe, it, expect, vi, beforeEach } from 'vitest'; import { render, screen } from '@testing-library/react'; import { BrowserRouter } from 'react-router-dom'; import React from 'react'; import HelpButton from '../HelpButton'; // Mock react-i18next vi.mock('react-i18next', () => ({ useTranslation: () => ({ t: (key: string, fallback: string) => fallback, }), })); // Test wrapper with Router const createWrapper = () => { return ({ children }: { children: React.ReactNode }) => ( {children} ); }; describe('HelpButton', () => { beforeEach(() => { vi.clearAllMocks(); }); describe('Rendering', () => { it('should render the button', () => { render(, { wrapper: createWrapper(), }); const link = screen.getByRole('link'); expect(link).toBeInTheDocument(); }); it('should render as a Link component with correct href', () => { render(, { wrapper: createWrapper(), }); const link = screen.getByRole('link'); expect(link).toHaveAttribute('href', '/help/resources'); }); it('should render with different help paths', () => { const { rerender } = render(, { wrapper: createWrapper(), }); let link = screen.getByRole('link'); expect(link).toHaveAttribute('href', '/help/page1'); rerender(); link = screen.getByRole('link'); expect(link).toHaveAttribute('href', '/help/page2'); }); }); describe('Icon Display', () => { it('should display the HelpCircle icon', () => { render(, { wrapper: createWrapper(), }); const link = screen.getByRole('link'); // Check for SVG icon (lucide-react renders as SVG) const svg = link.querySelector('svg'); expect(svg).toBeInTheDocument(); }); }); describe('Text Display', () => { it('should display help text', () => { render(, { wrapper: createWrapper(), }); const text = screen.getByText('Help'); expect(text).toBeInTheDocument(); }); it('should apply responsive class to hide text on small screens', () => { render(, { wrapper: createWrapper(), }); const text = screen.getByText('Help'); expect(text).toHaveClass('hidden', 'sm:inline'); }); }); describe('Accessibility', () => { it('should have title attribute', () => { render(, { wrapper: createWrapper(), }); const link = screen.getByRole('link'); expect(link).toHaveAttribute('title', 'Help'); }); it('should be keyboard accessible as a link', () => { render(, { wrapper: createWrapper(), }); const link = screen.getByRole('link'); expect(link).toBeInTheDocument(); expect(link.tagName).toBe('A'); }); it('should have accessible name from text content', () => { render(, { wrapper: createWrapper(), }); const link = screen.getByRole('link', { name: /help/i }); expect(link).toBeInTheDocument(); }); }); describe('Styling', () => { it('should apply default classes', () => { render(, { wrapper: createWrapper(), }); const link = screen.getByRole('link'); expect(link).toHaveClass('inline-flex'); expect(link).toHaveClass('items-center'); expect(link).toHaveClass('gap-1.5'); expect(link).toHaveClass('px-3'); expect(link).toHaveClass('py-1.5'); expect(link).toHaveClass('text-sm'); expect(link).toHaveClass('rounded-lg'); expect(link).toHaveClass('transition-colors'); }); it('should apply color classes for light mode', () => { render(, { wrapper: createWrapper(), }); const link = screen.getByRole('link'); expect(link).toHaveClass('text-gray-500'); expect(link).toHaveClass('hover:text-brand-600'); expect(link).toHaveClass('hover:bg-gray-100'); }); it('should apply color classes for dark mode', () => { render(, { wrapper: createWrapper(), }); const link = screen.getByRole('link'); expect(link).toHaveClass('dark:text-gray-400'); expect(link).toHaveClass('dark:hover:text-brand-400'); expect(link).toHaveClass('dark:hover:bg-gray-800'); }); it('should apply custom className when provided', () => { render(, { wrapper: createWrapper(), }); const link = screen.getByRole('link'); expect(link).toHaveClass('custom-class'); }); it('should merge custom className with default classes', () => { render(, { wrapper: createWrapper(), }); const link = screen.getByRole('link'); expect(link).toHaveClass('ml-auto'); expect(link).toHaveClass('inline-flex'); expect(link).toHaveClass('items-center'); }); it('should work without custom className', () => { render(, { wrapper: createWrapper(), }); const link = screen.getByRole('link'); expect(link).toBeInTheDocument(); }); }); describe('Internationalization', () => { it('should use translation for help text', () => { render(, { wrapper: createWrapper(), }); // The mock returns the fallback value const text = screen.getByText('Help'); expect(text).toBeInTheDocument(); }); it('should use translation for title attribute', () => { render(, { wrapper: createWrapper(), }); const link = screen.getByRole('link'); expect(link).toHaveAttribute('title', 'Help'); }); }); describe('Integration', () => { it('should render correctly with all props together', () => { render( , { wrapper: createWrapper() } ); const link = screen.getByRole('link'); expect(link).toBeInTheDocument(); expect(link).toHaveAttribute('href', '/help/advanced'); expect(link).toHaveAttribute('title', 'Help'); expect(link).toHaveClass('custom-styling'); expect(link).toHaveClass('inline-flex'); const icon = link.querySelector('svg'); expect(icon).toBeInTheDocument(); const text = screen.getByText('Help'); expect(text).toBeInTheDocument(); }); it('should maintain structure with icon and text', () => { render(, { wrapper: createWrapper(), }); const link = screen.getByRole('link'); const svg = link.querySelector('svg'); const span = link.querySelector('span'); expect(svg).toBeInTheDocument(); expect(span).toBeInTheDocument(); expect(span).toHaveTextContent('Help'); }); }); });