vue-render-surface-hook.spec.ts 5.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170
  1. import { afterEach, describe, expect, it, vi } from 'vitest'
  2. import { parseHTML } from 'linkedom'
  3. import { ref } from 'vue'
  4. import { useViewerRenderSurface } from '../packages/components/vue3/src/package/components/FileViewer/hooks/useViewerRenderSurface'
  5. import type { FileRenderExportAdapter } from '../packages/components/vue3/src/package/common/type'
  6. const createVueRenderSessionMock = vi.hoisted(() => vi.fn())
  7. vi.mock('../packages/components/vue3/src/package/components/FileViewer/rendererBridge', () => ({
  8. createVueRenderSession: createVueRenderSessionMock
  9. }))
  10. describe('Vue FileViewer render surface hook', () => {
  11. afterEach(() => {
  12. createVueRenderSessionMock.mockReset()
  13. vi.unstubAllGlobals()
  14. })
  15. const installDom = () => {
  16. const { document, window } = parseHTML('<div id="root"><span class="old">old</span></div>')
  17. vi.stubGlobal('window', window)
  18. vi.stubGlobal('document', document)
  19. return document.getElementById('root') as HTMLDivElement
  20. }
  21. it('mounts rendered content, wires export adapters and clears old DOM through one surface facade', async () => {
  22. const root = installDom()
  23. const output = ref<HTMLDivElement | null>(root)
  24. const session = { destroy: vi.fn() }
  25. const adapter: FileRenderExportAdapter = { exportHtml: true }
  26. const refreshDocumentIndex = vi.fn()
  27. const refreshZoomProvider = vi.fn()
  28. const stopZoomObserver = vi.fn()
  29. const clearZoomProvider = vi.fn()
  30. const clearDocumentState = vi.fn()
  31. createVueRenderSessionMock.mockImplementation(async (_buffer, _type, target, context) => {
  32. context.registerExportAdapter(adapter)
  33. context.onProgressiveRender()
  34. target.appendChild(document.createElement('strong'))
  35. return session
  36. })
  37. const surface = useViewerRenderSurface({
  38. output,
  39. getOptions: () => ({ theme: 'light' }),
  40. isCurrentRequest: version => version === 1,
  41. notifyActiveUnloadStart: vi.fn(() => null),
  42. notifyActiveUnloadComplete: vi.fn(),
  43. clearActiveDocumentContext: vi.fn(),
  44. clearDocumentState,
  45. refreshDocumentIndex,
  46. startZoomObserver: vi.fn(),
  47. stopZoomObserver,
  48. clearZoomProvider,
  49. refreshZoomProvider
  50. })
  51. const renderedSession = await surface.mountRenderedContent(
  52. new ArrayBuffer(4),
  53. new File(['demo'], 'demo.pdf'),
  54. 1,
  55. '/example/demo.pdf'
  56. )
  57. expect(renderedSession).toBe(session)
  58. expect(createVueRenderSessionMock).toHaveBeenCalledWith(
  59. expect.any(ArrayBuffer),
  60. 'pdf',
  61. expect.objectContaining({ className: 'file-render' }),
  62. expect.objectContaining({
  63. filename: 'demo.pdf',
  64. url: '/example/demo.pdf',
  65. options: { theme: 'light' }
  66. })
  67. )
  68. expect(root.innerHTML).not.toContain('old')
  69. expect(root.innerHTML).toContain('file-render')
  70. expect(root.innerHTML).toContain('strong')
  71. expect(surface.activeExportAdapter.value).toBe(adapter)
  72. expect(surface.progressiveReady.value).toBe(true)
  73. expect(refreshDocumentIndex).toHaveBeenCalledTimes(1)
  74. expect(refreshZoomProvider).toHaveBeenCalledTimes(1)
  75. surface.renderedReady.value = true
  76. surface.setActiveRenderSession(session)
  77. surface.clearRenderedContent('replace')
  78. expect(session.destroy).toHaveBeenCalledTimes(1)
  79. expect(surface.activeExportAdapter.value).toBeNull()
  80. expect(surface.renderedReady.value).toBe(false)
  81. expect(surface.progressiveReady.value).toBe(false)
  82. expect(clearDocumentState).toHaveBeenCalled()
  83. expect(stopZoomObserver).toHaveBeenCalled()
  84. expect(clearZoomProvider).toHaveBeenCalled()
  85. expect(root.childElementCount).toBe(0)
  86. })
  87. it('disposes stale render sessions and removes stale DOM before returning', async () => {
  88. const root = installDom()
  89. const output = ref<HTMLDivElement | null>(root)
  90. const session = { destroy: vi.fn() }
  91. let currentVersion = 1
  92. createVueRenderSessionMock.mockImplementation(async () => {
  93. currentVersion = 2
  94. return session
  95. })
  96. const surface = useViewerRenderSurface({
  97. output,
  98. getOptions: () => undefined,
  99. isCurrentRequest: version => version === currentVersion,
  100. notifyActiveUnloadStart: vi.fn(() => null),
  101. notifyActiveUnloadComplete: vi.fn(),
  102. clearActiveDocumentContext: vi.fn(),
  103. clearDocumentState: vi.fn(),
  104. refreshDocumentIndex: vi.fn(),
  105. startZoomObserver: vi.fn(),
  106. stopZoomObserver: vi.fn(),
  107. clearZoomProvider: vi.fn(),
  108. refreshZoomProvider: vi.fn()
  109. })
  110. await expect(surface.mountRenderedContent(
  111. new ArrayBuffer(4),
  112. new File(['demo'], 'demo.pdf'),
  113. 1
  114. )).resolves.toBeUndefined()
  115. expect(session.destroy).toHaveBeenCalledTimes(1)
  116. expect(root.innerHTML).not.toContain('file-render')
  117. })
  118. it('routes render session dispose warnings through the core report helper', () => {
  119. const root = installDom()
  120. const output = ref<HTMLDivElement | null>(root)
  121. const error = new Error('dispose failed')
  122. const session = {
  123. destroy: vi.fn(() => {
  124. throw error
  125. })
  126. }
  127. const warn = vi.spyOn(console, 'warn').mockImplementation(() => undefined)
  128. const surface = useViewerRenderSurface({
  129. output,
  130. getOptions: () => undefined,
  131. isCurrentRequest: version => version === 1,
  132. notifyActiveUnloadStart: vi.fn(() => null),
  133. notifyActiveUnloadComplete: vi.fn(),
  134. clearActiveDocumentContext: vi.fn(),
  135. clearDocumentState: vi.fn(),
  136. refreshDocumentIndex: vi.fn(),
  137. startZoomObserver: vi.fn(),
  138. stopZoomObserver: vi.fn(),
  139. clearZoomProvider: vi.fn(),
  140. refreshZoomProvider: vi.fn()
  141. })
  142. try {
  143. surface.destroyRenderSession(session)
  144. expect(session.destroy).toHaveBeenCalledTimes(1)
  145. expect(warn).toHaveBeenCalledWith('预览内容卸载失败', error)
  146. } finally {
  147. warn.mockRestore()
  148. }
  149. })
  150. })