From 50c63693483ba5b4b0d10794d8c68a1a9ddae9e0 Mon Sep 17 00:00:00 2001 From: Amit Haridas Date: Thu, 23 Jul 2026 00:24:41 +0530 Subject: [PATCH] test(e2e): verify monospace font embeds into PDF/DOCX/EPUB/HTML --- tests/smoke-e2e-monospace.test.js | 98 +++++++++++++++++++++++++++++++ 1 file changed, 98 insertions(+) create mode 100644 tests/smoke-e2e-monospace.test.js diff --git a/tests/smoke-e2e-monospace.test.js b/tests/smoke-e2e-monospace.test.js new file mode 100644 index 0000000..ff7fdc2 --- /dev/null +++ b/tests/smoke-e2e-monospace.test.js @@ -0,0 +1,98 @@ +/** + * E2E smoke for monospace embedding. Runs against the vendored fonts when + * available. If pandoc is missing, the PDF-specific test is skipped; the + * DOCX/EPUB/HTML tests exercise the JS modules directly and run either way. + */ +const { execFileSync } = require('child_process'); +const fs = require('fs'); +const path = require('path'); +const os = require('os'); + +function ttfAvailable() { + const ttf = path.resolve(__dirname, '..', 'assets', 'fonts', 'JetBrainsMono-Regular.ttf'); + return fs.existsSync(ttf); +} + +function pandocAvailable() { + try { + execFileSync('pandoc', ['--version'], { stdio: 'ignore' }); + return true; + } catch { + return false; + } +} + +describe('monospace E2E', () => { + let tmp; + + beforeAll(() => { + if (ttfAvailable()) { + tmp = fs.mkdtempSync(path.join(os.tmpdir(), 'mono-e2e-')); + } + }); + + afterAll(() => { + if (tmp) fs.rmSync(tmp, { recursive: true, force: true }); + }); + + it('DOCX export embeds the TTF', async () => { + if (!ttfAvailable()) return; + const { embedDocxFont } = require('../src/main/DocxFontEmbedder'); + const { getMonoFontTtfPath } = require('../src/main/MonospaceFontConfig'); + const JSZip = require('jszip'); + const zip = new JSZip(); + zip.file( + '[Content_Types].xml', + '' + ); + fs.writeFileSync(path.join(tmp, 'in.docx'), await zip.generateAsync({ type: 'nodebuffer' })); + await embedDocxFont( + path.join(tmp, 'in.docx'), + path.join(tmp, 'out.docx'), + getMonoFontTtfPath('jetbrains-mono', 400), + 'JetBrainsMono' + ); + const out = await JSZip.loadAsync(fs.readFileSync(path.join(tmp, 'out.docx'))); + const fonts = Object.keys(out.files).filter((n) => n.startsWith('word/fonts/')); + expect(fonts.length).toBeGreaterThan(0); + }); + + it('EPUB export args include --epub-embed-font', () => { + const { withEpubEmbedFontArgs } = require('../src/main/EpubFontEmbedder'); + const args = withEpubEmbedFontArgs( + ['-o', 'out.epub', 'in.md'], + '/tmp/JetBrainsMono-Regular.ttf', + 'JetBrains Mono' + ); + expect(args).toContain('--epub-embed-font=/tmp/JetBrainsMono-Regular.ttf'); + }); + + it('HTML CSS contains base64 font face', () => { + const { buildExportCss } = require('../src/main/ExportCss'); + const woff2 = Buffer.from('woff2-test'); + const css = buildExportCss( + { monospaceFont: 'jetbrains-mono', monospaceLigatures: true }, + { woff2 } + ); + const m = css.match(/base64,([A-Za-z0-9+/=]+)/); + expect(Buffer.from(m[1], 'base64').toString()).toBe('woff2-test'); + }); + + (ttfAvailable() && pandocAvailable() ? it : it.skip)( + 'PDF export references the monospace font (xelatex header)', + () => { + const { buildPdfFontHeader } = require('../src/main/PdfFontHeader'); + const { getMonoFontTtfPath } = require('../src/main/MonospaceFontConfig'); + const ttf = getMonoFontTtfPath('jetbrains-mono', 400); + const out = buildPdfFontHeader( + { monospaceFont: 'jetbrains-mono', monospaceLigatures: false }, + ttf, + 'JetBrains Mono' + ); + const content = fs.readFileSync(out.headerPath, 'utf-8'); + expect(content).toContain('\\setmonofont'); + expect(content).toContain('JetBrainsMono-Regular.ttf'); + fs.rmSync(out.dir, { recursive: true, force: true }); + } + ); +});