返回 oh-my-ppt
persistence-capabilities.test.ts
根目录 / tests / unit / presentation / persistence-capabilities.test.ts
1 import fs from 'fs'
2 import os from 'os'
3 import path from 'path'
4 import { afterEach, describe, expect, it, vi } from 'vitest'
5
6 vi.mock('electron', () => ({
7 app: { getPath: () => '/tmp' },
8 BrowserWindow: class BrowserWindow {},
9 ipcMain: {},
10 session: {}
11 }))
12 vi.mock('@electron-toolkit/utils', () => ({ is: { dev: false } }))
13
14 import { requireSlideSizePreset } from '../../../src/shared/slide-size'
15 import {
16 PageWriteValidationError,
17 persistPageHtmlFromFragment,
18 verifyPresentationPageFiles
19 } from '../../../src/main/presentation/html/page-writer-core'
20 import {
21 persistIndexTransition,
22 verifyIndexShellFile
23 } from '../../../src/main/presentation/html/index-transition'
24 import { buildProjectIndexHtml } from '../../../src/main/session/template-builder'
25
26 const temporaryDirectories: string[] = []
27
28 const createTemporaryDirectory = async (): Promise<string> => {
29 const directory = await fs.promises.mkdtemp(path.join(os.tmpdir(), 'ohmyppt-presentation-'))
30 temporaryDirectories.push(directory)
31 return directory
32 }
33
34 afterEach(async () => {
35 await Promise.all(
36 temporaryDirectories.splice(0).map((directory) => fs.promises.rm(directory, { recursive: true }))
37 )
38 })
39
40 describe('presentation persistence capabilities', () => {
41 it('owns page validation, runtime injection, serialized persistence, and verification', async () => {
42 const projectDir = await createTemporaryDirectory()
43 const pagePath = path.join(projectDir, 'page-1.html')
44 const result = await persistPageHtmlFromFragment({
45 content: '<section><h1>Quarterly review</h1><p>Growth is on track.</p></section>',
46 pageId: 'page-1',
47 projectDir,
48 targetPath: pagePath,
49 slideSize: requireSlideSizePreset('wide-16-9')
50 })
51
52 expect(result.html).toContain('data-ppt-guard-root="1"')
53 await expect(fs.promises.readFile(pagePath, 'utf-8')).resolves.toBe(result.html)
54 await expect(
55 verifyPresentationPageFiles({ pageFileMap: { 'page-1': pagePath }, pageIds: ['page-1', 'missing'] })
56 ).resolves.toEqual([
57 {
58 pageId: 'page-1',
59 filled: true,
60 hasContent: true,
61 hasRemoteRuntime: false
62 },
63 {
64 pageId: 'missing',
65 filled: false,
66 hasContent: false,
67 hasRemoteRuntime: false
68 }
69 ])
70 })
71
72 it('keeps template skeleton validation and index persistence in presentation', async () => {
73 const projectDir = await createTemporaryDirectory()
74 const pagePath = path.join(projectDir, 'page-1.html')
75 const templateHtml = '<div class="bg-cover" style="background-image: url(./images/template.png)"></div>'
76 await fs.promises.writeFile(pagePath, templateHtml, 'utf-8')
77
78 await expect(
79 persistPageHtmlFromFragment({
80 content: '<section><h1>New content</h1></section>',
81 pageId: 'page-1',
82 projectDir,
83 targetPath: pagePath,
84 slideSize: requireSlideSizePreset('wide-16-9'),
85 preserveTemplateSkeleton: true
86 })
87 ).rejects.toMatchObject<PageWriteValidationError>({ kind: 'template-skeleton' })
88 await expect(fs.promises.readFile(pagePath, 'utf-8')).resolves.toBe(templateHtml)
89
90 const indexPath = path.join(projectDir, 'index.html')
91 await fs.promises.writeFile(
92 indexPath,
93 buildProjectIndexHtml(
94 'Review',
95 [{ pageNumber: 1, pageId: 'page-1', title: 'Overview', htmlPath: 'page-1.html' }],
96 requireSlideSizePreset('wide-16-9')
97 ),
98 'utf-8'
99 )
100 await expect(verifyIndexShellFile(indexPath)).resolves.toEqual({ status: 'valid' })
101 await expect(
102 persistIndexTransition({
103 indexPath,
104 projectDir,
105 input: { type: 'fade', durationMs: 420 }
106 })
107 ).resolves.toMatchObject({ status: 'updated', config: { type: 'fade', durationMs: 420 } })
108 await expect(fs.promises.readFile(indexPath, 'utf-8')).resolves.toContain(
109 '"durationMs":420'
110 )
111 })
112 })
113
113 lines TYPESCRIPT