import assert from 'node:assert/strict' import test from 'node:test' import { ref } from 'vue' import { useAdminMarkdownPreviewFocus } from '../composables/useAdminMarkdownPreviewFocus.js' /** * 테스트용 source line DOM 대역을 만든다. * @param {Object} options - 줄 옵션 * @returns {Object} 줄 DOM 대역 */ const createSourceLineStub = (options) => { return { line: options.line, rect: options.rect, closest: (selector) => selector === '[data-source-line]' ? options.closestResult ?? null : null, getAttribute: (name) => name === 'data-source-line' ? String(options.line) : null, getBoundingClientRect: () => options.rect } } /** * 테스트용 미리보기 표면 상태를 만든다. * @param {Object} options - 테스트 옵션 * @returns {Object} 테스트 상태 */ const createPreviewFocusState = (options = {}) => { const calls = { focusEditableAtLine: [], selectAllLiveDocument: 0 } const sourceLines = options.sourceLines ?? [] const surface = { scrollTop: options.scrollTop ?? 80, contains: (element) => options.containedElements?.includes(element) ?? false, querySelectorAll: (selector) => selector === '[data-source-line]' ? sourceLines : [], getBoundingClientRect: () => options.surfaceRect ?? { top: 10, bottom: 600, height: 590 } } const previewSurfaceRef = ref({ getPreviewElement: () => surface, getSourceLineElement: (sourceLine) => sourceLines.find((line) => line.line === sourceLine) ?? null, focusEditableAtLine: (...args) => { calls.focusEditableAtLine.push(args) }, selectAllLiveDocument: () => { calls.selectAllLiveDocument += 1 } }) const focusState = useAdminMarkdownPreviewFocus({ previewSurfaceRef, isClient: true, getActiveElement: () => options.activeElement ?? null }) return { calls, focusState, previewSurfaceRef, sourceLines, surface } } test('미리보기 표면 함수 호출을 표면 컴포넌트로 위임한다', () => { const line = createSourceLineStub({ line: 3, rect: { top: 40, bottom: 70, height: 30 } }) const state = createPreviewFocusState({ sourceLines: [line] }) assert.equal(state.focusState.getPreviewSurfaceElement(), state.surface) assert.equal(state.focusState.getPreviewSourceLineElement(3), line) state.focusState.focusPreviewEditableAtLine(3, 0, 'start') state.focusState.selectAllPreviewDocument() assert.deepEqual(state.calls.focusEditableAtLine, [[3, 0, 'start']]) assert.equal(state.calls.selectAllLiveDocument, 1) }) test('미리보기 상단 스크롤을 0으로 되돌린다', () => { const state = createPreviewFocusState() state.focusState.scrollPreviewToTop() assert.equal(state.surface.scrollTop, 0) }) test('현재 포커스가 미리보기 줄 안에 있으면 해당 source line을 반환한다', () => { const focusedLine = createSourceLineStub({ line: 7, rect: { top: 240, bottom: 270, height: 30 } }) const activeElement = { closest: (selector) => selector === '[data-source-line]' ? focusedLine : null } const state = createPreviewFocusState({ activeElement, containedElements: [activeElement], sourceLines: [focusedLine] }) assert.equal(state.focusState.getCurrentPreviewSourceLine(), 7) }) test('포커스 줄이 없으면 화면 상단 기준으로 가까운 source line을 반환한다', () => { const firstLine = createSourceLineStub({ line: 1, rect: { top: 4, bottom: 20, height: 16 } }) const secondLine = createSourceLineStub({ line: 2, rect: { top: 40, bottom: 70, height: 30 } }) const thirdLine = createSourceLineStub({ line: 3, rect: { top: 120, bottom: 150, height: 30 } }) const state = createPreviewFocusState({ sourceLines: [thirdLine, firstLine, secondLine], surfaceRect: { top: 10, bottom: 500, height: 490 } }) assert.equal(state.focusState.getCurrentPreviewSourceLine(), 2) }) test('클라이언트가 아니거나 표면이 없으면 현재 줄을 반환하지 않는다', () => { const previewSurfaceRef = ref(null) const focusState = useAdminMarkdownPreviewFocus({ previewSurfaceRef, isClient: false }) assert.equal(focusState.getCurrentPreviewSourceLine(), null) })