import { fireEvent, render, screen } from '@testing-library/react' import { describe, expect, it, vi } from 'vitest' import HeadersInput from './headers-input' describe('HeadersInput', () => { const defaultProps = { headersItems: [], onChange: vi.fn(), } describe('Empty State', () => { it('should render no headers message when empty', () => { render() expect(screen.getByText('tools.mcp.modal.noHeaders')).toBeInTheDocument() }) it('should render add header button when empty and not readonly', () => { render() expect(screen.getByText('tools.mcp.modal.addHeader')).toBeInTheDocument() }) it('should not render add header button when empty and readonly', () => { render() expect(screen.queryByText('tools.mcp.modal.addHeader')).not.toBeInTheDocument() }) it('should call onChange with new item when add button is clicked', () => { const onChange = vi.fn() render() const addButton = screen.getByText('tools.mcp.modal.addHeader') fireEvent.click(addButton) expect(onChange).toHaveBeenCalledWith([ expect.objectContaining({ key: '', value: '', }), ]) }) }) describe('With Headers', () => { const headersItems = [ { id: '1', key: 'Authorization', value: 'Bearer token123' }, { id: '2', key: 'Content-Type', value: 'application/json' }, ] it('should render header items', () => { render() expect(screen.getByDisplayValue('Authorization')).toBeInTheDocument() expect(screen.getByDisplayValue('Bearer token123')).toBeInTheDocument() expect(screen.getByDisplayValue('Content-Type')).toBeInTheDocument() expect(screen.getByDisplayValue('application/json')).toBeInTheDocument() }) it('should render table headers', () => { render() expect(screen.getByText('tools.mcp.modal.headerKey')).toBeInTheDocument() expect(screen.getByText('tools.mcp.modal.headerValue')).toBeInTheDocument() }) it('should render delete buttons for each item when not readonly', () => { render() // Should have delete buttons for each header const deleteButtons = document.querySelectorAll('[class*="text-text-destructive"]') expect(deleteButtons.length).toBe(headersItems.length) }) it('should not render delete buttons when readonly', () => { render() const deleteButtons = document.querySelectorAll('[class*="text-text-destructive"]') expect(deleteButtons.length).toBe(0) }) it('should render add button at bottom when not readonly', () => { render() expect(screen.getByText('tools.mcp.modal.addHeader')).toBeInTheDocument() }) it('should not render add button when readonly', () => { render() expect(screen.queryByText('tools.mcp.modal.addHeader')).not.toBeInTheDocument() }) }) describe('Masked Headers', () => { const headersItems = [{ id: '1', key: 'Secret', value: '***' }] it('should show masked headers tip when isMasked is true', () => { render() expect(screen.getByText('tools.mcp.modal.maskedHeadersTip')).toBeInTheDocument() }) it('should not show masked headers tip when isMasked is false', () => { render() expect(screen.queryByText('tools.mcp.modal.maskedHeadersTip')).not.toBeInTheDocument() }) }) describe('Item Interactions', () => { const headersItems = [ { id: '1', key: 'Header1', value: 'Value1' }, ] it('should call onChange when key is changed', () => { const onChange = vi.fn() render() const keyInput = screen.getByDisplayValue('Header1') fireEvent.change(keyInput, { target: { value: 'NewHeader' } }) expect(onChange).toHaveBeenCalledWith([ { id: '1', key: 'NewHeader', value: 'Value1' }, ]) }) it('should call onChange when value is changed', () => { const onChange = vi.fn() render() const valueInput = screen.getByDisplayValue('Value1') fireEvent.change(valueInput, { target: { value: 'NewValue' } }) expect(onChange).toHaveBeenCalledWith([ { id: '1', key: 'Header1', value: 'NewValue' }, ]) }) it('should remove item when delete button is clicked', () => { const onChange = vi.fn() render() const deleteButton = document.querySelector('[class*="text-text-destructive"]')?.closest('button') if (deleteButton) { fireEvent.click(deleteButton) expect(onChange).toHaveBeenCalledWith([]) } }) it('should add new item when add button is clicked', () => { const onChange = vi.fn() render() const addButton = screen.getByText('tools.mcp.modal.addHeader') fireEvent.click(addButton) expect(onChange).toHaveBeenCalledWith([ { id: '1', key: 'Header1', value: 'Value1' }, expect.objectContaining({ key: '', value: '' }), ]) }) }) describe('Multiple Headers', () => { const headersItems = [ { id: '1', key: 'Header1', value: 'Value1' }, { id: '2', key: 'Header2', value: 'Value2' }, { id: '3', key: 'Header3', value: 'Value3' }, ] it('should render all headers', () => { render() expect(screen.getByDisplayValue('Header1')).toBeInTheDocument() expect(screen.getByDisplayValue('Header2')).toBeInTheDocument() expect(screen.getByDisplayValue('Header3')).toBeInTheDocument() }) it('should update correct item when changed', () => { const onChange = vi.fn() render() const header2Input = screen.getByDisplayValue('Header2') fireEvent.change(header2Input, { target: { value: 'UpdatedHeader2' } }) expect(onChange).toHaveBeenCalledWith([ { id: '1', key: 'Header1', value: 'Value1' }, { id: '2', key: 'UpdatedHeader2', value: 'Value2' }, { id: '3', key: 'Header3', value: 'Value3' }, ]) }) it('should remove correct item when deleted', () => { const onChange = vi.fn() render() // Find all delete buttons and click the second one const deleteButtons = document.querySelectorAll('[class*="text-text-destructive"]') const secondDeleteButton = deleteButtons[1]?.closest('button') if (secondDeleteButton) { fireEvent.click(secondDeleteButton) expect(onChange).toHaveBeenCalledWith([ { id: '1', key: 'Header1', value: 'Value1' }, { id: '3', key: 'Header3', value: 'Value3' }, ]) } }) }) describe('Readonly Mode', () => { const headersItems = [{ id: '1', key: 'ReadOnly', value: 'Value' }] it('should make inputs readonly when readonly is true', () => { render() const keyInput = screen.getByDisplayValue('ReadOnly') const valueInput = screen.getByDisplayValue('Value') expect(keyInput).toHaveAttribute('readonly') expect(valueInput).toHaveAttribute('readonly') }) it('should not make inputs readonly when readonly is false', () => { render() const keyInput = screen.getByDisplayValue('ReadOnly') const valueInput = screen.getByDisplayValue('Value') expect(keyInput).not.toHaveAttribute('readonly') expect(valueInput).not.toHaveAttribute('readonly') }) }) describe('Edge Cases', () => { it('should handle empty key and value', () => { const headersItems = [{ id: '1', key: '', value: '' }] render() const inputs = screen.getAllByRole('textbox') expect(inputs.length).toBe(2) }) it('should handle special characters in header key', () => { const headersItems = [{ id: '1', key: 'X-Custom-Header', value: 'value' }] render() expect(screen.getByDisplayValue('X-Custom-Header')).toBeInTheDocument() }) it('should handle JSON value', () => { const headersItems = [{ id: '1', key: 'Data', value: '{"key":"value"}' }] render() expect(screen.getByDisplayValue('{"key":"value"}')).toBeInTheDocument() }) }) })