슬래시 명령 실행 분리
This commit is contained in:
164
tests/useAdminMarkdownSlashCommandActions.test.js
Normal file
164
tests/useAdminMarkdownSlashCommandActions.test.js
Normal file
@@ -0,0 +1,164 @@
|
||||
import assert from 'node:assert/strict'
|
||||
import test from 'node:test'
|
||||
import { nextTick, ref } from 'vue'
|
||||
import { MARKDOWN_SLASH_COMMANDS } from '../lib/markdown-slash-commands.js'
|
||||
import { useAdminMarkdownSlashCommandActions } from '../composables/useAdminMarkdownSlashCommandActions.js'
|
||||
|
||||
/**
|
||||
* 테스트용 슬래시 명령 실행 상태를 생성한다.
|
||||
* @param {Object} [options] - 테스트 옵션
|
||||
* @returns {Object} 테스트 상태
|
||||
*/
|
||||
const createSlashCommandActionsState = (options = {}) => {
|
||||
const calls = {
|
||||
clear: 0,
|
||||
focusPreview: [],
|
||||
media: [],
|
||||
release: [],
|
||||
replace: [],
|
||||
selection: [],
|
||||
suppress: []
|
||||
}
|
||||
const activeMode = ref(options.activeMode ?? 'write')
|
||||
const markdownValue = ref(options.markdownValue ?? '/image\n아래')
|
||||
const liveSlashSourceLine = ref(options.sourceLine ?? 0)
|
||||
const liveSlashQuery = ref(options.query ?? '')
|
||||
const liveSlashInsertLine = ref(null)
|
||||
const actions = useAdminMarkdownSlashCommandActions({
|
||||
activeMode,
|
||||
markdownValue,
|
||||
liveSlashSourceLine,
|
||||
liveSlashQuery,
|
||||
liveSlashInsertLine,
|
||||
clearLiveSlashState: () => {
|
||||
calls.clear += 1
|
||||
liveSlashSourceLine.value = null
|
||||
},
|
||||
releaseLiveSlashLine: (line) => calls.release.push(line),
|
||||
suppressLiveSlashLine: (line, value) => calls.suppress.push({ line, value }),
|
||||
replaceLineRange: (startLine, endLine, replacementLines, focusEditor = true) => {
|
||||
calls.replace.push({ startLine, endLine, replacementLines, focusEditor })
|
||||
const lines = String(markdownValue.value || '').split('\n')
|
||||
lines.splice(startLine, endLine - startLine + 1, ...replacementLines)
|
||||
markdownValue.value = lines.join('\n')
|
||||
},
|
||||
openMediaPicker: (mediaKind) => calls.media.push(mediaKind),
|
||||
setTextareaSelection: (start, end) => calls.selection.push({ start, end }),
|
||||
focusPreviewEditableAtLine: (...args) => calls.focusPreview.push(args),
|
||||
isClient: options.isClient ?? false
|
||||
})
|
||||
|
||||
return {
|
||||
actions,
|
||||
activeMode,
|
||||
calls,
|
||||
liveSlashInsertLine,
|
||||
liveSlashQuery,
|
||||
liveSlashSourceLine,
|
||||
markdownValue
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 테스트용 프레임 콜백을 즉시 실행하도록 설치한다.
|
||||
* @returns {() => void} 복구 함수
|
||||
*/
|
||||
const installFrameStub = () => {
|
||||
const previousFrame = globalThis.requestAnimationFrame
|
||||
globalThis.requestAnimationFrame = (callback) => {
|
||||
callback()
|
||||
return 1
|
||||
}
|
||||
|
||||
return () => {
|
||||
if (previousFrame === undefined) {
|
||||
delete globalThis.requestAnimationFrame
|
||||
return
|
||||
}
|
||||
|
||||
globalThis.requestAnimationFrame = previousFrame
|
||||
}
|
||||
}
|
||||
|
||||
test('관리자 Markdown 슬래시 미디어 명령은 원문 줄을 제거하고 미디어 모달을 연다', () => {
|
||||
const state = createSlashCommandActionsState()
|
||||
|
||||
state.actions.applyLiveSlashCommand(MARKDOWN_SLASH_COMMANDS.find((command) => command.id === 'image'))
|
||||
|
||||
assert.equal(state.markdownValue.value, '아래')
|
||||
assert.equal(state.liveSlashInsertLine.value, 0)
|
||||
assert.deepEqual(state.calls.replace, [{
|
||||
startLine: 0,
|
||||
endLine: 0,
|
||||
replacementLines: [],
|
||||
focusEditor: false
|
||||
}])
|
||||
assert.deepEqual(state.calls.media, ['image'])
|
||||
assert.equal(state.calls.clear, 1)
|
||||
})
|
||||
|
||||
test('관리자 Markdown 슬래시 줄 명령은 작성 모드에서 textarea 선택을 복원한다', () => {
|
||||
const state = createSlashCommandActionsState({
|
||||
markdownValue: '/h2\n아래',
|
||||
query: 'h2'
|
||||
})
|
||||
|
||||
state.actions.applyLiveSlashCommand(MARKDOWN_SLASH_COMMANDS.find((command) => command.id === 'h2'))
|
||||
|
||||
assert.equal(state.markdownValue.value, '## \n아래')
|
||||
assert.deepEqual(state.calls.release, [0])
|
||||
assert.deepEqual(state.calls.selection, [{ start: 3, end: 3 }])
|
||||
assert.equal(state.calls.clear, 1)
|
||||
})
|
||||
|
||||
test('관리자 Markdown 슬래시 줄 명령은 라이브 모드에서 미리보기 포커스를 예약한다', async () => {
|
||||
const restoreFrame = installFrameStub()
|
||||
const state = createSlashCommandActionsState({
|
||||
activeMode: 'preview',
|
||||
markdownValue: '/code\n아래',
|
||||
query: 'code',
|
||||
isClient: true
|
||||
})
|
||||
|
||||
try {
|
||||
state.actions.applyLiveSlashCommand(MARKDOWN_SLASH_COMMANDS.find((command) => command.id === 'code'))
|
||||
await nextTick()
|
||||
await nextTick()
|
||||
|
||||
assert.equal(state.markdownValue.value, ['```', '', '```', '아래'].join('\n'))
|
||||
assert.deepEqual(state.calls.focusPreview, [[1, 0, 'end', 0]])
|
||||
} finally {
|
||||
restoreFrame()
|
||||
}
|
||||
})
|
||||
|
||||
test('관리자 Markdown 슬래시 적용 payload 후보가 없으면 입력 상태를 종료한다', () => {
|
||||
const state = createSlashCommandActionsState({
|
||||
query: '없는명령',
|
||||
sourceLine: 2
|
||||
})
|
||||
|
||||
state.actions.onLiveSlashApply({
|
||||
sourceLine: 2,
|
||||
value: '/없는명령'
|
||||
})
|
||||
|
||||
assert.deepEqual(state.calls.release, [2])
|
||||
assert.equal(state.calls.clear, 1)
|
||||
assert.deepEqual(state.calls.replace, [])
|
||||
})
|
||||
|
||||
test('관리자 Markdown 슬래시 취소는 현재 줄 내용을 기준으로 억제한다', () => {
|
||||
const state = createSlashCommandActionsState({
|
||||
markdownValue: '첫 줄\n/gallery',
|
||||
sourceLine: 1
|
||||
})
|
||||
|
||||
state.actions.cancelLiveSlashCommand()
|
||||
|
||||
assert.deepEqual(state.calls.suppress, [{
|
||||
line: 1,
|
||||
value: '/gallery'
|
||||
}])
|
||||
assert.equal(state.calls.clear, 1)
|
||||
})
|
||||
Reference in New Issue
Block a user