teqo-backup/tests/unit/campaignHome.unit.spec.tsx

140 lines
5.6 KiB
TypeScript
Raw Permalink Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

import { cleanup, render, screen } from '@testing-library/react'
import { afterEach, describe, expect, it, vi } from 'vitest'
import HomePage from '@/app/(frontend)/(home)/page'
import { toContentPiecePublicItem, type ContentPiecePublicItem } from '@/lib/contentPieceCatalog'
const pieceItem = toContentPiecePublicItem({
id: 1,
slug: 'peca-da-home',
title: 'Peça da home',
type: 'foto',
status: 'publicado',
origin: 'arquivo',
media: { id: 2, filename: 'peca.png', mimeType: 'image/png' },
})
if (!pieceItem) throw new Error('fixture should be public')
type MockImageProps = React.ImgHTMLAttributes<HTMLImageElement> & {
fill?: boolean
priority?: boolean
}
vi.mock('next/image', () => ({
default: ({ alt, fill: _fill, priority: _priority, ...props }: MockImageProps) => (
// eslint-disable-next-line @next/next/no-img-element
<img alt={alt} {...props} />
),
}))
// The content section is an async server component that reads posts through
// the Payload DB; the unit env has no database and an async child suspends the
// whole tree under testing-library. Its empty/full behavior is covered by e2e
// (frontend.e2e.spec.ts), so the home skeleton tests render it away.
vi.mock('@/components/CampaignContentSection', () => ({
CampaignContentSection: () => null,
}))
// The S9 capture form is a client component (radix comboboxes/checkbox) whose
// interactions are e2e-covered; the unit skeleton only pins the section shell.
vi.mock('@/components/CampaignNewsletterForm', () => ({
CampaignNewsletterCapture: () => null,
}))
// S10 — the section reads the site-settings global (server-only, Payload DB);
// the unit env has no database, and the pixel behavior is e2e-covered.
vi.mock('@/utilities/campaignHomeTracking', () => ({
getCampaignHomeMetaPixelId: async () => null,
}))
// S21/S22 — the home reads the jingle listing through the Payload DB +
// unstable_cache for the sound section and the footer discovery flag; the unit
// env has neither, and the real listing/kill-switch behavior is e2e-covered
// (frontendJingles.e2e.spec.ts).
vi.mock('@/utilities/jingleReads', () => ({
getPublishedJingleItems: async () => [],
}))
// S27/S39 — the same for the Central de Conteúdos listing (cached read that
// feeds the discovery flag and the home sample); the real kill-switch behavior
// is e2e-covered (frontendConteudos.e2e.spec.ts).
const contentPieces = vi.hoisted(() => ({ items: [] as ContentPiecePublicItem[] }))
vi.mock('@/utilities/content/contentPieceReads', () => ({
getPublishedContentPieceItems: async () => contentPieces.items,
}))
// S14 — the card section renders the client studio island (next/font local
// face + matchMedia + canvas); its behavior is e2e-covered, so the unit
// skeleton mocks both the face module and the island.
vi.mock('@/app/(frontend)/fonts', () => ({
brexterBold: { variable: 'font-brexter-mock', style: { fontFamily: 'Brexter' } },
}))
vi.mock('@/components/cards/CardsStudio', () => ({
CardsStudio: () => null,
}))
afterEach(() => {
contentPieces.items = []
cleanup()
})
describe('Campaign home', () => {
it('monta as seis seções previstas e o rodapé eleitoral', async () => {
render(await HomePage())
expect(screen.getByRole('heading', { level: 1, name: 'MAIS SAÚDE MAIS FUTURO' })).toBeTruthy()
expect(screen.getByText('3.333')).toBeTruthy()
expect(screen.getByText('1.031')).toBeTruthy()
expect(screen.getByText('3º')).toBeTruthy()
expect(screen.getByRole('heading', { name: /Eleger deputado é coisa séria/i })).toBeTruthy()
expect(
screen.getByRole('heading', {
name: /Junto com o trabalhador e do lado de quem mais precisa, sempre/i,
}),
).toBeTruthy()
// S22 — with zero published jingles the sound section shows only the radio.
expect(screen.getByRole('heading', { name: 'Sintonize com a Rádio 1313' })).toBeTruthy()
expect(screen.getByRole('heading', { name: /Mostre que você está com Solla/i })).toBeTruthy()
expect(screen.getByRole('heading', { name: /Receba as novidades da campanha/i })).toBeTruthy()
expect(screen.getByText(/CNPJ: 68\.430\.467\/0001-05/)).toBeTruthy()
})
it('usa exatamente as seis bandeiras aprovadas', async () => {
render(await HomePage())
// S6: cada bandeira aparece em duas renderizações irmãs (grade desktop e
// carrossel mobile); o teste garante que as duas seguem a mesma copy.
const expectedTitles = [
'Defender o SUS e valorizar quem cuida',
'Fim da escala 6×1 e jornada de 40h',
'Educação em tempo integral e federais no interior',
'Recomprar a Refinaria de Mataripe',
'Salário mínimo forte, emprego e moradia',
'Defesa intransigente da democracia',
]
for (const title of expectedTitles) {
expect(screen.getAllByText(title)).toHaveLength(2)
}
})
it('mostra a seção da Central com peça publicada e a esconde sem nenhuma (fail-closed)', async () => {
contentPieces.items = [pieceItem]
render(await HomePage())
expect(screen.getByRole('heading', { name: 'Peça voto pra Solla 1313' })).toBeTruthy()
// The single-piece layout renders one CTA per viewport (split desktop +
// stacked mobile); both hand off to the Central.
const ctas = screen.getAllByRole('link', { name: /Ver todas as peças/ })
expect(ctas.length).toBeGreaterThan(0)
for (const cta of ctas) expect(cta.getAttribute('href')).toBe('/conteudos')
cleanup()
contentPieces.items = []
render(await HomePage())
expect(screen.queryByRole('heading', { name: 'Peça voto pra Solla 1313' })).toBeNull()
expect(screen.queryByRole('link', { name: /Ver todas as peças/ })).toBeNull()
})
})