mirror of
https://github.com/fsolla/teqo.git
synced 2026-11-13 23:53:05 +00:00
140 lines
5.6 KiB
TypeScript
140 lines
5.6 KiB
TypeScript
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()
|
||
})
|
||
})
|