165 lines
5.1 KiB
JavaScript
165 lines
5.1 KiB
JavaScript
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)
|
|
})
|