import { describe, expect, it } from 'vitest'
import { normalizeBriefingContent, trimBriefing } from '../../scripts/lib/briefingContent.mjs'
import { renderBriefingHtml, renderBriefingMd } from '../../scripts/lib/briefingRender.mjs'
import { buildDossierReport } from '../../scripts/lib/dossieBlocks.mjs'
import {
mergeDossierResearch,
normalizeDossierResearchInput,
} from '../../scripts/lib/dossieResearch.mjs'
import { MUNICIPALITY_UNIT } from '../../scripts/lib/dossieUnit.mjs'
// C210 (replanejado 2026-09-23): the briefing is the approved gate design ported
// to inline print CSS — four fixed recorte sheets (defesas → essencial + pedido
// → qa 1/2 → qa 2/2), the internal label on each and the request literal. The
// tests pin the anchors the page guard reads, the guards that cannot regress
// (literal label, "vote 1313", source per anchor) and the `.md` superset.
const generatedAt = new Date('2026-09-22T12:00:00.000Z')
const snapshot = {
meta: { readAt: '2026-09-16T10:00:00.000Z', database: 'teqo_test' },
municipality: { slug: 'ilheus', name: 'Ilhéus', region: 'Litoral Sul', ibgeCode: '2913606' },
}
const research = mergeDossierResearch([
normalizeDossierResearchInput({
municipalitySlug: 'ilheus',
era: 'B',
researchedAt: '2026-09-16T10:00:00.000Z',
items: [
{
id: 'era_b_equipamentos',
answer: 'Ambulância e atenção domiciliar no município',
sourceUrl: 'https://saude.test/ambulancia',
sourceDate: '2012-01-25',
},
{
id: 'era_b_programas',
answer: 'Saúde em Movimento com menção direta ',
sourceUrl: 'https://saude.test/programas',
sourceDate: '2013-05-10',
},
],
news: [],
gaps: [],
}),
])
const report = buildDossierReport({ snapshot, research, generatedAt })
const facts = report.bulletinFacts
const rawContent = (qaCount = 5) => ({
unitId: 'municipality',
municipalitySlug: 'ilheus',
generatedAt: generatedAt.toISOString(),
subtitle: 'Litoral Sul · consulta antes e durante o contato',
lede: 'Universidade pública não é gasto: é projeto de país.',
defenses: [
{ factId: 'era_b_programas', title: 'Ensino superior', note: 'Defesa regional.' },
{ factId: 'era_b_equipamentos', title: 'Saúde perto de casa', note: 'Defesa local.' },
],
essential: [
{
factId: 'era_b_equipamentos',
title: 'Ambulância ',
note: 'Registro local de 2012.',
},
{ factId: 'era_b_programas', title: 'Saúde em Movimento', note: 'Menção direta.' },
{ gapReason: 'sem fala própria localizada', title: 'Sem fala local', note: 'Anote a lacuna.' },
],
plan: 'Combine onde, quando e como votar e registre o compromisso nomeado.',
qa: Array.from({ length: qaCount }, (_value, index) => ({
side: index % 2 === 0 ? 'direita' : 'esquerda',
question: `Pergunta ${index}`,
acknowledge: 'Entendo a preocupação.',
answer: 'Há registro nominal no recorte.',
close: 'Posso contar com o 1313?',
factId: 'era_b_equipamentos',
})),
})
const content = normalizeBriefingContent(rawContent(), {
unit: MUNICIPALITY_UNIT,
slug: 'ilheus',
facts,
})
describe('renderBriefingHtml', () => {
const html = renderBriefingHtml(content, { unit: MUNICIPALITY_UNIT, report })
it('renders exactly the four fixed recorte sheets the page guard counts', () => {
for (const anchor of ['defesas', 'essencial', 'qa', 'qa-2']) {
expect(html).toContain(`data-page="${anchor}"`)
}
expect((html.match(/class="sheet/g) ?? []).length).toBe(4)
})
it('prints the internal label on every sheet and the page count', () => {
expect((html.match(/Insumo interno de capacitação — não publicar/g) ?? []).length).toBe(4)
for (const page of ['folha 1/4', 'folha 2/4', 'folha 3/4', 'folha 4/4']) {
expect(html).toContain(page)
}
})
it('opens with the principles copy and the defenses, not a cover', () => {
const firstSheet = html.split('data-page="essencial"')[0]
expect(firstSheet).toContain('Princípios e crenças')
expect(firstSheet).toContain('Universidade pública não é gasto: é projeto de país.')
expect(firstSheet).toContain('O que Solla defende')
expect(firstSheet).not.toContain('Como usar')
})
it('renders the literal request with the vote number and the one-line plan', () => {
expect(html).toContain('vote 1313, Jorge Solla')
expect(html).toContain('vote 13, Lula')
expect(html).toContain('request-line')
expect(html).toContain('request-block')
expect(html).toContain('compromisso nomeado')
})
it('splits the qa list across the two last sheets', () => {
const firstQa = html.split('data-page="qa"')[1].split('data-page="qa-2"')[0]
const secondQa = html.split('data-page="qa-2"')[1]
expect((html.match(/class="qa-item"/g) ?? []).length).toBe(5)
expect(firstQa).toContain('Pergunta 0')
expect(firstQa).toContain('Pergunta 2')
expect(firstQa).not.toContain('Pergunta 4')
expect(secondQa).toContain('Pergunta 3')
expect(secondQa).toContain('Pergunta 4')
})
it('links every anchored item to its sourced fact and marks the lacuna', () => {
expect(html).toContain('https://saude.test/ambulancia')
expect(html).toContain('25/01/2012')
expect(html).toContain('lacuna')
})
it('escapes authored and research text', () => {
expect(html).not.toContain('')
expect(html).toContain('<script>alert(1)</script>')
})
it('declares the shed remainder on the sheet instead of dropping it silently', () => {
const trimmed = trimBriefing(trimBriefing(content)!)!
const shedHtml = renderBriefingHtml(trimmed, { unit: MUNICIPALITY_UNIT, report })
expect(shedHtml).toContain('no briefing completo (.md)')
expect(trimmed.qa).toHaveLength(4)
})
it('prints the lacuna line only when the recorte never had a defense', () => {
const noDefenses = normalizeBriefingContent(
{ ...rawContent(), defenses: [] },
{ unit: MUNICIPALITY_UNIT, slug: 'ilheus', facts },
)
expect(renderBriefingHtml(noDefenses, { unit: MUNICIPALITY_UNIT, report })).toContain(
'Sem registro localizado no dossiê.',
)
})
it('names the recorte in the kicker and the footer', () => {
expect(html).toContain('Briefing de capacitação · Ilhéus')
expect(html).toContain('Briefing de capacitação · cidade')
expect(html).toContain('Recorte: cidade · Ilhéus')
})
})
describe('renderBriefingMd (companion superset)', () => {
it('carries every item of the full content, including what the PDF shed', () => {
let trimmed = content
while (trimBriefing(trimmed)) trimmed = trimBriefing(trimmed)!
expect(trimmed.qa.length).toBeLessThan(content.qa.length)
expect(trimmed.defenses.length).toBeLessThan(content.defenses.length)
const droppedQuestion = content.qa.find(
(item: { question: string }) =>
!trimmed.qa.some((kept: { question: string }) => kept.question === item.question),
)!
const shedHtml = renderBriefingHtml(trimmed, { unit: MUNICIPALITY_UNIT, report })
expect(shedHtml).not.toContain(droppedQuestion.question)
const md = renderBriefingMd(content, { unit: MUNICIPALITY_UNIT, report })
expect(md).toContain(droppedQuestion.question)
expect(md).toContain('Insumo interno de capacitação — não publicar')
expect(md).toContain('vote 1313, Jorge Solla')
expect(md).toContain('vote 13, Lula')
expect(md).toContain('**Plano:** Combine onde, quando e como votar')
expect(md).toContain('[fonte](https://saude.test/ambulancia)')
expect(md).toContain('## O que Solla defende')
expect(md).toContain('## O essencial do recorte')
expect(md).toContain('## O pedido')
expect(md).toContain('## Perguntas prováveis × melhores respostas')
expect(md).not.toContain('## O que evitar')
expect(md).not.toContain('## Limites e defeso')
expect((md.match(/^### /gm) ?? []).length).toBe(5)
})
})