teqo-backup/tests/unit/e2eAffectedManifest.unit.spec.ts
Francisco Solla dda55adc27 feat(S27): Central de Conteúdos pública — Peça voto pra Solla 1313
/conteudos + /conteudos/<slug>: catálogo filtrável (Tipo · Cidade · Região ·
Tema · Instituição + termo na URL canônica), play sob demanda (Vídeo/Áudio só
carregam no toque; Foto com preview leve), Baixar e Compartilhar do próprio
card com a mensagem de voto por tipo (wa.me sem destinatário, editável; "A
mídia" usa a folha nativa e cai em download onde o aparelho não suportar) e o
card personalizado como peça sintética do catálogo → /cards.

- leitura pública nova (contentPieceReads) cacheada na tag contentPieces com o
  gate where status=publicado + contentPieceIsPublic; a rota GET
  /conteudos/<slug>/midia reusa buildPrivateMediaResponse (range, disposition,
  private no-store, 404 uniforme) com download jorge-solla-1313-<slug>.<ext> e
  a allowlist inline do dono estendida (webp/gif/avif + áudios das peças)
- contrato público puro do catálogo (filtros/VM/download) + mensagens de voto
  (contentPieceShare); OG com fallback do global; conteudos entra nos slugs
  reservados; kill switch por peça vale na hora (lista, página, rodapé/selo);
  descoberta condicional no CampaignFooter e no header local
- UI portada classe-a-classe do design aprovado (cenas 01–08, com as cenas
  06–08 estendidas no trigger a); sem migration, sem Consent e sem access novo;
  contentMedia segue privada (sem segunda cópia de mídia)
- testes: 2 unit specs novos, 18 casos int (leitura pública + rota de mídia) e
  e2e frontendConteudos (5 casos); manifest e curado atualizados
- follow-up de engenharia: #1276 (docs/plans/escala-dry-pos-s27.md)

Closes #1255
2026-09-23 11:13:57 -03:00

100 lines
4.1 KiB
TypeScript

// @vitest-environment node
import { existsSync, readdirSync } from 'node:fs'
import { join } from 'node:path'
import { describe, expect, it } from 'vitest'
import {
E2E_AFFECTED_MANIFEST,
E2E_CURATED_SPECS,
E2E_RISK_PREFIXES,
E2E_SMOKE_FALLBACK_SPEC,
} from '../../scripts/lib/e2e-affected-manifest.mjs'
const E2E_DIR = join(__dirname, '../e2e')
function specNamesOnDisk() {
return readdirSync(E2E_DIR)
.filter((file) => file.endsWith('.e2e.spec.ts'))
.map((file) => file.slice(0, -'.e2e.spec.ts'.length))
}
describe('E2E_AFFECTED_MANIFEST (OPS5)', () => {
it('only references specs that exist on disk', () => {
const onDisk = new Set(specNamesOnDisk())
for (const entry of E2E_AFFECTED_MANIFEST) {
for (const spec of entry.specs) {
expect(onDisk.has(spec), `${spec} (prefixes: ${entry.prefixes.join(', ')})`).toBe(true)
expect(existsSync(join(E2E_DIR, `${spec}.e2e.spec.ts`))).toBe(true)
}
}
})
it('keeps prefixes repo-relative and trailing-slash-clean', () => {
for (const entry of E2E_AFFECTED_MANIFEST) {
expect(entry.prefixes.length).toBeGreaterThan(0)
for (const prefix of entry.prefixes) {
expect(prefix.startsWith('src/')).toBe(true)
expect(prefix.endsWith('/'), `${prefix} — drop the trailing slash`).toBe(false)
}
}
})
it('covers every on-disk spec family that has a clear domain prefix', () => {
// Smoke/infrastructure specs are allowed to be unmapped (they run in full
// mode); this pins the reverse direction: no manifest entry is dead.
const mapped = new Set(E2E_AFFECTED_MANIFEST.flatMap((entry) => entry.specs))
expect(mapped.size).toBeGreaterThanOrEqual(10)
})
it('freezes the curated high-risk e2e set (OPS86 — deliberate growth only)', () => {
// The curated set is the never-zero answer for high-risk PR diffs; change
// it on purpose (cost of the PR CI high-risk path), not by drift.
expect(E2E_CURATED_SPECS).toEqual([
// OPS87 — deliberate: the permission-profile family moved to the
// browserless HTTP mode (same coverage, new spec name).
'campaignPermissionProfileHttp',
'campaignDemandVisibility',
'campaignAiTranscribe',
'campaignAgendaFeed',
'campaignNewsletter',
// C167 — deliberate: the cut public page/route contract is new and the
// migration makes every PR of this delivery high-risk (curated only).
'campaignSpeechCut',
// C191-runtime — deliberate: real-Chromium PNG smoke; the curated net is
// the only path that wakes it for a scripts/ (high-risk) diff.
'campaignChartPng',
// S19 — deliberate: the share-link OG/redirect contract is new and the
// migration makes every PR of this delivery high-risk (curated only).
'frontendShareLink',
// S21 — deliberate: the jingles page/player/download contract is new and
// the migration makes every PR of this delivery high-risk (curated only).
'frontendJingles',
// S27 — deliberate: the Central catalogue/piece page, the public media
// route and the vote share sheet are new contracts over the C211
// collection (curated only).
'frontendConteudos',
])
const onDisk = new Set(specNamesOnDisk())
for (const spec of E2E_CURATED_SPECS) {
expect(onDisk.has(spec), spec).toBe(true)
}
})
it('keeps every risk prefix covered by a manifest entry (OPS86 fail-closed net)', () => {
// The classifier fails closed when a risk-area file matches no entry; if
// the entries drift out of sync with the risk prefixes, that fail-closed
// fires on every diff in the area. Pin the pair together.
const mappedPrefixes = new Set(E2E_AFFECTED_MANIFEST.flatMap((entry) => entry.prefixes))
for (const prefix of E2E_RISK_PREFIXES) {
expect(mappedPrefixes.has(prefix), prefix).toBe(true)
}
})
it('the unmapped fallback smoke spec exists on disk (OPS86 never-zero)', () => {
// A rename of the smoke spec would turn the fallback into a CI
// "No tests found" failure.
expect(new Set(specNamesOnDisk()).has(E2E_SMOKE_FALLBACK_SPEC)).toBe(true)
})
})