Files
sori.studio/tests/useAdminMarkdownBlockPanel.test.js

155 lines
4.1 KiB
JavaScript

import assert from 'node:assert/strict'
import test from 'node:test'
import { effectScope, nextTick, ref } from 'vue'
import { useAdminMarkdownBlockPanel } from '../composables/useAdminMarkdownBlockPanel.js'
globalThis.window ??= {
setTimeout,
clearTimeout
}
/**
* 테스트용 블록 패널 상태를 만든다.
* @param {Object} options - 테스트 옵션
* @returns {Object} 테스트 상태
*/
const createBlockPanelState = (options = {}) => {
const scope = effectScope()
const activeMode = ref(options.activeMode ?? 'write')
const value = ref(options.value ?? '![이미지](/a.png)')
const writeModeContextValue = ref(null)
const activeLogicalLineIndex = ref(options.activeLogicalLineIndex ?? 0)
const isMediaPickerOpen = ref(false)
const currentBlock = ref(options.block ?? {
kind: 'image',
startLine: 0,
endLine: 0
})
const inlineContext = ref(null)
const emitted = []
let panel
scope.run(() => {
panel = useAdminMarkdownBlockPanel({
activeMode,
value,
writeModeContextValue,
activeLogicalLineIndex,
isMediaPickerOpen,
resolveBlockContext: () => currentBlock.value,
getInlineColorSelectionContext: () => inlineContext.value,
emitBlockPanel: (payload) => {
emitted.push(payload)
}
})
})
return {
activeLogicalLineIndex,
currentBlock,
emitted,
inlineContext,
isMediaPickerOpen,
panel,
stop: () => scope.stop()
}
}
/**
* 패널 watch와 내부 nextTick을 비운다.
* @returns {Promise<void>}
*/
const flushBlockPanelTicks = async () => {
await nextTick()
await nextTick()
}
test('textarea 포커스가 있으면 활성 블록 패널을 연다', () => {
const state = createBlockPanelState()
state.panel.handleTextareaFocusIn()
state.panel.syncBlockPanelState()
assert.equal(state.panel.isBlockPanelVisible.value, true)
assert.deepEqual(state.emitted.at(-1), {
open: true,
panel: {
kind: 'image',
startLine: 0,
endLine: 0
}
})
state.stop()
})
test('인라인 색상 선택 컨텍스트는 일반 블록보다 우선 표시된다', () => {
const state = createBlockPanelState()
state.inlineContext.value = {
kind: 'inline-color',
mode: 'source',
start: 0,
end: 2,
text: '색상'
}
state.panel.syncInlineColorSelectionContext()
state.panel.syncBlockPanelState()
assert.equal(state.panel.isBlockPanelEngaged.value, true)
assert.equal(state.panel.activePanelContext.value.kind, 'inline-color')
assert.deepEqual(state.emitted.at(-1).panel, state.inlineContext.value)
state.stop()
})
test('IME 조합 중 활성 블록이 잠시 비어도 직전 블록 패널을 유지한다', async () => {
const state = createBlockPanelState()
state.panel.handleTextareaFocusIn()
state.panel.handleTextareaCompositionStart()
state.currentBlock.value = null
state.activeLogicalLineIndex.value = 0
await flushBlockPanelTicks()
assert.equal(state.panel.activePanelContext.value.kind, 'image')
state.stop()
})
test('라이브 줄 포커스는 해당 줄 블록 패널을 편집 중 상태로 연다', async () => {
const state = createBlockPanelState({
activeMode: 'preview',
block: {
kind: 'callout',
startLine: 2,
endLine: 4
}
})
await state.panel.handleLiveLineFocus(3)
await flushBlockPanelTicks()
assert.equal(state.activeLogicalLineIndex.value, 3)
assert.equal(state.panel.isBlockPanelEngaged.value, true)
assert.equal(state.panel.activePanelContext.value.kind, 'callout')
assert.equal(state.emitted.at(-1).open, true)
state.stop()
})
test('패널 닫기는 편집 상태와 인라인 색상 컨텍스트를 초기화한다', () => {
const state = createBlockPanelState()
state.inlineContext.value = {
kind: 'inline-color',
mode: 'source',
start: 0,
end: 2,
text: '색상'
}
state.panel.syncInlineColorSelectionContext()
state.panel.closeBlockPanel()
assert.equal(state.panel.isBlockPanelEngaged.value, false)
assert.equal(state.panel.inlineColorSelectionContext.value, null)
assert.equal(state.emitted.at(-1).open, false)
state.stop()
})