WYSIWYG: Moveable 가이드라인 고도화(중심/가장자리 스냅, 8px 임계) TDD/구현 및 테스트
Auto PR / open-pr (push) Successful in 18s
Auto Label PR / add-automerge-label (pull_request) Successful in 7s
CI / test (pull_request) Successful in 1m13s

This commit is contained in:
2025-11-16 23:21:43 +09:00
parent ccb706de5b
commit 1b1b916a30
7 changed files with 514 additions and 0 deletions
@@ -0,0 +1,68 @@
import React from 'react'
import { describe, it, expect, vi, beforeEach } from 'vitest'
import { render, screen, waitFor } from '@testing-library/react'
import { act } from 'react-dom/test-utils'
import { Canvas } from '@/components/wysiwyg/Canvas'
import type { Frame } from '@/lib/wysiwyg/schema'
const capture = vi.fn()
type MProps = {
onDragStart?: () => void
onDrag?: (e: { left?: number; top?: number }) => void
onDragEnd?: () => void
}
vi.mock('react-moveable', () => {
const Mock = (props: Record<string, unknown>) => {
capture(props)
return <div data-testid="moveable" />
}
return { default: Mock }
})
const frame: Frame = {
id: 'f-guides',
width: 500,
height: 400,
background: '#fff',
layers: [
{ id: 'l1', name: 'L1', visible: true, locked: false, objects: [
// 드래그 대상(o1) - 기본 선택이 되도록 첫 번째로 배치
{ id: 'o1', type: 'text', x: 10, y: 10, width: 40, height: 30, rotate: 0, zIndex: 1, props: { text: 'A', fontSize: 16, color: '#000' } },
// 대상(o2)의 수직 중심선은 x = 240 (x=200, w=80)
{ id: 'o2', type: 'text', x: 200, y: 50, width: 80, height: 30, rotate: 0, zIndex: 0, props: { text: 'B', fontSize: 16, color: '#000' } },
]}
]
}
describe('Canvas guidelines advanced (object-object snap)', () => {
beforeEach(() => capture.mockReset())
it('shows vertical guide and snaps x to other object center within 8px threshold', async () => {
render(<Canvas frame={frame} />)
const last = capture.mock.calls.at(-1)?.[0] as MProps
expect(last).toBeTruthy()
// 드래그 시작 → 가이드 활성화 상태
await act(async () => { last.onDragStart?.() })
// o1의 중심을 o2 중심(240)에 근접시키도록 left를 220로 제안
// o1 width=40이므로 중심은 left+20 → 240이 되도록 left=220 필요
await act(async () => { last.onDrag?.({ left: 220, top: 10 }) })
const obj = screen.getByTestId('obj-o1') as HTMLElement
// x 스냅 결과: left가 정확히 220으로 맞춰짐
await waitFor(() => {
expect(obj.style.left).toBe('220px')
})
// 가이드 라인 표시
await screen.findByTestId('guide-x')
// 드래그 종료 → 가이드 해제
await act(async () => { last.onDragEnd?.() })
await waitFor(() => {
expect(screen.queryByTestId('guide-x')).toBeNull()
})
})
})