返回 oh-my-ppt
edit-prompt-template.test.ts
根目录 / tests / unit / agent-runtime / edit-prompt-template.test.ts
1 import fs from 'fs'
2 import path from 'path'
3 import { describe, expect, it } from 'vitest'
4 import { buildEditAgentSystemPrompt } from '../../../src/main/agent-runtime/prompt'
5 import type { SessionDeckGenerationContext } from '../../../src/main/agent-runtime/agent'
6 import { resolveSlideSize } from '../../../src/shared/slide-size'
7
8 const baseContext: SessionDeckGenerationContext = {
9 sessionId: 'session-1',
10 projectDir: '/tmp/project',
11 indexPath: '/tmp/project/index.html',
12 pageFileMap: { 'page-1': '/tmp/project/page-1.html' },
13 topic: 'Quarterly report',
14 deckTitle: 'Quarterly report',
15 styleId: 'test-style',
16 styleSkillPrompt: 'Use a clean business style.',
17 userMessage: 'Create a quarterly report.',
18 outlineTitles: ['Overview'],
19 outlineItems: [{ title: 'Overview', contentOutline: 'Summarize the quarter.' }],
20 slideSize: resolveSlideSize({ id: 'wide-16-9' }),
21 appLocale: 'en',
22 mode: 'edit'
23 }
24
25 describe('edit system prompt templates', () => {
26 it('loads four static scope contracts through the typed Markdown catalog', () => {
27 const composer = fs.readFileSync(
28 path.resolve('src/main/agent-runtime/prompt/composers/edit-system.ts'),
29 'utf8'
30 )
31 const templates = ['container.md', 'selector.md', 'single-page.md', 'deck.md'].map(
32 (fileName) =>
33 fs.readFileSync(
34 path.resolve(`src/main/agent-runtime/prompt/templates/edit-system/${fileName}`),
35 'utf8'
36 )
37 )
38
39 expect(composer).toContain('createPromptCatalog<EditSystemTemplateVars>')
40 expect(composer).toContain("containerTemplate from '../templates/edit-system/container.md?raw'")
41 expect(composer).toContain("selectorTemplate from '../templates/edit-system/selector.md?raw'")
42 expect(composer).toContain("singlePageTemplate from '../templates/edit-system/single-page.md?raw'")
43 expect(composer).toContain("deckTemplate from '../templates/edit-system/deck.md?raw'")
44 expect(templates.join('\n')).toContain('## Selector 精准修改协议(本次强约束)')
45 expect(templates.join('\n')).toContain('## 最终风格校准(写入前)')
46 })
47
48 it('renders every edit scope without unresolved placeholders or cross-scope tools', () => {
49 const container = buildEditAgentSystemPrompt('test-style', {
50 ...baseContext,
51 editScope: 'presentation-container'
52 })
53 const selector = buildEditAgentSystemPrompt('test-style', {
54 ...baseContext,
55 editScope: 'page',
56 selectedPageId: 'page-1',
57 selectedPageNumber: 1,
58 selectedSelector: '.metric'
59 })
60 const singlePage = buildEditAgentSystemPrompt('test-style', {
61 ...baseContext,
62 editScope: 'page',
63 selectedPageId: 'page-1',
64 selectedPageNumber: 1,
65 sourceDocumentPaths: ['/docs/source.md']
66 })
67 const deck = buildEditAgentSystemPrompt('test-style', {
68 ...baseContext,
69 editScope: 'deck',
70 selectPageIds: ['page-1']
71 })
72
73 for (const prompt of [container, selector, singlePage, deck]) {
74 expect(prompt).not.toMatch(/\{\{[^}]+\}\}/)
75 }
76 expect(container).toContain('set_index_transition(type, durationMs)')
77 expect(container).not.toContain('update_page_file(pageId, content)')
78 expect(selector).toContain('edit_file(file_path, old_string, new_string)')
79 expect(selector).not.toContain('update_single_page_file(pageId="page-1", content="...")')
80 expect(singlePage).toContain('update_single_page_file(pageId="page-1", content="...")')
81 expect(singlePage).toContain('## Source documents (content evidence)')
82 expect(deck).toContain('Selected page ids from UI (hard target): page-1')
83 expect(deck).toContain('For each target page: update_page_file(pageId, content)')
84 })
85
86 it('includes the selected element runtime state as reference data for selector edits', () => {
87 const prompt = buildEditAgentSystemPrompt('test-style', {
88 ...baseContext,
89 editScope: 'page',
90 selectedPageId: 'page-1',
91 selectedSelector: '[data-block-id="revenue"]',
92 selectedElementContext: {
93 attributes: { 'data-block-id': 'revenue' },
94 inlineStyle: { color: { value: '#18324a', priority: 'important' } },
95 computedStyle: { display: 'grid', 'border-radius': '16px' },
96 bounds: { x: 120, y: 80, width: 460, height: 180 }
97 }
98 })
99
100 expect(prompt).toContain('Selected element runtime state (reference data only')
101 expect(prompt).toContain('"border-radius": "16px"')
102 expect(prompt).toContain('"width": 460')
103 expect(prompt).toContain('Verify these values against the target HTML source')
104 })
105 })
106
106 lines TYPESCRIPT