| 1 | // @vitest-environment happy-dom |
| 2 | |
| 3 | import { describe, expect, it, vi } from 'vitest' |
| 4 | |
| 5 | vi.mock('electron', () => ({ |
| 6 | app: { getPath: () => '/tmp' }, |
| 7 | BrowserWindow: class BrowserWindow {}, |
| 8 | ipcMain: {}, |
| 9 | session: {} |
| 10 | })) |
| 11 | |
| 12 | vi.mock('@electron-toolkit/utils', () => ({ is: { dev: false } })) |
| 13 | |
| 14 | import { buildBasePageStyleTag, buildFitScript } from '../../../src/main/presentation/html/page-writer-core' |
| 15 | import { requireSlideSizePreset, resolveSlideSize } from '../../../src/shared/slide-size' |
| 16 | |
| 17 | describe('canvas-aware font floors', () => { |
| 18 | // Root font-size is NOT scaled — we keep the browser 16px default so rem-based |
| 19 | // Tailwind utilities (gap-4, p-8, ...) don't blow up layout on tall canvases. |
| 20 | // Instead, the fit-script font floors scale with canvas height, and the layout |
| 21 | // skills / design contract prompt tell the model to write larger explicit px |
| 22 | // font-sizes on taller canvases. |
| 23 | |
| 24 | it('keeps the root and content font-size at the 16px baseline on every canvas', () => { |
| 25 | const portrait = resolveSlideSize({ id: 'vertical-9-16' }) // 900x1600 |
| 26 | const css = buildBasePageStyleTag(portrait) |
| 27 | |
| 28 | // No canvas-height-aware calc on root; .ppt-page-content stays at 16px so |
| 29 | // rem-based Tailwind spacing utilities are not side-effect-amplified. |
| 30 | expect(css).not.toMatch(/font-size:\s*calc\(16px \* var\(--ppt-slide-height\)/) |
| 31 | expect(css).toMatch(/\.ppt-page-content\s*{[^}]*font-size:\s*16px/) |
| 32 | expect(css).toMatch(/\[data-ppt-readable-fonts="1"\]\s*{[^}]*font-size:\s*18px/) |
| 33 | }) |
| 34 | |
| 35 | it('scales fit-script font floors with canvas height (1600h portrait)', () => { |
| 36 | const portrait = resolveSlideSize({ id: 'vertical-9-16' }) // height 1600 -> scale ~1.778 |
| 37 | const script = buildFitScript(portrait) |
| 38 | |
| 39 | // 1600/900 = 1.777...; Math.round(18 * 1.777) = 32 |
| 40 | expect(script).toContain('const BODY_MIN_FONT = 32;') |
| 41 | // Math.round(24 * 1.777) = 43 |
| 42 | expect(script).toContain('const HEADING_MIN_FONT = 43;') |
| 43 | // Math.round(12 * 1.777) = 21 |
| 44 | expect(script).toContain('const AUXILIARY_MIN_FONT = 21;') |
| 45 | // Math.round(14 * 1.777) = 25 |
| 46 | expect(script).toContain('const LEGACY_MIN_FONT = 25;') |
| 47 | }) |
| 48 | |
| 49 | it('keeps the original 18/24/12/14 floors on the 900h reference canvas', () => { |
| 50 | const wide = requireSlideSizePreset('wide-16-9') // height 900 -> scale 1 |
| 51 | const script = buildFitScript(wide) |
| 52 | |
| 53 | expect(script).toContain('const BODY_MIN_FONT = 18;') |
| 54 | expect(script).toContain('const HEADING_MIN_FONT = 24;') |
| 55 | expect(script).toContain('const AUXILIARY_MIN_FONT = 12;') |
| 56 | expect(script).toContain('const LEGACY_MIN_FONT = 14;') |
| 57 | }) |
| 58 | |
| 59 | it('uses an intermediate scale for 1200h canvases (4:3 / 1:1)', () => { |
| 60 | const square = resolveSlideSize({ id: 'square-1-1' }) // 1200x1200 |
| 61 | const script = buildFitScript(square) |
| 62 | |
| 63 | // 1200/900 = 1.333; Math.round(18 * 1.333) = 24, Math.round(24 * 1.333) = 32 |
| 64 | expect(script).toContain('const BODY_MIN_FONT = 24;') |
| 65 | expect(script).toContain('const HEADING_MIN_FONT = 32;') |
| 66 | expect(script).toContain('const AUXILIARY_MIN_FONT = 16;') |
| 67 | expect(script).toContain('const LEGACY_MIN_FONT = 19;') |
| 68 | }) |
| 69 | |
| 70 | it('uses the largest scale for 1660h xiaohongshu canvas', () => { |
| 71 | const note = resolveSlideSize({ id: 'xiaohongshu-note' }) // 1242x1660 |
| 72 | const script = buildFitScript(note) |
| 73 | |
| 74 | // 1660/900 = 1.844; Math.round(18 * 1.844) = 33, Math.round(24 * 1.844) = 44 |
| 75 | expect(script).toContain('const BODY_MIN_FONT = 33;') |
| 76 | expect(script).toContain('const HEADING_MIN_FONT = 44;') |
| 77 | expect(script).toContain('const AUXILIARY_MIN_FONT = 22;') |
| 78 | expect(script).toContain('const LEGACY_MIN_FONT = 26;') |
| 79 | }) |
| 80 | }) |
| 81 |