Files
new-markdown/tests/image-display.test.ts
Muse b6acbf6f30 fix(image): 编辑区相对路径图片显示解析(docs/16)
粘贴/插入的图片落盘与相对引用本身没问题,但编辑区从未解析相对路径:
页面在应用源下按相对路径取图必 404、只剩裂图。新增 image NodeView,
仅显示层把本地图片按文档目录归一化后经 read_file_base64 读成 data URL;
文档内容不变,Web 模式不启用。vitest 83 通过 + 2 跳过。
2026-11-14 12:17:37 +08:00

73 lines
3.1 KiB
TypeScript
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
// 图片显示解析回归:文档里的相对路径图片在编辑区经 NodeView 读成 data URL 显示;
// 文档内容(序列化)保持相对路径不变。upload 插件以 instanceof 把关事件类型,
// jsdom 缺 ClipboardEvent/DragEvent 全局,这里垫上。
import { describe, it, expect } from 'vitest'
import { createMarkdownEditor, getMarkdown } from '../src/editor/editor'
import { editorViewCtx } from '@milkdown/kit/core'
class FakeClipboardEvent extends Event {
clipboardData: unknown
constructor(type: string, init?: { clipboardData?: unknown }) {
super(type, { bubbles: true, cancelable: true })
this.clipboardData = init?.clipboardData
}
}
;(globalThis as Record<string, unknown>).ClipboardEvent = FakeClipboardEvent
;(globalThis as Record<string, unknown>).DragEvent = class extends Event {}
const tick = (ms = 40) => new Promise((r) => setTimeout(r, ms))
describe('图片显示解析', () => {
it('相对路径图片在 DOM 里被换成 data URL,文档仍是相对路径', async () => {
const host = document.createElement('div')
document.body.appendChild(host)
const editor = createMarkdownEditor(host, '看图 ![截图](doc.assets/a.png) 完', {}, {
resolveImageDisplay: async (src) => (src === 'doc.assets/a.png' ? 'data:image/png;base64,QUJD' : null),
})
await editor.create()
await tick()
const img = host.querySelector('img:not(.ProseMirror-separator)')
expect(img?.getAttribute('src')).toBe('data:image/png;base64,QUJD')
expect(img?.getAttribute('alt')).toBe('截图')
expect(getMarkdown(editor)).toContain('![截图](doc.assets/a.png)')
await editor.destroy()
host.remove()
})
it('粘贴图片落盘为相对路径后同样能显示', async () => {
const host = document.createElement('div')
document.body.appendChild(host)
const editor = createMarkdownEditor(host, '正文', {}, {
onImageFile: async () => 'doc.assets/pasted.png',
resolveImageDisplay: async (src) => (src === 'doc.assets/pasted.png' ? 'data:image/png;base64,QUJD' : null),
})
await editor.create()
const view = editor.action((ctx) => ctx.get(editorViewCtx))
const file = new File(['png'], 'image.png', { type: 'image/png' })
const event = new FakeClipboardEvent('paste', {
clipboardData: { files: [file], items: [], getData: () => '' },
})
view.dom.dispatchEvent(event)
await tick(80)
expect(getMarkdown(editor)).toContain('doc.assets/pasted.png')
const img = host.querySelector('img:not(.ProseMirror-separator)')
expect(img?.getAttribute('src')).toBe('data:image/png;base64,QUJD')
await editor.destroy()
host.remove()
})
it('解析不到(文件缺失)时不崩溃、文档不变', async () => {
const host = document.createElement('div')
document.body.appendChild(host)
const editor = createMarkdownEditor(host, '![x](missing.png)', {}, {
resolveImageDisplay: async () => null,
})
await editor.create()
await tick()
expect(host.querySelector('img')).not.toBeNull()
expect(getMarkdown(editor)).toContain('![x](missing.png)')
await editor.destroy()
host.remove()
})
})