返回 oh-my-ppt
page-beautify-fragment-replace.test.ts
根目录 / tests / unit / edit-jobs / page-beautify-fragment-replace.test.ts
1 import { describe, expect, it, vi } from 'vitest'
2
3 vi.mock('electron', () => ({ app: { getPath: () => '/tmp' }, session: { defaultSession: {} } }))
4 vi.mock('@electron-toolkit/utils', () => ({ is: { dev: false, main: true } }))
5
6 import { replacePageContentFragment } from '../../../src/main/presentation/html/page-writer-core'
7
8 const SHELL_HTML = `<!doctype html>
9 <html lang="zh-CN">
10 <head>
11 <meta charset="UTF-8" />
12 <title>Target</title>
13 <link rel="stylesheet" href="./assets/fonts/inter.css" />
14 <style id="ppt-page-guard-style">:root { --ppt-page-bg: #ffffff; }</style>
15 </head>
16 <body data-page-id="page-1">
17 <main class="ppt-page-root" data-ppt-guard-root="1" data-ppt-slide-size-id="wide-16-9" data-ppt-width="1600" data-ppt-height="900">
18 <div class="ppt-page-fit-scope">
19 <div class="ppt-page-content"><h1>Target</h1></div>
20 </div>
21 </main>
22 <script src="./assets/ppt-page-fit.js"></script>
23 <script>window.__ppt = { pageId: 'page-1' }</script>
24 </body>
25 </html>`
26
27 describe('replacePageContentFragment', () => {
28 it('swaps only .ppt-page-content and preserves the shell, head, fonts, and runtime scripts', () => {
29 const fragment = '<section class="grid"><h1>Target</h1><p>subtitle</p></section>'
30 const { html, content, repaired } = replacePageContentFragment({
31 originalHtml: SHELL_HTML,
32 content: fragment,
33 pageId: 'page-1'
34 })
35
36 expect(content).toBe(fragment)
37 expect(repaired).toBe(false)
38 // Head, fonts, CSS, runtime scripts are untouched.
39 expect(html).toContain('<title>Target</title>')
40 expect(html).toContain('./assets/fonts/inter.css')
41 expect(html).toContain('ppt-page-guard-style')
42 expect(html).toContain('./assets/ppt-page-fit.js')
43 expect(html).toContain("window.__ppt = { pageId: 'page-1' }")
44 // Shell attributes on .ppt-page-root are preserved.
45 expect(html).toContain('data-ppt-slide-size-id="wide-16-9"')
46 expect(html).toContain('data-ppt-width="1600"')
47 expect(html).toContain('data-ppt-height="900"')
48 // The original inner fragment is gone, the new one is in place (the
49 // normalizer wraps it in a scaffold and decorates block-level children).
50 expect(html).not.toContain('<div class="ppt-page-content"><h1>Target</h1></div>')
51 expect(html).toContain('<div class="ppt-page-content">')
52 expect(html).toContain('<section class="grid">')
53 expect(html).toContain('subtitle</p>')
54 })
55
56 it('rejects fragments that reference remote CDN resources', () => {
57 expect(() =>
58 replacePageContentFragment({
59 originalHtml: SHELL_HTML,
60 content: '<section><script src="https://cdn.example/lib.js"></script></section>',
61 pageId: 'page-1'
62 })
63 ).toThrow(/CDN\/远程资源引用/)
64 })
65
66 it('strips model-authored block ids before persisted validation', () => {
67 const { html } = replacePageContentFragment({
68 originalHtml: SHELL_HTML,
69 content: `
70 <section data-block-id="select-arcsin1-5kQfdkFj"><h1>Target</h1></section>
71 <section data-block-id="select-arcsin1-5kQfdkFj"><p>subtitle</p></section>
72 `,
73 pageId: 'page-1'
74 })
75
76 expect(html).not.toContain('data-block-id=')
77 })
78
79 it('throws when the original page no longer carries the ppt-page-content container', () => {
80 expect(() =>
81 replacePageContentFragment({
82 originalHtml: '<!doctype html><html><body><main class="something-else"></main></body></html>',
83 content: '<h1>Target</h1>',
84 pageId: 'page-1'
85 })
86 ).toThrow(/页面主体容器/)
87 })
88 })
89
89 lines TYPESCRIPT