블록 패널 액션 적용 분리
This commit is contained in:
153
tests/useAdminMarkdownBlockActions.test.js
Normal file
153
tests/useAdminMarkdownBlockActions.test.js
Normal file
@@ -0,0 +1,153 @@
|
||||
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']])
|
||||
})
|
||||
Reference in New Issue
Block a user