mirror of
https://github.com/fsolla/teqo.git
synced 2026-11-12 22:13:05 +00:00
Some checks failed
Deploy / preflight (push) Has been cancelled
Deploy / verify (push) Has been cancelled
Deploy / deploy staging (teqo-staging) (push) Has been cancelled
Deploy / requeue when main advanced (push) Has been cancelled
Deploy / deploy production (teqo-1313) (push) Has been cancelled
A federal foi decidida em 04/10; o documento de cidade passa a servir inteiro à eleição de Lula. Saem conta eleitoral/cadeira de Solla, concorrentes e pré-candidatos, emendas e entregas (sem fetch do Portal da Transparência no builder), acervo de falas, panorama regional e sinais/demandas; demografia, economia e transporte ficam como contexto da visita. A página 1 vira só mobilização: KPIs oficiais do artefato TSE 2026 (Lula/Δ, X₁/X₂, Solla), papel determinístico da cidade, quem mobiliza, movimento local do 2º turno, riscos e o painel "O que pedir e falar" (pedido Lula 13; guardrail de votos em jogo no lugar do defeso). O checklist cai de 10 para 8 itens (emendas_web e quem_investe saem; relacao_campo/disputa_local reancorados no 2º turno); listas preCandidates/investments/emendasIndicators saem do contrato (arquivos antigos seguem aceitos em replay). O snapshot enxuga para rede + conjuntura + demografia (cityReportTerritory.mjs removido). Prova: unit 5.232 verdes, int do snapshot sem PII e sem os grupos aposentados, lint/format/typecheck/knip limpos e rebuild da fixture de Cruz das Almas (9 seções, 16 páginas contra 21, página 1 fechando sem fallback).
112 lines
3.3 KiB
TypeScript
112 lines
3.3 KiB
TypeScript
import { describe, expect, it } from 'vitest'
|
||
|
||
import { buildCityReport } from '../../scripts/lib/cityReportBlocks.mjs'
|
||
import { renderReportHtml, renderReportMd } from '../../scripts/lib/cityReportRender.mjs'
|
||
import {
|
||
normalizeResearchInput,
|
||
RESEARCH_CHECKLIST_IDS,
|
||
} from '../../scripts/lib/cityReportResearch.mjs'
|
||
|
||
const generatedAt = new Date('2026-09-15T12:00:00.000Z')
|
||
|
||
const research = normalizeResearchInput(
|
||
{
|
||
municipalitySlug: 'feira-de-santana',
|
||
researchedAt: '2026-09-14T10:00:00.000Z',
|
||
items: RESEARCH_CHECKLIST_IDS.map((id) => ({
|
||
id,
|
||
answer: id === 'prefeito' ? 'Prefeito <script>alert(1)</script>' : `Resposta de ${id}`,
|
||
sourceUrl: `https://exemplo.test/${id}`,
|
||
sourceDate: '2026-09-10',
|
||
})),
|
||
news: [],
|
||
gaps: [],
|
||
},
|
||
{ now: generatedAt },
|
||
)
|
||
|
||
const report = buildCityReport({
|
||
snapshot: {
|
||
meta: {
|
||
readAt: '2026-09-15T10:30:00.000Z',
|
||
database: '127.0.0.1:5433/teqo_1313',
|
||
codeSha: 'abc1234',
|
||
},
|
||
municipality: {
|
||
id: 42,
|
||
slug: 'feira-de-santana',
|
||
name: 'Feira de Santana',
|
||
kind: 'municipio',
|
||
city: 'Feira de Santana',
|
||
region: 'Portal do Sertão',
|
||
ibgeCode: '2910800',
|
||
},
|
||
pledges: {
|
||
declaredTotal: 0,
|
||
lastPledgeAt: null,
|
||
},
|
||
leaderships: { totalCount: 0, rows: [] },
|
||
advisors: [],
|
||
conjuncture: null,
|
||
demographics: null,
|
||
},
|
||
research,
|
||
generatedAt,
|
||
})
|
||
|
||
describe('renderReportHtml', () => {
|
||
const html = renderReportHtml(report)
|
||
|
||
it('renders the summary page anchor and both headers', () => {
|
||
expect(html).toContain('data-page="summary"')
|
||
expect(html).toContain('pág. 1')
|
||
expect(html).toContain('Índice')
|
||
})
|
||
|
||
it('escapes research text (no raw injection)', () => {
|
||
expect(html).not.toContain('<script>alert(1)</script>')
|
||
expect(html).toContain('<script>')
|
||
})
|
||
|
||
it('turns every URL into a clickable anchor', () => {
|
||
expect(html).toContain(
|
||
'<a href="https://exemplo.test/prefeito">https://exemplo.test/prefeito</a>',
|
||
)
|
||
expect(html).toContain('href="https://exemplo.test/segundo_turno"')
|
||
})
|
||
|
||
it('prints the gaps as callouts and the limits list', () => {
|
||
expect(html).toContain('Lacuna explícita')
|
||
expect(html).toContain('Limites')
|
||
})
|
||
|
||
it('renders the ask panel of the 2º turno (no mandate-era copy)', () => {
|
||
expect(html).toContain('<div class="panel decision">')
|
||
expect(html).toContain('O que pedir e falar')
|
||
expect(html).not.toContain('O que anunciar agora')
|
||
expect(html).not.toContain('O que NÃO anunciar')
|
||
expect(html).not.toContain('indicators')
|
||
})
|
||
})
|
||
|
||
describe('renderReportMd', () => {
|
||
const md = renderReportMd(report)
|
||
|
||
it('carries the same sections as tables and headings', () => {
|
||
expect(md).toContain('## 1. 2º turno no município (Lula × Flávio)')
|
||
expect(md).toContain('> **Papel no 2º turno: Defesa e comparecimento**')
|
||
expect(md).toContain('## 2. Rede e lideranças')
|
||
expect(md).toContain('## 9. Fontes e limites')
|
||
expect(md).not.toContain('Conta eleitoral')
|
||
expect(md).not.toContain('Concorrentes')
|
||
})
|
||
|
||
it('lists sources with URLs', () => {
|
||
expect(md).toContain('https://exemplo.test/prefeito')
|
||
expect(md).toContain('base Teqo')
|
||
})
|
||
|
||
it('states the base read date (dated snapshot)', () => {
|
||
expect(md).toContain('Base Teqo (read-only) lida em')
|
||
})
|
||
})
|