| 1 | import { describe, expect, it } from 'vitest' |
| 2 | import { buildPageBeautifySystemPrompt } from '../../../src/main/edit-jobs/page-beautify-prompt' |
| 3 | import { resolveLayoutSkillName } from '../../../src/main/product-skills/contract' |
| 4 | |
| 5 | const baseArgs = { |
| 6 | provider: 'provider', |
| 7 | apiKey: 'key', |
| 8 | model: 'model', |
| 9 | baseUrl: 'https://example.com', |
| 10 | maxTokens: 1000, |
| 11 | modelTimeoutMs: { agent: 1000 }, |
| 12 | signal: new AbortController().signal, |
| 13 | styleKey: 'editorial', |
| 14 | styleName: 'Editorial', |
| 15 | styleSkillPrompt: 'Use a clear editorial hierarchy.', |
| 16 | styleCase: '<section class="hero"><h1>Title</h1></section>', |
| 17 | slideSize: { id: 'wide-16-9', width: 1600, height: 900, label: '宽屏 16:9' }, |
| 18 | targetPageId: 'page-2', |
| 19 | targetPageNumber: 2, |
| 20 | targetHtmlPath: '/tmp/page-2.html' |
| 21 | } |
| 22 | |
| 23 | describe('page beautify prompt', () => { |
| 24 | it('limits the agent to the current page and visual contracts', () => { |
| 25 | const prompt = buildPageBeautifySystemPrompt({ |
| 26 | ...baseArgs, |
| 27 | layoutSkillName: resolveLayoutSkillName(baseArgs.slideSize) |
| 28 | }) |
| 29 | |
| 30 | expect(prompt).toContain('exactly one editable region') |
| 31 | expect(prompt).toContain('Target: page-2 (slide 2)') |
| 32 | expect(prompt).toContain('Use a clear editorial hierarchy.') |
| 33 | expect(prompt).toContain('Preserve the visible wording and reading order') |
| 34 | expect(prompt).toContain('when the page is too dense to remain legible') |
| 35 | expect(prompt).toContain('every number, date, unit, table value, chart value') |
| 36 | expect(prompt).toContain('Overflow recovery (required)') |
| 37 | expect(prompt).toContain('material re-composition') |
| 38 | expect(prompt).toContain('Never solve overflow by adding overflow-hidden') |
| 39 | expect(prompt).toContain('read_page_html') |
| 40 | expect(prompt).toContain('save_current_page_content') |
| 41 | expect(prompt).toContain('Read/write asymmetry') |
| 42 | expect(prompt).toContain('Style case (visual reference)') |
| 43 | expect(prompt).toContain('class="hero"') |
| 44 | expect(prompt).toContain('ppt-page-root') |
| 45 | expect(prompt).toContain('Do not add data-block-id attributes') |
| 46 | expect(prompt).not.toContain('update_single_page_file') |
| 47 | expect(prompt).not.toContain('get_session_context') |
| 48 | expect(prompt).not.toContain('/docs/source.pdf') |
| 49 | expect(prompt).not.toContain('read_current_page_content') |
| 50 | }) |
| 51 | |
| 52 | it('directs the model to read the slide-size layout skill via read_file before re-layouting', () => { |
| 53 | const prompt = buildPageBeautifySystemPrompt({ |
| 54 | ...baseArgs, |
| 55 | slideSize: { id: 'wide-16-9', width: 1600, height: 900, label: '16:9' }, |
| 56 | layoutSkillName: resolveLayoutSkillName({ id: 'wide-16-9', width: 1600, height: 900, label: '16:9' }) |
| 57 | }) |
| 58 | |
| 59 | expect(prompt).toContain('oh-my-ppt-layout') |
| 60 | expect(prompt).toMatch(/read_file/) |
| 61 | expect(prompt).toMatch(/references\/catalog\.md/) |
| 62 | expect(prompt).toMatch(/Re-layout is the goal/) |
| 63 | expect(prompt).toContain('creative version upgrade within the selected style') |
| 64 | expect(prompt).toContain('selected style is a hard visual guardrail') |
| 65 | expect(prompt).toContain('only changes wording, number formatting, comments, animations') |
| 66 | expect(prompt).toContain('Review the finished layout before saving') |
| 67 | expect(prompt).toContain('chart configuration, and data-bearing attributes') |
| 68 | expect(prompt).toContain('fixed 16:9 canvas: 1600px wide x 900px high') |
| 69 | expect(prompt).toContain('x=0..1599, y=0..899') |
| 70 | expect(prompt).toContain('overflow:hidden') |
| 71 | }) |
| 72 | |
| 73 | it('names the size-specific layout skill for non-16:9 canvases', () => { |
| 74 | const slideSize = { id: 'vertical-9-16', width: 1080, height: 1920, label: '9:16' } |
| 75 | const prompt = buildPageBeautifySystemPrompt({ |
| 76 | ...baseArgs, |
| 77 | slideSize, |
| 78 | layoutSkillName: resolveLayoutSkillName(slideSize) |
| 79 | }) |
| 80 | |
| 81 | expect(prompt).toContain('vertical-9-16-layout-skill') |
| 82 | expect(prompt).toContain('fixed 9:16 canvas: 1080px wide x 1920px high') |
| 83 | }) |
| 84 | |
| 85 | it('gives the model a browser-measured layout audit when the preview supplies one', () => { |
| 86 | const prompt = buildPageBeautifySystemPrompt({ |
| 87 | ...baseArgs, |
| 88 | layoutSkillName: resolveLayoutSkillName(baseArgs.slideSize), |
| 89 | layoutAudit: |
| 90 | 'Canvas: 1242px x 1660px.\nMeasured defects:\n- [text-overflow] <p>: text needs 86px more width' |
| 91 | }) |
| 92 | |
| 93 | expect(prompt).toContain('Current rendered layout audit') |
| 94 | expect(prompt).toContain('measured this from the already-rendered current page') |
| 95 | expect(prompt).toContain('text needs 86px more width') |
| 96 | }) |
| 97 | }) |
| 98 |