Files
sori.studio/tests/useAdminMarkdownSlashCommandActions.test.js
2026-07-29 11:43:39 +09:00

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)
})