import { describe, it, expect } from 'vitest' import { render, screen, fireEvent } from '@testing-library/react' import React from 'react' import FormBuilderPanel from '@/components/FormBuilderPanel' import { createFormStore } from '@/lib/state/formStore' import type { SelectField, RadioField } from '@/lib/state/formStore' import { NextIntlClientProvider } from 'next-intl' import enMessages from '@/messages/en.json' const messages: Record = enMessages as Record describe('FormBuilderPanel', () => { it('필드 추가와 action URL/스팸 옵션 변경을 수행한다', () => { const store = createFormStore() render( ) // 초기 상태 expect(screen.queryAllByTestId('field-item').length).toBe(0) // 필드 추가 2개 (번역 키 이슈 회피: 첫 번째 버튼 클릭) fireEvent.click(screen.getAllByRole('button')[0]) fireEvent.click(screen.getAllByRole('button')[0]) expect(screen.queryAllByTestId('field-item').length).toBe(2) // action URL 입력 const actionInput = screen.getByLabelText('Action URL') as HTMLInputElement fireEvent.change(actionInput, { target: { value: 'https://example.com/form' } }) expect(actionInput.value).toBe('https://example.com/form') expect(store.getState().actionUrl).toBe('https://example.com/form') // 스팸 옵션 - honeypot 변경 const hpInput = screen.getByLabelText('Honeypot Field') as HTMLInputElement fireEvent.change(hpInput, { target: { value: '_trap' } }) expect(store.getState().spam.honeypotFieldName).toBe('_trap') // 스팸 옵션 - min seconds 변경 const tsInput = screen.getByLabelText('Min Submit Seconds') as HTMLInputElement fireEvent.change(tsInput, { target: { value: '5' } }) expect(store.getState().spam.minSubmitSeconds).toBe(5) }) it('pattern과 maxLength 편집이 상태에 반영된다', () => { const store = createFormStore() render( ) // 텍스트 필드 1개 추가 fireEvent.click(screen.getAllByRole('button')[0]) const pat0 = screen.getByLabelText('Pattern 0') as HTMLInputElement fireEvent.change(pat0, { target: { value: '^[A-Za-z ]+$' } }) expect(store.getState().fields[0].pattern).toBe('^[A-Za-z ]+$') const max0 = screen.getByLabelText('MaxLength 0') as HTMLInputElement fireEvent.change(max0, { target: { value: '50' } }) expect(store.getState().fields[0].maxLength).toBe(50) }) it('select/radio 옵션 편집이 동작하고 추가 버튼으로 타입별 필드를 만든다', () => { const store = createFormStore() render( ) // select 추가 후 옵션 편집 (버튼 인덱스로 접근) const buttons1 = screen.getAllByRole('button') // 버튼 순서: Text, Email, Tel, Date, Textarea, Checkbox, Select, Radio fireEvent.click(buttons1[6]) const opts0 = screen.getByLabelText('Options 0') as HTMLInputElement fireEvent.change(opts0, { target: { value: 'A,B,C' } }) expect(store.getState().fields[0].type).toBe('select') expect((store.getState().fields[0] as SelectField).options).toEqual(['A','B','C']) // radio 추가 후 옵션 편집 const buttons2 = screen.getAllByRole('button') fireEvent.click(buttons2[7]) const opts1 = screen.getByLabelText('Options 1') as HTMLInputElement fireEvent.change(opts1, { target: { value: 'S,M,L' } }) expect(store.getState().fields[1].type).toBe('radio') expect((store.getState().fields[1] as RadioField).options).toEqual(['S','M','L']) // email/checkbox/textarea/date/tel도 추가 버튼 존재 (인덱스 기반 클릭) const buttons3 = screen.getAllByRole('button') fireEvent.click(buttons3[1]) // Email fireEvent.click(buttons3[5]) // Checkbox fireEvent.click(buttons3[4]) // Textarea fireEvent.click(buttons3[3]) // Date fireEvent.click(buttons3[2]) // Tel const names = store.getState().fields.map((f) => f.type) expect(names).toContain('email') expect(names).toContain('checkbox') expect(names).toContain('textarea') expect(names).toContain('date') expect(names).toContain('tel') }) it('필드 레이블/필수 편집, 이동/삭제가 동작한다', () => { const store = createFormStore() render( ) // 3개 추가 fireEvent.click(screen.getAllByRole('button')[0]) fireEvent.click(screen.getAllByRole('button')[0]) fireEvent.click(screen.getAllByRole('button')[0]) expect(screen.queryAllByTestId('field-item').length).toBe(3) // 첫 필드 레이블 변경 const label0 = screen.getByLabelText('Label 0') as HTMLInputElement fireEvent.change(label0, { target: { value: 'Name' } }) expect(store.getState().fields[0].label).toBe('Name') // 첫 필드 required 토글 const req0 = screen.getByLabelText('Required 0') as HTMLInputElement fireEvent.click(req0) expect(store.getState().fields[0].required).toBe(true) // 첫 필드를 아래로 이동 (0 -> 1) fireEvent.click(screen.getByRole('button', { name: 'Down 0' })) expect(store.getState().fields.map(f => f.name)).toEqual(['field2','field1','field3']) // 두 번째(이전 첫) 필드 삭제 fireEvent.click(screen.getByRole('button', { name: 'Remove 1' })) expect(store.getState().fields.map(f => f.name)).toEqual(['field2','field3']) }) })