import { afterEach, describe, expect, it, vi } from 'vitest'
import { parseHTML } from 'linkedom'
import { ref } from 'vue'
import { useViewerRenderSurface } from '../packages/components/vue3/src/package/components/FileViewer/hooks/useViewerRenderSurface'
import type { FileRenderExportAdapter } from '../packages/components/vue3/src/package/common/type'
const createVueRenderSessionMock = vi.hoisted(() => vi.fn())
vi.mock('../packages/components/vue3/src/package/components/FileViewer/rendererBridge', () => ({
createVueRenderSession: createVueRenderSessionMock
}))
describe('Vue FileViewer render surface hook', () => {
afterEach(() => {
createVueRenderSessionMock.mockReset()
vi.unstubAllGlobals()
})
const installDom = () => {
const { document, window } = parseHTML('
old
')
vi.stubGlobal('window', window)
vi.stubGlobal('document', document)
return document.getElementById('root') as HTMLDivElement
}
it('mounts rendered content, wires export adapters and clears old DOM through one surface facade', async () => {
const root = installDom()
const output = ref(root)
const session = { destroy: vi.fn() }
const adapter: FileRenderExportAdapter = { exportHtml: true }
const refreshDocumentIndex = vi.fn()
const refreshZoomProvider = vi.fn()
const stopZoomObserver = vi.fn()
const clearZoomProvider = vi.fn()
const clearDocumentState = vi.fn()
createVueRenderSessionMock.mockImplementation(async (_buffer, _type, target, context) => {
context.registerExportAdapter(adapter)
context.onProgressiveRender()
target.appendChild(document.createElement('strong'))
return session
})
const surface = useViewerRenderSurface({
output,
getOptions: () => ({ theme: 'light' }),
isCurrentRequest: version => version === 1,
notifyActiveUnloadStart: vi.fn(() => null),
notifyActiveUnloadComplete: vi.fn(),
clearActiveDocumentContext: vi.fn(),
clearDocumentState,
refreshDocumentIndex,
startZoomObserver: vi.fn(),
stopZoomObserver,
clearZoomProvider,
refreshZoomProvider
})
const renderedSession = await surface.mountRenderedContent(
new ArrayBuffer(4),
new File(['demo'], 'demo.pdf'),
1,
'/example/demo.pdf'
)
expect(renderedSession).toBe(session)
expect(createVueRenderSessionMock).toHaveBeenCalledWith(
expect.any(ArrayBuffer),
'pdf',
expect.objectContaining({ className: 'file-render' }),
expect.objectContaining({
filename: 'demo.pdf',
url: '/example/demo.pdf',
options: { theme: 'light' }
})
)
expect(root.innerHTML).not.toContain('old')
expect(root.innerHTML).toContain('file-render')
expect(root.innerHTML).toContain('strong')
expect(surface.activeExportAdapter.value).toBe(adapter)
expect(surface.progressiveReady.value).toBe(true)
expect(refreshDocumentIndex).toHaveBeenCalledTimes(1)
expect(refreshZoomProvider).toHaveBeenCalledTimes(1)
surface.renderedReady.value = true
surface.setActiveRenderSession(session)
surface.clearRenderedContent('replace')
expect(session.destroy).toHaveBeenCalledTimes(1)
expect(surface.activeExportAdapter.value).toBeNull()
expect(surface.renderedReady.value).toBe(false)
expect(surface.progressiveReady.value).toBe(false)
expect(clearDocumentState).toHaveBeenCalled()
expect(stopZoomObserver).toHaveBeenCalled()
expect(clearZoomProvider).toHaveBeenCalled()
expect(root.childElementCount).toBe(0)
})
it('disposes stale render sessions and removes stale DOM before returning', async () => {
const root = installDom()
const output = ref(root)
const session = { destroy: vi.fn() }
let currentVersion = 1
createVueRenderSessionMock.mockImplementation(async () => {
currentVersion = 2
return session
})
const surface = useViewerRenderSurface({
output,
getOptions: () => undefined,
isCurrentRequest: version => version === currentVersion,
notifyActiveUnloadStart: vi.fn(() => null),
notifyActiveUnloadComplete: vi.fn(),
clearActiveDocumentContext: vi.fn(),
clearDocumentState: vi.fn(),
refreshDocumentIndex: vi.fn(),
startZoomObserver: vi.fn(),
stopZoomObserver: vi.fn(),
clearZoomProvider: vi.fn(),
refreshZoomProvider: vi.fn()
})
await expect(surface.mountRenderedContent(
new ArrayBuffer(4),
new File(['demo'], 'demo.pdf'),
1
)).resolves.toBeUndefined()
expect(session.destroy).toHaveBeenCalledTimes(1)
expect(root.innerHTML).not.toContain('file-render')
})
it('routes render session dispose warnings through the core report helper', () => {
const root = installDom()
const output = ref(root)
const error = new Error('dispose failed')
const session = {
destroy: vi.fn(() => {
throw error
})
}
const warn = vi.spyOn(console, 'warn').mockImplementation(() => undefined)
const surface = useViewerRenderSurface({
output,
getOptions: () => undefined,
isCurrentRequest: version => version === 1,
notifyActiveUnloadStart: vi.fn(() => null),
notifyActiveUnloadComplete: vi.fn(),
clearActiveDocumentContext: vi.fn(),
clearDocumentState: vi.fn(),
refreshDocumentIndex: vi.fn(),
startZoomObserver: vi.fn(),
stopZoomObserver: vi.fn(),
clearZoomProvider: vi.fn(),
refreshZoomProvider: vi.fn()
})
try {
surface.destroyRenderSession(session)
expect(session.destroy).toHaveBeenCalledTimes(1)
expect(warn).toHaveBeenCalledWith('预览内容卸载失败', error)
} finally {
warn.mockRestore()
}
})
})