import assert from 'node:assert/strict' import test from 'node:test' import { nextTick, ref } from 'vue' import { useContentMarkdownLiveSelection } from '../composables/useContentMarkdownLiveSelection.js' /** * 테스트용 라이브 선택 상태를 만든다. * @param {Object} calls - 콜백 기록 객체 * @returns {{ content: import('vue').Ref, state: Object }} 상태 */ const createLiveSelectionState = (calls = {}) => { const content = ref('첫 줄\n둘째 줄') const state = useContentMarkdownLiveSelection({ content, interactive: ref(true), onLineFocus: (line) => { calls.focus = line }, onLineBlur: () => { calls.blur = true }, onContentReplace: (payload) => { calls.replace = payload }, provideBridge: false }) return { content, state } } test('라이브 선택 상태는 자식 편집기 포커스 요청과 원문 모드 표시를 관리한다', () => { const calls = {} const { state } = createLiveSelectionState(calls) state.onEditorFocusLine({ line: 3, position: 'end', offset: 2 }) assert.equal(state.pendingFocusLine.value, 3) assert.equal(state.pendingFocusPosition.value, 'end') assert.equal(state.pendingFocusOffset.value, 2) state.onInlineRawMode({ sourceLine: 7, active: true }) assert.equal(state.rawEditingSourceLines.value.has(7), true) state.onInlineRawMode({ sourceLine: 7, active: false }) assert.equal(state.rawEditingSourceLines.value.has(7), false) state.emitEditorFocusedLine(5) assert.equal(calls.focus, 5) }) test('라이브 선택 상태는 본문 변경 후 예약된 포커스 값을 초기화한다', async () => { const { content, state } = createLiveSelectionState() state.onEditorFocusLine({ line: 1, position: 'start' }) content.value = '바뀐 줄' await nextTick() assert.equal(state.pendingFocusLine.value, null) assert.equal(state.pendingFocusPosition.value, 'auto') assert.equal(state.pendingFocusOffset.value, null) }) test('라이브 줄 포커스와 블러 이벤트를 상위 콜백으로 전달한다', () => { class FakeElement { /** * @param {number} line - 원본 줄 번호 */ constructor(line) { this.line = line } /** * 가장 가까운 데이터 줄 요소를 반환한다. * @returns {FakeElement} 자기 자신 */ closest() { return this } /** * 속성 값을 반환한다. * @param {string} name - 속성명 * @returns {string|null} 속성 값 */ getAttribute(name) { return name === 'data-source-line' ? String(this.line) : null } } const previousElement = globalThis.Element const previousNode = globalThis.Node globalThis.Element = FakeElement globalThis.Node = FakeElement try { const calls = {} const { state } = createLiveSelectionState(calls) const target = new FakeElement(4) state.emitLiveLineFocus({ target }) assert.equal(calls.focus, 4) state.rendererRootRef.value = { contains: (node) => node === target } state.emitLiveLineBlur({ relatedTarget: target }) assert.equal(calls.blur, undefined) state.emitLiveLineBlur({ relatedTarget: new FakeElement(9) }) assert.equal(calls.blur, true) } finally { globalThis.Element = previousElement globalThis.Node = previousNode } })