슬래시 메뉴 위치 관리 분리
This commit is contained in:
216
tests/useAdminMarkdownSlashMenuPosition.test.js
Normal file
216
tests/useAdminMarkdownSlashMenuPosition.test.js
Normal file
@@ -0,0 +1,216 @@
|
||||
import assert from 'node:assert/strict'
|
||||
import test from 'node:test'
|
||||
import { computed, effectScope, nextTick, ref } from 'vue'
|
||||
import { useAdminMarkdownSlashMenuPosition } from '../composables/useAdminMarkdownSlashMenuPosition.js'
|
||||
|
||||
/**
|
||||
* 테스트용 브라우저 window 객체를 설치한다.
|
||||
* @returns {{ added: string[], removed: string[], restore: () => void }} 이벤트 기록과 복구 함수
|
||||
*/
|
||||
const installWindowStub = () => {
|
||||
const previousWindow = globalThis.window
|
||||
const added = []
|
||||
const removed = []
|
||||
|
||||
globalThis.window = {
|
||||
innerHeight: 800,
|
||||
innerWidth: 1200,
|
||||
addEventListener: (eventName) => {
|
||||
added.push(eventName)
|
||||
},
|
||||
removeEventListener: (eventName) => {
|
||||
removed.push(eventName)
|
||||
}
|
||||
}
|
||||
|
||||
return {
|
||||
added,
|
||||
removed,
|
||||
restore: () => {
|
||||
if (previousWindow === undefined) {
|
||||
delete globalThis.window
|
||||
return
|
||||
}
|
||||
|
||||
globalThis.window = previousWindow
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 슬래시 메뉴 위치 composable 테스트 상태를 생성한다.
|
||||
* @param {Object} [overrides] - 상태 재정의
|
||||
* @returns {Object} 테스트 상태
|
||||
*/
|
||||
const createSlashMenuPositionHarness = (overrides = {}) => {
|
||||
const activeMode = ref(overrides.activeMode ?? 'preview')
|
||||
const markdownValue = ref(overrides.markdownValue ?? '/image')
|
||||
const liveSlashSourceLine = ref(overrides.sourceLine ?? 0)
|
||||
const liveSlashQuery = ref(overrides.query ?? 'image')
|
||||
const liveSlashHighlightedIndex = ref(overrides.highlightedIndex ?? 0)
|
||||
const liveSlashMenuPosition = ref({ top: 0, left: 0 })
|
||||
const liveSlashMenuRef = ref(overrides.menuRef ?? null)
|
||||
const liveSlashVisible = computed(() => liveSlashSourceLine.value !== null)
|
||||
const visibleLiveSlashCommands = ref(overrides.commands ?? [{ id: 'image' }, { id: 'gallery' }])
|
||||
const resetCalls = []
|
||||
const clampCalls = []
|
||||
const previewSurfaceEvents = []
|
||||
const textareaEvents = []
|
||||
const previewSurface = {
|
||||
addEventListener: (eventName) => previewSurfaceEvents.push(`add:${eventName}`),
|
||||
removeEventListener: (eventName) => previewSurfaceEvents.push(`remove:${eventName}`)
|
||||
}
|
||||
const textarea = {
|
||||
scrollTop: 0,
|
||||
scrollLeft: 0,
|
||||
addEventListener: (eventName) => textareaEvents.push(`add:${eventName}`),
|
||||
removeEventListener: (eventName) => textareaEvents.push(`remove:${eventName}`),
|
||||
getBoundingClientRect: () => ({ top: 20, left: 30 })
|
||||
}
|
||||
|
||||
return {
|
||||
options: {
|
||||
activeMode,
|
||||
markdownValue,
|
||||
liveSlashSourceLine,
|
||||
liveSlashQuery,
|
||||
liveSlashHighlightedIndex,
|
||||
liveSlashMenuPosition,
|
||||
liveSlashMenuRef,
|
||||
liveSlashVisible,
|
||||
visibleLiveSlashCommands,
|
||||
resetLiveSlashHighlightedIndex: () => resetCalls.push('reset'),
|
||||
clampLiveSlashHighlightedIndex: (commandCount) => clampCalls.push(commandCount),
|
||||
getPreviewSourceLineElement: () => ({
|
||||
getBoundingClientRect: () => ({ top: 100, bottom: 124, left: 48 })
|
||||
}),
|
||||
getPreviewSurfaceElement: () => previewSurface,
|
||||
getWriteTextareaElement: () => textarea,
|
||||
isClient: true
|
||||
},
|
||||
activeMode,
|
||||
liveSlashSourceLine,
|
||||
liveSlashQuery,
|
||||
liveSlashHighlightedIndex,
|
||||
liveSlashMenuPosition,
|
||||
liveSlashMenuRef,
|
||||
visibleLiveSlashCommands,
|
||||
resetCalls,
|
||||
clampCalls,
|
||||
previewSurfaceEvents,
|
||||
textareaEvents
|
||||
}
|
||||
}
|
||||
|
||||
test('관리자 Markdown 슬래시 메뉴 위치는 미리보기 줄 기준으로 갱신한다', async () => {
|
||||
const windowStub = installWindowStub()
|
||||
const scope = effectScope()
|
||||
const harness = createSlashMenuPositionHarness({
|
||||
menuRef: {
|
||||
offsetHeight: 160,
|
||||
offsetWidth: 240
|
||||
}
|
||||
})
|
||||
|
||||
try {
|
||||
scope.run(() => {
|
||||
const position = useAdminMarkdownSlashMenuPosition(harness.options)
|
||||
position.updateLiveSlashMenuPosition()
|
||||
})
|
||||
|
||||
await nextTick()
|
||||
await nextTick()
|
||||
|
||||
assert.deepEqual(harness.liveSlashMenuPosition.value, {
|
||||
top: 128,
|
||||
left: 48
|
||||
})
|
||||
} finally {
|
||||
scope.stop()
|
||||
windowStub.restore()
|
||||
}
|
||||
})
|
||||
|
||||
test('관리자 Markdown 슬래시 메뉴 위치는 노출 상태에 맞춰 스크롤 리스너를 정리한다', async () => {
|
||||
const windowStub = installWindowStub()
|
||||
const scope = effectScope()
|
||||
const harness = createSlashMenuPositionHarness()
|
||||
|
||||
try {
|
||||
scope.run(() => {
|
||||
useAdminMarkdownSlashMenuPosition(harness.options)
|
||||
})
|
||||
|
||||
harness.liveSlashSourceLine.value = null
|
||||
await nextTick()
|
||||
harness.liveSlashSourceLine.value = 0
|
||||
await nextTick()
|
||||
harness.liveSlashSourceLine.value = null
|
||||
await nextTick()
|
||||
|
||||
assert.deepEqual(windowStub.added, ['scroll', 'resize'])
|
||||
assert.deepEqual(windowStub.removed.slice(-2), ['scroll', 'resize'])
|
||||
assert.deepEqual(harness.previewSurfaceEvents, ['add:scroll', 'remove:scroll'])
|
||||
assert.deepEqual(harness.textareaEvents, ['add:scroll', 'remove:scroll'])
|
||||
} finally {
|
||||
scope.stop()
|
||||
windowStub.restore()
|
||||
}
|
||||
})
|
||||
|
||||
test('관리자 Markdown 슬래시 메뉴 하이라이트는 메뉴 안으로 스크롤된다', async () => {
|
||||
const windowStub = installWindowStub()
|
||||
const scope = effectScope()
|
||||
const menu = {
|
||||
offsetHeight: 160,
|
||||
offsetWidth: 240,
|
||||
scrollTop: 0,
|
||||
clientHeight: 80,
|
||||
querySelector: () => ({
|
||||
offsetTop: 120,
|
||||
offsetHeight: 32
|
||||
})
|
||||
}
|
||||
const harness = createSlashMenuPositionHarness({
|
||||
menuRef: {
|
||||
getRootElement: () => menu
|
||||
}
|
||||
})
|
||||
|
||||
try {
|
||||
scope.run(() => {
|
||||
useAdminMarkdownSlashMenuPosition(harness.options)
|
||||
})
|
||||
|
||||
harness.liveSlashHighlightedIndex.value = 1
|
||||
await nextTick()
|
||||
await nextTick()
|
||||
|
||||
assert.equal(menu.scrollTop, 72)
|
||||
} finally {
|
||||
scope.stop()
|
||||
windowStub.restore()
|
||||
}
|
||||
})
|
||||
|
||||
test('관리자 Markdown 슬래시 메뉴 검색어와 명령 수 변경은 하이라이트를 보정한다', async () => {
|
||||
const windowStub = installWindowStub()
|
||||
const scope = effectScope()
|
||||
const harness = createSlashMenuPositionHarness()
|
||||
|
||||
try {
|
||||
scope.run(() => {
|
||||
useAdminMarkdownSlashMenuPosition(harness.options)
|
||||
})
|
||||
|
||||
harness.liveSlashQuery.value = 'callout'
|
||||
harness.visibleLiveSlashCommands.value = [{ id: 'callout' }]
|
||||
await nextTick()
|
||||
|
||||
assert.deepEqual(harness.resetCalls, ['reset'])
|
||||
assert.deepEqual(harness.clampCalls, [1])
|
||||
} finally {
|
||||
scope.stop()
|
||||
windowStub.restore()
|
||||
}
|
||||
})
|
||||
Reference in New Issue
Block a user