import assert from 'node:assert/strict' import { describe, it } from 'node:test' import { ref } from 'vue' import { useAdminMarkdownMediaActions } from '../composables/useAdminMarkdownMediaActions.js' /** * 미디어 액션 테스트 상태를 만든다. * @param {Object} [overrides] - 상태 재정의 * @returns {Object} 테스트 상태 */ const createMediaActionHarness = (overrides = {}) => { const value = ref(overrides.value ?? '본문') const mediaPickerTarget = ref(overrides.target ?? 'image') const liveSlashInsertLine = ref(overrides.liveSlashLine ?? null) const isUploading = ref(overrides.isUploading ?? false) const isMediaPickerOpen = ref(overrides.isMediaPickerOpen ?? false) const activeMediaPickerTab = ref(overrides.activeMediaPickerTab ?? 'upload') const calls = { insertedBlocks: [], activeGalleryImages: [], closed: 0, fetched: 0, toasts: [] } const selectedItems = ref(overrides.selectedItems ?? []) const uploadedItems = ref(overrides.uploadedItems ?? []) const modalUploadedItems = ref(overrides.modalUploadedItems ?? []) const actions = useAdminMarkdownMediaActions({ value, mediaPickerTarget, liveSlashInsertLine, isUploading, isMediaPickerOpen, activeMediaPickerTab, resolveCurrentSelectedMediaItems: () => selectedItems.value, uploadMediaFilesWithState: async () => uploadedItems.value, uploadFilesIntoMediaPicker: async () => modalUploadedItems.value, fetchMediaItems: async () => { calls.fetched += 1 }, closeMediaPicker: () => { calls.closed += 1 }, insertBlockSnippet: (snippet) => { calls.insertedBlocks.push(snippet) value.value = `${value.value}\n${snippet}` }, appendImagesToActiveGallery: (images) => { calls.activeGalleryImages.push(images) }, showToast: (type, message) => { calls.toasts.push({ type, message }) }, resolveUploadFetchErrorMessage: (error) => `업로드 오류: ${error.message}` }) return { value, mediaPickerTarget, liveSlashInsertLine, isUploading, isMediaPickerOpen, activeMediaPickerTab, selectedItems, uploadedItems, modalUploadedItems, calls, actions } } describe('useAdminMarkdownMediaActions', () => { it('일반 이미지와 갤러리를 블록 조각으로 삽입한다', () => { const imageHarness = createMediaActionHarness({ selectedItems: [{ url: '/uploads/a.png' }] }) imageHarness.actions.applyMediaSelection() assert.deepEqual(imageHarness.calls.insertedBlocks, ['![](/uploads/a.png)']) assert.equal(imageHarness.calls.closed, 1) const galleryHarness = createMediaActionHarness({ target: 'gallery', selectedItems: [{ url: '/uploads/a.png' }, { url: '/uploads/b.png' }] }) galleryHarness.actions.applyMediaSelection() assert.deepEqual(galleryHarness.calls.insertedBlocks, [ [':::gallery', '![](/uploads/a.png)', '![](/uploads/b.png)', ':::'].join('\n') ]) }) it('활성 갤러리 대상은 블록 삽입 대신 갤러리 추가 콜백을 호출한다', () => { const harness = createMediaActionHarness({ target: 'active-gallery', selectedItems: [{ url: '/uploads/a.png' }, { url: '/uploads/b.png' }] }) harness.actions.applyMediaSelection() assert.equal(harness.calls.insertedBlocks.length, 0) assert.deepEqual(harness.calls.activeGalleryImages, [[ { url: '/uploads/a.png', useAlt: false, caption: '' }, { url: '/uploads/b.png', useAlt: false, caption: '' } ]]) }) it('슬래시 미디어 대기 줄에는 해당 줄 위치로 삽입하고 대기 상태를 초기화한다', () => { const harness = createMediaActionHarness({ value: '첫 줄\n/image\n마지막 줄', target: 'image', liveSlashLine: 1, selectedItems: [{ url: '/uploads/a.png' }] }) harness.actions.applyMediaSelection() assert.equal(harness.value.value, '첫 줄\n![](/uploads/a.png)\n마지막 줄') assert.equal(harness.liveSlashInsertLine.value, null) }) it('커서 업로드 삽입 후 라이브러리 탭이 열려 있으면 목록을 갱신한다', async () => { const harness = createMediaActionHarness({ target: 'image', uploadedItems: [{ url: '/uploads/a.png' }], isMediaPickerOpen: true, activeMediaPickerTab: 'library' }) await harness.actions.uploadAndInsert([{ name: 'a.png' }], 'image') assert.deepEqual(harness.calls.insertedBlocks, ['![](/uploads/a.png)']) assert.equal(harness.calls.fetched, 1) }) it('업로드 중이면 삽입과 모달 업로드를 실행하지 않는다', async () => { const harness = createMediaActionHarness({ isUploading: true, uploadedItems: [{ url: '/uploads/a.png' }], modalUploadedItems: [{ url: '/uploads/b.png' }] }) await harness.actions.uploadAndInsert([{ name: 'a.png' }], 'image') await harness.actions.uploadFromMediaModal([{ name: 'b.png' }]) assert.equal(harness.calls.insertedBlocks.length, 0) assert.equal(harness.calls.toasts.length, 0) }) it('미디어 모달 업로드 성공 시 완료 토스트를 표시한다', async () => { const harness = createMediaActionHarness({ modalUploadedItems: [{ url: '/uploads/a.png' }] }) await harness.actions.uploadFromMediaModal([{ name: 'a.png' }]) assert.deepEqual(harness.calls.toasts, [{ type: 'success', message: '업로드가 완료되어 파일이 선택되었습니다.' }]) }) })