粘贴/插入的图片落盘与相对引用本身没问题,但编辑区从未解析相对路径: 页面在应用源下按相对路径取图必 404、只剩裂图。新增 image NodeView, 仅显示层把本地图片按文档目录归一化后经 read_file_base64 读成 data URL; 文档内容不变,Web 模式不启用。vitest 83 通过 + 2 跳过。
73 lines
3.1 KiB
TypeScript
73 lines
3.1 KiB
TypeScript
// 图片显示解析回归:文档里的相对路径图片在编辑区经 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, '看图  完', {}, {
|
||
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('')
|
||
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, '', {}, {
|
||
resolveImageDisplay: async () => null,
|
||
})
|
||
await editor.create()
|
||
await tick()
|
||
expect(host.querySelector('img')).not.toBeNull()
|
||
expect(getMarkdown(editor)).toContain('')
|
||
await editor.destroy()
|
||
host.remove()
|
||
})
|
||
})
|