// packages/core/__tests__/meeting-markdown-inline-redteam-r3-27.test.ts // 회귀: DOCX 내보내기가 인라인 강조(**x**)를 별표째 찍어 PDF 와 결과가 달랐다. // 이제 두 경로가 parseInlineRuns 하나로 굵기를 해석해야 한다. import { describe, expect, it } from 'vitest' import JSZip from 'jszip' import { markdownToDocx } from '../src/utils/markdown-to-docx' import { markdownToSimpleHtml, parseInlineRuns } from '../src/utils/meeting-markdown' interface RunSpan { text: string bold: boolean } /** document.xml 에서 본문 run(텍스트·굵기)만 뽑는다. */ async function docxRuns(markdown: string): Promise { const buffer = await markdownToDocx(markdown, 'T') const zip = await JSZip.loadAsync(buffer) const file = zip.file('word/document.xml') if (!file) throw new Error('document.xml missing') const xml = await file.async('string') const runs: RunSpan[] = [] for (const match of xml.matchAll(/([\s\S]*?)<\/w:r>/g)) { const body = match[1] const text = [...body.matchAll(/]*>([\s\S]*?)<\/w:t>/g)].map((m) => m[1]).join('') const bold = /|/.test(body) runs.push({ text, bold }) } // 첫 run 은 문서 제목('T') return runs.slice(1) } function boldTexts(runs: readonly RunSpan[]): string[] { return runs.filter((r) => r.bold).map((r) => r.text) } function htmlBoldTexts(html: string): string[] { return [...html.matchAll(/([\s\S]*?)<\/strong>/g)].map((m) => m[1]) } describe('parseInlineRuns', () => { it('강조 표기를 벗기고 굵은 조각으로 나눈다', () => { expect(parseInlineRuns('**결정**: 3월 출시')).toEqual([ { text: '결정', bold: true }, { text: ': 3월 출시', bold: false }, ]) expect(parseInlineRuns('a **b** c **d**')).toEqual([ { text: 'a ', bold: false }, { text: 'b', bold: true }, { text: ' c ', bold: false }, { text: 'd', bold: true }, ]) }) it('짝이 없는 별표·빈 문자열은 그대로 둔다', () => { expect(parseInlineRuns('2 ** 3')).toEqual([{ text: '2 ** 3', bold: false }]) expect(parseInlineRuns('****')).toEqual([{ text: '****', bold: false }]) expect(parseInlineRuns('')).toEqual([]) }) it('HTML 렌더링은 기존 출력과 같다 (이스케이프 후 )', () => { expect(markdownToSimpleHtml('- **A** & "c"')).toBe( '
    \n
  • A <b> & "c"
  • \n
', ) }) }) describe('DOCX 인라인 강조 (PDF 와 동일)', () => { it("'- **A** b' 는 DOCX 와 HTML 에서 같은 굵은 조각을 만든다", async () => { const md = '- **A** b' const runs = await docxRuns(md) expect(runs.map((r) => r.text).join('')).toBe('A b') expect(boldTexts(runs)).toEqual(htmlBoldTexts(markdownToSimpleHtml(md))) expect(boldTexts(runs)).toEqual(['A']) }) it('제목·체크박스·문단·표 셀에 별표가 남지 않는다', async () => { const md = [ '## **결정** 사항', '- [x] **담당**: 윤', '**요약** 줄', '', '| **담당** | 기한 |', '|---|---|', '| **윤** | 3월 |', ].join('\n') const runs = await docxRuns(md) const allText = runs.map((r) => r.text).join('') expect(allText).not.toContain('**') expect(allText).toContain('☑ 담당: 윤') // 표 머리글은 전체가 굵다 — 기한도 굵음 expect(boldTexts(runs)).toEqual(['결정', '담당', '요약', '담당', '기한', '윤']) expect(htmlBoldTexts(markdownToSimpleHtml(md))).toEqual(['결정', '담당', '요약', '담당', '윤']) }) })