返回 oh-my-ppt
html-editor-open-document.test.ts
根目录 / tests / unit / html-editor / html-editor-open-document.test.ts
1 import fs from 'node:fs'
2 import os from 'node:os'
3 import path from 'node:path'
4 import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest'
5
6 const state = vi.hoisted(() => {
7 const handlers = new Map<string, (event: unknown, payload: unknown) => Promise<unknown>>()
8 return {
9 handlers,
10 ipcMain: {
11 handle: vi.fn(
12 (channel: string, handler: (event: unknown, payload: unknown) => Promise<unknown>) => {
13 handlers.set(channel, handler)
14 }
15 )
16 },
17 log: { error: vi.fn(), info: vi.fn(), warn: vi.fn() },
18 normalizeImportedHtml: vi.fn(({ html }: { html: string }) => ({
19 html,
20 designWidth: 1280,
21 title: ''
22 }))
23 }
24 })
25
26 vi.mock('electron', () => ({
27 app: { isPackaged: false },
28 BrowserWindow: { fromWebContents: vi.fn(), getFocusedWindow: vi.fn() },
29 dialog: { showOpenDialog: vi.fn(), showSaveDialog: vi.fn() },
30 ipcMain: state.ipcMain,
31 shell: { openPath: vi.fn(), showItemInFolder: vi.fn() }
32 }))
33 vi.mock('electron-log/main.js', () => ({ default: state.log }))
34 vi.mock('../../../src/main/html-editor/html-editor-import', () => ({
35 normalizeImportedHtml: state.normalizeImportedHtml
36 }))
37 vi.mock('../../../src/main/html-editor/html-editor-git', () => ({
38 commitHtmlFile: vi.fn(),
39 ensureHtmlRepo: vi.fn(),
40 getHtmlRepoHead: vi.fn(),
41 readHtmlAtCommit: vi.fn(),
42 restoreHtmlFileAtCommit: vi.fn(),
43 restoreHtmlRepoHead: vi.fn()
44 }))
45 vi.mock('../../../src/main/html-editor/html-editor-thumbnail', () => ({
46 refreshHtmlEditorCoverThumbnail: vi.fn(),
47 warmHtmlEditorCoverThumbnails: vi.fn()
48 }))
49 vi.mock('../../../src/main/html-editor/html-editor-media', () => ({
50 getHtmlEditorMediaExtensions: vi.fn(),
51 importHtmlEditorMedia: vi.fn(),
52 listHtmlEditorMedia: vi.fn()
53 }))
54
55 describe('html-editor:openDocument', () => {
56 let storagePath = ''
57
58 beforeEach(async () => {
59 vi.resetModules()
60 state.handlers.clear()
61 state.ipcMain.handle.mockClear()
62 state.normalizeImportedHtml.mockClear()
63 state.normalizeImportedHtml.mockImplementation(({ html }: { html: string }) => ({
64 html,
65 designWidth: 1280,
66 title: ''
67 }))
68 storagePath = await fs.promises.mkdtemp(path.join(os.tmpdir(), 'html-editor-open-'))
69 })
70
71 afterEach(async () => {
72 vi.restoreAllMocks()
73 if (storagePath) await fs.promises.rm(storagePath, { recursive: true, force: true })
74 })
75
76 it('reuses an unchanged disk version and revalidates after the file changes', async () => {
77 const docId = 'doc-1'
78 const workspaceDir = path.join(storagePath, 'html-editor', docId)
79 const htmlPath = path.join(workspaceDir, 'current.html')
80 await fs.promises.mkdir(workspaceDir, { recursive: true })
81 await fs.promises.writeFile(htmlPath, '<html><body>first</body></html>', 'utf-8')
82
83 const { registerHtmlEditorHandlers } =
84 await import('../../../src/main/html-editor/html-editor-handlers')
85 registerHtmlEditorHandlers({
86 mainWindow: null,
87 db: {
88 getHtmlEditDocument: vi.fn().mockResolvedValue({
89 id: docId,
90 title: 'Document',
91 sourcePath: null,
92 htmlPath,
93 designWidth: 1280
94 })
95 },
96 resolveStoragePath: vi.fn().mockResolvedValue(storagePath)
97 } as never)
98
99 const handler = state.handlers.get('html-editor:openDocument')
100 expect(handler).toBeDefined()
101 const readFileSpy = vi.spyOn(fs.promises, 'readFile')
102
103 await handler!({}, { docId })
104 await handler!({}, { docId })
105
106 expect(state.normalizeImportedHtml).toHaveBeenCalledTimes(1)
107 expect(readFileSpy.mock.calls.filter(([filePath]) => filePath === htmlPath)).toHaveLength(1)
108
109 await fs.promises.writeFile(htmlPath, '<html><body>second version</body></html>', 'utf-8')
110 await handler!({}, { docId })
111
112 expect(state.normalizeImportedHtml).toHaveBeenCalledTimes(2)
113 expect(readFileSpy.mock.calls.filter(([filePath]) => filePath === htmlPath)).toHaveLength(2)
114 })
115 })
116
116 lines TYPESCRIPT