import { cleanup, fireEvent, render, screen, waitFor } from '@testing-library/react' import * as React from 'react' import Toast from '@/app/components/base/toast' import BlockInput, { getInputKeys } from './index' vi.mock('@/utils/var', () => ({ checkKeys: vi.fn((_keys: string[]) => ({ isValid: true, errorMessageKey: '', errorKey: '', })), })) describe('BlockInput', () => { beforeEach(() => { vi.clearAllMocks() vi.spyOn(Toast, 'notify') cleanup() }) describe('Rendering', () => { it('should render without crashing', () => { render() const wrapper = screen.getByTestId('block-input') expect(wrapper).toBeInTheDocument() }) it('should render with initial value', () => { const { container } = render() expect(container.textContent).toContain('Hello World') }) it('should render variable highlights', () => { render() const nameElement = screen.getByText('name') expect(nameElement).toBeInTheDocument() expect(nameElement.parentElement).toHaveClass('text-primary-600') }) it('should render multiple variable highlights', () => { render() expect(screen.getByText('foo')).toBeInTheDocument() expect(screen.getByText('bar')).toBeInTheDocument() }) it('should display character count in footer when not readonly', () => { render() expect(screen.getByText('5')).toBeInTheDocument() }) it('should hide footer in readonly mode', () => { render() expect(screen.queryByText('5')).not.toBeInTheDocument() }) }) describe('Props', () => { it('should apply custom className', () => { render() const innerContent = screen.getByTestId('block-input-content') expect(innerContent).toHaveClass('custom-class') }) it('should apply readonly prop with max height', () => { render() const contentDiv = screen.getByTestId('block-input').firstChild as Element expect(contentDiv).toHaveClass('max-h-[180px]') }) it('should have default empty value', () => { render() const contentDiv = screen.getByTestId('block-input') expect(contentDiv).toBeInTheDocument() }) }) describe('User Interactions', () => { it('should enter edit mode when clicked', async () => { render() const contentArea = screen.getByText('Hello') fireEvent.click(contentArea) await waitFor(() => { expect(screen.getByRole('textbox')).toBeInTheDocument() }) }) it('should update value when typing in edit mode', async () => { const onConfirm = vi.fn() const { checkKeys } = await import('@/utils/var') ; (checkKeys as ReturnType).mockReturnValue({ isValid: true, errorMessageKey: '', errorKey: '' }) render() const contentArea = screen.getByText('Hello') fireEvent.click(contentArea) const textarea = await screen.findByRole('textbox') fireEvent.change(textarea, { target: { value: 'Hello World' } }) expect(textarea).toHaveValue('Hello World') }) it('should call onConfirm on value change with valid keys', async () => { const onConfirm = vi.fn() const { checkKeys } = await import('@/utils/var') ; (checkKeys as ReturnType).mockReturnValue({ isValid: true, errorMessageKey: '', errorKey: '' }) render() const contentArea = screen.getByText('initial') fireEvent.click(contentArea) const textarea = await screen.findByRole('textbox') fireEvent.change(textarea, { target: { value: '{{name}}' } }) await waitFor(() => { expect(onConfirm).toHaveBeenCalledWith('{{name}}', ['name']) }) }) it('should show error toast on value change with invalid keys', async () => { const onConfirm = vi.fn() const { checkKeys } = await import('@/utils/var'); (checkKeys as ReturnType).mockReturnValue({ isValid: false, errorMessageKey: 'invalidKey', errorKey: 'test_key', }) render() const contentArea = screen.getByText('initial') fireEvent.click(contentArea) const textarea = await screen.findByRole('textbox') fireEvent.change(textarea, { target: { value: '{{invalid}}' } }) await waitFor(() => { expect(Toast.notify).toHaveBeenCalled() }) expect(onConfirm).not.toHaveBeenCalled() }) it('should not enter edit mode when readonly is true', () => { render() const contentArea = screen.getByText('Hello') fireEvent.click(contentArea) expect(screen.queryByRole('textbox')).not.toBeInTheDocument() }) }) describe('Edge Cases', () => { it('should handle empty string value', () => { const { container } = render() expect(container.textContent).toBe('0') const span = screen.getByTestId('block-input').querySelector('span') expect(span).toBeInTheDocument() expect(span).toBeEmptyDOMElement() }) it('should handle value without variables', () => { render() expect(screen.getByText('plain text')).toBeInTheDocument() }) it('should handle newlines in value', () => { render() expect(screen.getByText(/line1/)).toBeInTheDocument() }) it('should handle multiple same variables', () => { render() const highlights = screen.getAllByText('name') expect(highlights).toHaveLength(2) }) it('should handle value with only variables', () => { render() expect(screen.getByText('foo')).toBeInTheDocument() expect(screen.getByText('bar')).toBeInTheDocument() expect(screen.getByText('baz')).toBeInTheDocument() }) it('should handle text adjacent to variables', () => { render() expect(screen.getByText(/prefix/)).toBeInTheDocument() expect(screen.getByText(/suffix/)).toBeInTheDocument() }) }) }) describe('getInputKeys', () => { it('should extract keys from {{}} syntax', () => { const keys = getInputKeys('Hello {{name}}') expect(keys).toEqual(['name']) }) it('should extract multiple keys', () => { const keys = getInputKeys('{{foo}} and {{bar}}') expect(keys).toEqual(['foo', 'bar']) }) it('should remove duplicate keys', () => { const keys = getInputKeys('{{name}} and {{name}}') expect(keys).toEqual(['name']) }) it('should return empty array for no variables', () => { const keys = getInputKeys('plain text') expect(keys).toEqual([]) }) it('should return empty array for empty string', () => { const keys = getInputKeys('') expect(keys).toEqual([]) }) it('should handle keys with underscores and numbers', () => { const keys = getInputKeys('{{user_1}} and {{user_2}}') expect(keys).toEqual(['user_1', 'user_2']) }) })