mirror of
https://github.com/fsolla/teqo.git
synced 2026-11-13 15:13:07 +00:00
* deploy: limpeza pós-deploy do build cache e tags locais antigas (INF3/F2) O deploy compila no homeserver e acumulava build cache + tags locais antigas no disco raiz (incidente 19/08: 45G de cache). Após o smoke: docker builder prune -f + remoção das tags locais teqo-1313 exceto o SHA em uso (best-effort, registry preserva o rollback). * feat(S9): captura de novidades da campanha na home pública (form + CTA do hero) Seção 'Receba as novidades da campanha' acima do rodapé: nome + WhatsApp obrigatórios, e-mail/estado/cidade/comentário opcionais, toggle pré-selecionado 'Quero fazer parte do time' gravado como subscription.campaignLevel (time|esporadico). CTA secundário do hero vira o atalho (#novidades, scroll suave). Contact + Subscription em withPayloadTransaction com consentimento novo fail-closed por chave estável 'campanha-novidades' — nada é gravado sem o texto aprovado no admin. Migration 20260819_192605 (state de contact nullable + coluna campaign_level). Impl plan, changelog, int + e2e. * plans(C141,C143,C142): permissões granulares por assessor + responsáveis de demanda + UI por perfil * plans(C141,C143,C142): permissões granulares por assessor + responsáveis de demanda + UI por perfil * feat(S8): seção 'Nossa história' na home — vídeo da trajetória embutido do YouTube (Closes #91) * docs(S8): resolução do conflito com S9 (história → newsletter) + nota do guard e2e dev-only * feat(S10): pixel do Meta na página pública de campanha (campo admin em SiteSettings + PageView na home + Lead no sucesso da captura) Campo tracking.facebookPixelId no global SiteSettings (mesma validação/normalização do precedente Petition, regra centralizada em facebookPixel.ts); home (grupo (home)) injeta o base code + PageView quando o ID está configurado (fail-closed) e o CampaignNewsletterForm dispara exatamente um Lead no sucesso do S9. Migration 20260819_213947_add_site_settings_facebook_pixel_id. Closes #94 * refactor(S10): simplify pass — Lead wiring unit-tested e raças de estado compartilhado do e2e resolvidas e2e: stub hermético de connect.facebook.net no guard compartilhado (specs paralelos renderizam a home com o pixel); advisory lock no Postgres serializando o login do usuário de teste (Payload perde sessão em login concorrente read-modify-write — classe documentada no webauthn); seedTestUser vira create-if-missing (sem janela de usuário ausente nem reset de senha); Lead do form protegido do catch de erro (tracking nunca falha uma captura commitada); descrição do campo compartilhada; unidade nova campaignNewsletterLead cobre exatamente um Lead no sucesso. * feat(C141): perfis de permissão por assessor — Visão × Edição configuráveis, enforcement fail-closed (Closes #105) * chore(C141): regenera changelog agregado após rebase com S10 * plans(C144): options write-scoped para formulários de escrita pós-C141 --------- Co-authored-by: Francisco Solla <admin@solla.dev>
75 lines
2.9 KiB
TypeScript
75 lines
2.9 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'
|
||
|
||
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,
|
||
}))
|
||
|
||
afterEach(cleanup)
|
||
|
||
describe('Campaign home', () => {
|
||
it('monta as cinco 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()
|
||
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)
|
||
}
|
||
})
|
||
})
|