import { fireEvent, 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 { MdFileUpload } from '../MdFileUpload'; describe('MdFileUpload', () => { describe('rendering', () => { it('renders file upload component', () => { const { container } = render(); expect(container.querySelector('.md-fileupload')).toBeInTheDocument(); }); it('renders drop area', () => { const { container } = render(); expect(container.querySelector('.md-fileupload__droparea')).toBeInTheDocument(); }); it('renders upload icon', () => { const { container } = render(); expect(container.querySelector('.md-fileupload__droparea-icon')).toBeInTheDocument(); }); it('renders file selection button', () => { render(); expect(screen.getByRole('button', { name: /velg fra denne maskinen/i })).toBeInTheDocument(); }); it('renders action buttons by default', () => { render(); expect(screen.getByRole('button', { name: /last opp/i })).toBeInTheDocument(); expect(screen.getByRole('button', { name: /avbryt/i })).toBeInTheDocument(); }); it('hides action buttons when hideButtons is true', () => { render(); expect(screen.queryByRole('button', { name: /last opp/i })).not.toBeInTheDocument(); }); }); describe('custom text', () => { it('renders custom upload button text', () => { render(); expect(screen.getByRole('button', { name: /submit files/i })).toBeInTheDocument(); }); it('renders custom cancel button text', () => { render(); expect(screen.getByRole('button', { name: /clear/i })).toBeInTheDocument(); }); it('renders custom upload texts', () => { render(); expect(screen.getByText(/drop file here or/i)).toBeInTheDocument(); expect(screen.getByRole('button', { name: /browse files/i })).toBeInTheDocument(); }); }); describe('file input', () => { it('has file input with correct type', () => { const { container } = render(); const input = container.querySelector('input[type="file"]'); expect(input).toBeInTheDocument(); }); it('allows multiple files by default', () => { const { container } = render(); const input = container.querySelector('input[type="file"]'); expect(input).toHaveAttribute('multiple'); }); it('restricts to single file when multiple is false', () => { const { container } = render(); const input = container.querySelector('input[type="file"]'); expect(input).not.toHaveAttribute('multiple'); }); it('accepts only images when imagesOnly is true', () => { const { container } = render(); const input = container.querySelector('input[type="file"]'); expect(input).toHaveAttribute('accept', 'image/*'); }); it('accepts all files by default', () => { const { container } = render(); const input = container.querySelector('input[type="file"]'); expect(input).toHaveAttribute('accept', '*'); }); }); describe('file count display', () => { it('shows file count text', () => { render(); expect(screen.getByText(/antall filer: 0/i)).toBeInTheDocument(); }); it('shows image count text when imagesOnly', () => { render(); expect(screen.getByText(/antall bilder: 0/i)).toBeInTheDocument(); }); it('shows single file limit when multiple is false', () => { render(); expect(screen.getByText(/antall filer: 0 \/ 1/i)).toBeInTheDocument(); }); }); describe('interactions', () => { it('opens file dialog when selection button is clicked', async () => { const user = userEvent.setup(); const { container } = render(); const input = container.querySelector('input[type="file"]') as HTMLInputElement; const clickSpy = vi.spyOn(input, 'click'); await user.click(screen.getByRole('button', { name: /velg fra denne maskinen/i })); expect(clickSpy).toHaveBeenCalled(); }); it('calls onCancel when cancel button is clicked', async () => { const user = userEvent.setup(); const onCancel = vi.fn(); render(); await user.click(screen.getByRole('button', { name: /avbryt/i })); expect(onCancel).toHaveBeenCalled(); }); it('disables upload button when no files selected', () => { render(); expect(screen.getByRole('button', { name: /last opp/i })).toBeDisabled(); }); }); describe('drag and drop', () => { it('adds active class on drag enter', () => { const { container } = render(); const dropArea = container.querySelector('.md-fileupload__droparea') as HTMLElement; const dragEvent = new Event('dragenter', { bubbles: true }); Object.defineProperty(dragEvent, 'preventDefault', { value: vi.fn() }); Object.defineProperty(dragEvent, 'stopPropagation', { value: vi.fn() }); dropArea.dispatchEvent(dragEvent); expect(dropArea).toHaveClass('md-fileupload__droparea--active'); }); it('removes active class on drag leave', () => { const { container } = render(); const dropArea = container.querySelector('.md-fileupload__droparea') as HTMLElement; // First add the class dropArea.classList.add('md-fileupload__droparea--active'); const dragLeaveEvent = new Event('dragleave', { bubbles: true }); Object.defineProperty(dragLeaveEvent, 'preventDefault', { value: vi.fn() }); Object.defineProperty(dragLeaveEvent, 'stopPropagation', { value: vi.fn() }); dropArea.dispatchEvent(dragLeaveEvent); expect(dropArea).not.toHaveClass('md-fileupload__droparea--active'); }); }); describe('default text for images', () => { it('shows image-specific drop text when imagesOnly', () => { render(); expect(screen.getByText(/dropp et bilde her eller/i)).toBeInTheDocument(); }); it('shows file-specific drop text by default', () => { render(); expect(screen.getByText(/dropp en fil her eller/i)).toBeInTheDocument(); }); }); describe('drag events', () => { it('handles drag end event', () => { const { container } = render(); const dropArea = container.querySelector('.md-fileupload__droparea') as HTMLElement; dropArea.classList.add('md-fileupload__droparea--active'); fireEvent.dragEnd(dropArea); expect(dropArea).not.toHaveClass('md-fileupload__droparea--active'); }); it('handles drag over event', () => { const { container } = render(); const dropArea = container.querySelector('.md-fileupload__droparea') as HTMLElement; const dragOverEvent = new Event('dragover', { bubbles: true }); const preventDefault = vi.fn(); const stopPropagation = vi.fn(); Object.defineProperty(dragOverEvent, 'preventDefault', { value: preventDefault }); Object.defineProperty(dragOverEvent, 'stopPropagation', { value: stopPropagation }); dropArea.dispatchEvent(dragOverEvent); expect(preventDefault).toHaveBeenCalled(); expect(stopPropagation).toHaveBeenCalled(); }); }); });