154 lines
4.6 KiB
JavaScript
154 lines
4.6 KiB
JavaScript
import assert from 'node:assert/strict'
|
|
import test from 'node:test'
|
|
import { nextTick, ref } from 'vue'
|
|
import { useAdminMarkdownBlockActions } from '../composables/useAdminMarkdownBlockActions.js'
|
|
|
|
/**
|
|
* 테스트용 블록 액션 상태를 만든다.
|
|
* @param {Object} options - 테스트 옵션
|
|
* @returns {Object} 테스트 상태
|
|
*/
|
|
const createBlockActionsState = (options = {}) => {
|
|
const calls = {
|
|
applyPlans: [],
|
|
ensure: 0,
|
|
focusPreview: [],
|
|
insertGallery: [],
|
|
previewFocusRequests: [],
|
|
textareaSelections: []
|
|
}
|
|
const value = ref(options.value ?? '')
|
|
const activeMediaBlock = ref(options.activeMediaBlock ?? {
|
|
kind: 'image',
|
|
startLine: 0,
|
|
endLine: 0,
|
|
images: [{ alt: '기존', url: '/old.png' }]
|
|
})
|
|
const activeBlockContext = ref(options.activeBlockContext ?? activeMediaBlock.value)
|
|
const inlineColorSelectionContext = ref(options.inlineColorSelectionContext ?? null)
|
|
const actions = useAdminMarkdownBlockActions({
|
|
value,
|
|
activeMediaBlock,
|
|
activeBlockContext,
|
|
inlineColorSelectionContext,
|
|
applyLineReplacementPlan: (plan, focusEditor = false) => {
|
|
calls.applyPlans.push({ plan, focusEditor })
|
|
|
|
if (!plan) {
|
|
return false
|
|
}
|
|
|
|
const lines = String(value.value || '').split('\n')
|
|
lines.splice(plan.startLine, plan.endLine - plan.startLine + 1, ...plan.replacementLines)
|
|
value.value = lines.join('\n')
|
|
return true
|
|
},
|
|
ensureBlockPanelEngaged: () => {
|
|
calls.ensure += 1
|
|
},
|
|
insertGallery: (images) => {
|
|
calls.insertGallery.push(images)
|
|
},
|
|
setTextareaSelection: (...args) => {
|
|
calls.textareaSelections.push(args)
|
|
},
|
|
requestPreviewFocus: (focus) => {
|
|
calls.previewFocusRequests.push(focus)
|
|
},
|
|
focusPreviewEditableAtLine: (...args) => {
|
|
calls.focusPreview.push(args)
|
|
}
|
|
})
|
|
|
|
return {
|
|
actions,
|
|
activeBlockContext,
|
|
activeMediaBlock,
|
|
calls,
|
|
inlineColorSelectionContext,
|
|
value
|
|
}
|
|
}
|
|
|
|
test('이미지 표시 너비 변경을 현재 미디어 줄에 적용한다', () => {
|
|
const state = createBlockActionsState()
|
|
|
|
state.actions.updateActiveMediaImage(0, { pixelWidth: 240 })
|
|
|
|
assert.equal(state.value.value, '{size=240}')
|
|
assert.equal(state.calls.ensure, 1)
|
|
assert.equal(state.calls.applyPlans.at(-1).focusEditor, false)
|
|
})
|
|
|
|
test('파일명 캡션 사용은 이미지 URL 기준 기본 캡션을 채운다', () => {
|
|
const state = createBlockActionsState({
|
|
value: '',
|
|
activeMediaBlock: {
|
|
kind: 'image',
|
|
startLine: 0,
|
|
endLine: 0,
|
|
images: [{ alt: '', caption: '', url: '/uploads/posts/2026/07/sample.png' }]
|
|
}
|
|
})
|
|
|
|
state.actions.setActiveMediaUseAlt(0, true)
|
|
|
|
assert.equal(state.value.value, '')
|
|
assert.equal(state.calls.ensure, 1)
|
|
})
|
|
|
|
test('활성 갤러리가 없으면 갤러리 삽입 함수로 위임한다', () => {
|
|
const state = createBlockActionsState({
|
|
activeMediaBlock: null
|
|
})
|
|
const images = [{ url: '/a.png', alt: 'A' }]
|
|
|
|
state.actions.appendImagesToActiveGallery(images)
|
|
|
|
assert.deepEqual(state.calls.insertGallery, [images])
|
|
assert.equal(state.calls.applyPlans.length, 0)
|
|
})
|
|
|
|
test('소스 모드 인라인 색상 적용 후 textarea 선택 범위를 갱신한다', () => {
|
|
const state = createBlockActionsState({
|
|
value: '강조 문장',
|
|
inlineColorSelectionContext: {
|
|
kind: 'inline-color',
|
|
mode: 'source',
|
|
start: 0,
|
|
end: 2,
|
|
text: '강조'
|
|
}
|
|
})
|
|
|
|
state.actions.updateActiveInlineColor('red')
|
|
|
|
assert.equal(state.value.value, '::red[강조] 문장')
|
|
assert.equal(state.inlineColorSelectionContext.value, null)
|
|
assert.deepEqual(state.calls.textareaSelections, [[9, 9, { preserveScroll: true }]])
|
|
assert.equal(state.calls.ensure, 1)
|
|
})
|
|
|
|
test('라이브 모드 인라인 색상 적용 후 미리보기 포커스를 예약한다', async () => {
|
|
const state = createBlockActionsState({
|
|
value: '> 문장 테스트',
|
|
inlineColorSelectionContext: {
|
|
kind: 'inline-color',
|
|
mode: 'live',
|
|
sourceLine: 0,
|
|
startOffset: 0,
|
|
endOffset: 2,
|
|
bodyText: '문장 테스트',
|
|
text: '문장'
|
|
}
|
|
})
|
|
|
|
state.actions.updateActiveInlineColor('blue')
|
|
await nextTick()
|
|
|
|
assert.equal(state.value.value, '> ::blue[문장] 테스트')
|
|
assert.equal(state.inlineColorSelectionContext.value, null)
|
|
assert.deepEqual(state.calls.previewFocusRequests, [{ line: 0, offset: 2 }])
|
|
assert.deepEqual(state.calls.focusPreview, [[0, 0, 'auto', 2, 'nearest']])
|
|
})
|