Files
landing-builder/lib/wysiwyg/export.form.test.ts
T
jaybe 5bf78b3e01
Auto PR / open-pr (push) Successful in 21s
Auto Label PR / add-automerge-label (pull_request) Successful in 6s
CI / test (pull_request) Successful in 1m49s
테스트/린트 안정화: 버튼 쿼리 모호성 제거, 훅 deps 보강, vi.spyOn 모킹, 멀티선택 Moveable 간섭 방지 재확인, 스냅 우선순위 회귀 그린
2025-11-17 17:33:39 +09:00

63 lines
3.6 KiB
TypeScript

import { describe, it, expect } from 'vitest'
import { exportFrame } from '@/lib/wysiwyg/export'
import type { Frame } from '@/lib/wysiwyg/schema'
function baseFrame(): Frame {
return {
id: 'f', width: 400, height: 300, background: '#ffffff',
layers: [{ id: 'l1', name: 'Base', visible: true, locked: false, objects: [] }],
}
}
describe('wysiwyg export - forms a11y', () => {
it('exports input with label/for and required/placeholder', () => {
const f = baseFrame()
f.layers[0].objects.push({ id: 'in1', type: 'input', x: 10, y: 20, width: 260, height: 48, rotate: 0, zIndex: 0, props: { name: 'email', label: 'Email', placeholder: 'you@example.com', required: true } })
const out = exportFrame(f)
// label and input id/for
expect(out.html).toMatch(/<label for="in1-control">Email<\/label>/)
expect(out.html).toMatch(/<input id="in1-control" name="email"[^>]*placeholder="you@example.com"[^>]*required/)
// absolutely positioned wrapper
expect(out.css).toMatch(/#in1\s*{[^}]*left:10px;[^}]*top:20px;[^}]*width:260px;[^}]*height:48px/)
})
it('exports select with options and defaultValue', () => {
const f = baseFrame()
f.layers[0].objects.push({ id: 'sel1', type: 'select', x: 12, y: 30, width: 220, height: 40, rotate: 0, zIndex: 0, props: { name: 'size', label: 'Size', options: ['S','M','L'], defaultValue: 'M' } })
const out = exportFrame(f)
expect(out.html).toMatch(/<label for="sel1-control">Size<\/label>/)
expect(out.html).toMatch(/<select id="sel1-control" name="size"/)
expect(out.html).toMatch(/<option value="S">S<\/option>/)
expect(out.html).toMatch(/<option value="M" selected>M<\/option>/)
expect(out.html).toMatch(/<option value="L">L<\/option>/)
})
it('exports textarea with label and rows/placeholder/required', () => {
const f = baseFrame()
f.layers[0].objects.push({ id: 'ta1', type: 'textarea', x: 14, y: 40, width: 300, height: 90, rotate: 0, zIndex: 0, props: { name: 'message', label: 'Message', placeholder: 'Type...', rows: 5, required: true } })
const out = exportFrame(f)
expect(out.html).toMatch(/<label for="ta1-control">Message<\/label>/)
expect(out.html).toMatch(/<textarea id="ta1-control" name="message"[^>]*rows="5"[^>]*placeholder="Type..."[^>]*required/)
})
it('exports radio group with fieldset/legend and checked default', () => {
const f = baseFrame()
f.layers[0].objects.push({ id: 'rg1', type: 'radio', x: 16, y: 60, width: 300, height: 80, rotate: 0, zIndex: 0, props: { name: 'color', legend: 'Color', options: ['Red','Green','Blue'], defaultValue: 'Green', required: true } })
const out = exportFrame(f)
expect(out.html).toMatch(/<fieldset id="rg1"/)
expect(out.html).toMatch(/<legend>Color<\/legend>/)
expect(out.html).toMatch(/<input type="radio" id="rg1-1" name="color" value="Green" checked required\/>/)
expect(out.html).toMatch(/<label for="rg1-1">Green<\/label>/)
})
it('exports checkbox group with fieldset/legend and defaultValues', () => {
const f = baseFrame()
f.layers[0].objects.push({ id: 'cb1', type: 'checkbox', x: 18, y: 80, width: 320, height: 100, rotate: 0, zIndex: 0, props: { name: 'agreements', legend: 'Agree', options: ['One','Two','Three'], defaultValues: ['Two','Three'], required: true } })
const out = exportFrame(f)
expect(out.html).toMatch(/<fieldset id="cb1"/)
expect(out.html).toMatch(/<legend>Agree<\/legend>/)
expect(out.html).toMatch(/<input type="checkbox" id="cb1-1" name="agreements" value="Two" checked required\/>/)
expect(out.html).toMatch(/<label for="cb1-1">Two<\/label>/)
})
})