import { describe, it, expect } from 'vitest'; import { renderFluffy, renderDoc } from './secretary-render-fluffy.mjs'; const base = () => ({ subject: 'дело', status: 'открыто', decisions: [], will: [], open: [], consequences: [], doneNext: [], hidden: [], acceptance: [], tails: [], candidates: [], steps: [] }); describe('renderFluffy', () => { it('пустой протокол не падает, даёт заголовок и секции', () => { const md = renderFluffy(base()); expect(md).toContain('# 📋 Протокол: дело'); expect(md).toContain('## 🌳 Ствол'); expect(md).toContain('## 🧭 Шаги'); }); it('живое решение наверху, зачёркнутое — в свёрнутом блоке, оба со ссылкой', () => { const p = base(); p.decisions = [{ text: 'живое', why: 'потому', struck: false, turns: [5] }, { text: 'старое', struck: true, turns: [3] }]; const md = renderFluffy(p); expect(md).toMatch(/- живое — потому.*ходы\/turn-5\.log/); expect(md).toContain('
▸ решённое в стволе'); expect(md).toMatch(/~~старое~~.*ходы\/turn-3\.log/); }); it('живая ветка: глиф состояния + паспорт + источник born→lastTouch', () => { const p = base(); p.hidden = [{ id: 'СВ-7', lens: 'Л6', status: 'сужен', text: 'граф хрупкость', опора: 'догадка', тяжесть: 'мелочь', born: 12, lastTouch: 15 }]; const md = renderFluffy(p); expect(md).toContain('## 🌿 Живые ветки'); expect(md).toMatch(/✂️ сужен/); expect(md).toMatch(/догадка · мелочь/); expect(md).toMatch(/turn-12\.log.*turn-15\.log/); }); it('закрытая ветка уходит в свёрнутые «решённые» с пруфом', () => { const p = base(); p.hidden = [{ id: 'СВ-5', lens: 'Л4', status: 'закрыт', text: 'без пруфа', proof: 'код:107', born: 12, lastTouch: 13 }]; const md = renderFluffy(p); expect(md).toContain('✅ решённые ветки'); expect(md).toMatch(/~~без пруфа~~.*пруф: код:107/); }); it('кандидаты: релевантные наверху, low — свёрнуты, все с источником', () => { const p = base(); p.candidates = [ { branch: 'сильная', опора: 'внутр', релевантность: 'medium', born: 15 }, { branch: 'слабая', trigger: 'цитата', опора: 'догадка', релевантность: 'low', born: 15 }, ]; const md = renderFluffy(p); expect(md).toMatch(/- сильная `внутр · medium`.*turn-15\.log/); expect(md).toContain('▸ свёрнуто 1 слабых'); expect(md).toMatch(/- слабая .*turn-15\.log/); }); it('горящие Л8/Л9 показаны с источником', () => { const p = base(); p.acceptance = [{ text: 'не проверено', born: 3, done: false }]; p.tails = [{ text: 'не убрано', born: 3, done: false }]; const md = renderFluffy(p); expect(md).toContain('## 🔥 Горит'); expect(md).toMatch(/Приёмка \(Л8\):.*не проверено.*turn-3\.log/); expect(md).toMatch(/Хвост \(Л9\):.*не убрано/); }); it('шаги со ссылкой на файл хода', () => { const p = base(); p.steps = [{ turn: 15, text: 'Ход 15 — я: …' }]; const md = renderFluffy(p); expect(md).toMatch(/- \[Ход 15\]\(ходы\/turn-15\.log\) — Ход 15/); }); it('ветка без паспорта рисует «—»', () => { const p = base(); p.hidden = [{ id: 'СВ-1', lens: 'Л1', status: 'открыт', text: 'старая', born: 3 }]; const md = renderFluffy(p); expect(md).toMatch(/🌿 открыт \| — \|/); }); }); describe('renderDoc — развилка по флагу', () => { const proto = { subject: 'дело', status: 'открыто', decisions: [{ text: 'A', struck: false }], will: [], open: [], consequences: [], doneNext: [], hidden: [], acceptance: [], tails: [], candidates: [], steps: [] }; it('флаг ON → пушистый рендер (🌳 Ствол)', () => { expect(renderDoc(proto, {}, { SECRETARY_FLUFFY: '1' })).toContain('## 🌳 Ствол'); }); it('флаг OFF (по умолчанию) → старый renderProtocol (## Решения, без 🌳)', () => { const md = renderDoc(proto, {}, {}); expect(md).toContain('## Решения'); expect(md).not.toContain('🌳'); }); });