import { describe, it, expect, vi } from 'vitest' import { render, screen, fireEvent } from '@testing-library/react' import { NodeModal } from '../NodeModal' describe('NodeModal', () => { it('renders nothing when closed', () => { const { container } = render( ) expect(container.querySelector('[role="dialog"]')).toBeNull() }) it('renders form fields when open', () => { render() expect(screen.getByPlaceholderText('My Server')).toBeDefined() expect(screen.getByText('Add Node')).toBeDefined() }) it('does not call onSubmit when label is empty and shows error', () => { const onSubmit = vi.fn() render() fireEvent.click(screen.getByText('Add')) expect(onSubmit).not.toHaveBeenCalled() expect(screen.getByText('Label is required')).toBeDefined() }) it('calls onSubmit with form data when label is filled', () => { const onSubmit = vi.fn() const onClose = vi.fn() render() fireEvent.change(screen.getByPlaceholderText('My Server'), { target: { value: 'My NAS' } }) fireEvent.click(screen.getByText('Add')) expect(onSubmit).toHaveBeenCalledOnce() expect(onSubmit.mock.calls[0][0].label).toBe('My NAS') expect(onClose).toHaveBeenCalledOnce() }) it('clears label error when user starts typing', () => { render() fireEvent.click(screen.getByText('Add')) expect(screen.getByText('Label is required')).toBeDefined() fireEvent.change(screen.getByPlaceholderText('My Server'), { target: { value: 'x' } }) expect(screen.queryByText('Label is required')).toBeNull() }) it('pre-fills form from initial prop', () => { render( ) const input = screen.getByPlaceholderText('My Server') as HTMLInputElement expect(input.value).toBe('Pre-filled') }) it('shows Save button text when title is Edit Node', () => { render() expect(screen.getByText('Save')).toBeDefined() }) it('calls onClose when Cancel is clicked', () => { const onClose = vi.fn() render() fireEvent.click(screen.getByText('Cancel')) expect(onClose).toHaveBeenCalledOnce() }) describe('Hardware section', () => { it('renders Hardware toggle button', () => { render() expect(screen.getByText('Hardware')).toBeDefined() }) it('hardware fields are hidden by default', () => { render() expect(screen.queryByPlaceholderText('e.g. Intel Xeon E5-2680')).toBeNull() }) it('expands hardware fields on toggle click', () => { render() fireEvent.click(screen.getByText('Hardware')) expect(screen.getByPlaceholderText('e.g. Intel Xeon E5-2680')).toBeDefined() expect(screen.getByPlaceholderText('e.g. 8')).toBeDefined() expect(screen.getByPlaceholderText('e.g. 32')).toBeDefined() expect(screen.getByPlaceholderText('e.g. 500')).toBeDefined() }) it('submits hardware fields when filled', () => { const onSubmit = vi.fn() render() fireEvent.change(screen.getByPlaceholderText('My Server'), { target: { value: 'Homelab' } }) fireEvent.click(screen.getByText('Hardware')) fireEvent.change(screen.getByPlaceholderText('e.g. Intel Xeon E5-2680'), { target: { value: 'Intel i7-12700K' } }) fireEvent.change(screen.getByPlaceholderText('e.g. 8'), { target: { value: '12' } }) fireEvent.change(screen.getByPlaceholderText('e.g. 32'), { target: { value: '64' } }) fireEvent.change(screen.getByPlaceholderText('e.g. 500'), { target: { value: '2000' } }) fireEvent.click(screen.getByText('Add')) const submitted = onSubmit.mock.calls[0][0] expect(submitted.cpu_model).toBe('Intel i7-12700K') expect(submitted.cpu_count).toBe(12) expect(submitted.ram_gb).toBe(64) expect(submitted.disk_gb).toBe(2000) }) it('auto-expands when initial has hardware data', () => { render( ) expect(screen.getByPlaceholderText('e.g. Intel Xeon E5-2680')).toBeDefined() }) it('hides hardware section for groupRect type', () => { render( ) expect(screen.queryByText('Hardware')).toBeNull() }) it('show on node toggle is hidden when section is collapsed', () => { render() expect(screen.queryByText('Show on node')).toBeNull() }) it('show on node toggle appears when section is expanded', () => { render() fireEvent.click(screen.getByText('Hardware')) expect(screen.getByText('Show on node')).toBeDefined() }) it('show_hardware defaults to false', () => { const onSubmit = vi.fn() render() fireEvent.change(screen.getByPlaceholderText('My Server'), { target: { value: 'Node' } }) fireEvent.click(screen.getByText('Add')) expect(onSubmit.mock.calls[0][0].show_hardware).toBeFalsy() }) it('toggling show on node sets show_hardware to true', () => { const onSubmit = vi.fn() render() fireEvent.change(screen.getByPlaceholderText('My Server'), { target: { value: 'Node' } }) fireEvent.click(screen.getByText('Hardware')) fireEvent.click(screen.getByRole('switch')) fireEvent.click(screen.getByText('Add')) expect(onSubmit.mock.calls[0][0].show_hardware).toBe(true) }) it('pre-fills show_hardware from initial prop', () => { const onSubmit = vi.fn() render( ) fireEvent.click(screen.getByText('Add')) expect(onSubmit.mock.calls[0][0].show_hardware).toBe(true) }) }) })