mirror of
https://github.com/fsolla/teqo.git
synced 2026-11-13 23:35:20 +00:00
Ship C3 mobilization events with the actionPlan collection, list/detail/forms UI, upcoming-event blocks, access control, and migration.
204 lines
7.2 KiB
TypeScript
204 lines
7.2 KiB
TypeScript
import { readFileSync } from 'node:fs'
|
|
import { resolve } from 'node:path'
|
|
import { createElement } from 'react'
|
|
import { renderToStaticMarkup } from 'react-dom/server'
|
|
import { describe, expect, it } from 'vitest'
|
|
|
|
import { CampaignScopeBadge } from '@/components/campaign/CampaignScopeBadge'
|
|
import { LeadershipRow } from '@/components/campaign/LeadershipRow'
|
|
import { NucleusCard } from '@/components/campaign/NucleusCard'
|
|
import { SupportStatusBadge } from '@/components/campaign/SupportStatusBadge'
|
|
import { TseZoneBadge } from '@/components/campaign/TseZoneBadge'
|
|
import { VoteEstimateCard } from '@/components/campaign/VoteEstimateCard'
|
|
import { Progress } from '@/components/ui/Progress'
|
|
import { Toggle } from '@/components/ui/Toggle'
|
|
import { ToggleGroup, ToggleGroupItem } from '@/components/ui/ToggleGroup'
|
|
import { buildGeneralDashboardViewModel } from '@/utilities/campaignDashboardViewModels'
|
|
|
|
describe('campaign visual foundation', () => {
|
|
it('exposes the current progress value to assistive technology', () => {
|
|
const html = renderToStaticMarkup(createElement(Progress, { value: 42 }))
|
|
|
|
expect(html).toContain('role="progressbar"')
|
|
expect(html).toContain('aria-valuenow="42"')
|
|
})
|
|
|
|
it.each(['sm', 'default', 'lg'] as const)(
|
|
'keeps the %s toggle at least 44 by 44 pixels',
|
|
(size) => {
|
|
const html = renderToStaticMarkup(createElement(Toggle, { size }, 'Filtro'))
|
|
|
|
expect(html).toContain('min-h-11')
|
|
expect(html).toContain('min-w-11')
|
|
},
|
|
)
|
|
|
|
it('keeps toggle group items at least 44 by 44 pixels', () => {
|
|
const html = renderToStaticMarkup(
|
|
createElement(
|
|
ToggleGroup,
|
|
{ type: 'single' },
|
|
createElement(ToggleGroupItem, { value: 'engajado' }, 'Engajado'),
|
|
),
|
|
)
|
|
|
|
expect(html).toContain('data-slot="toggle-group-item"')
|
|
expect(html).toContain('min-h-11')
|
|
expect(html).toContain('min-w-11')
|
|
})
|
|
|
|
it('renders campaign scope with a visible, accessible label', () => {
|
|
const html = renderToStaticMarkup(
|
|
createElement(CampaignScopeBadge, null, '3 núcleos sob sua coordenação'),
|
|
)
|
|
|
|
expect(html).toContain('3 núcleos sob sua coordenação')
|
|
expect(html).toContain('data-scope="campaign"')
|
|
})
|
|
|
|
it('keeps the authenticated shell fixed while its content pane scrolls', () => {
|
|
const layoutSource = readFileSync(
|
|
resolve(process.cwd(), 'src/app/(campaign)/campanha/(app)/layout.tsx'),
|
|
'utf8',
|
|
)
|
|
const sidebarSource = readFileSync(
|
|
resolve(process.cwd(), 'src/components/campaign/CampaignSidebar.tsx'),
|
|
'utf8',
|
|
)
|
|
|
|
expect(layoutSource).toContain('className="h-svh min-h-0 overflow-hidden"')
|
|
expect(layoutSource).toContain('data-slot="campaign-content-scroll"')
|
|
expect(layoutSource).toContain('min-h-0 flex-1 overflow-y-auto overscroll-contain')
|
|
expect(sidebarSource).toContain('h-svh shrink-0')
|
|
})
|
|
|
|
it('uses light sidebar tokens and readable logout states', () => {
|
|
const styles = readFileSync(resolve(process.cwd(), 'src/app/(frontend)/styles.css'), 'utf8')
|
|
const sidebarSource = readFileSync(
|
|
resolve(process.cwd(), 'src/components/campaign/CampaignSidebar.tsx'),
|
|
'utf8',
|
|
)
|
|
|
|
expect(styles).toContain('--sidebar: #fafaf9')
|
|
expect(styles).toContain('--sidebar-foreground: #1c1917')
|
|
expect(styles).toContain('--sidebar-accent-foreground: #1c1917')
|
|
expect(sidebarSource).toContain('text-sidebar-foreground')
|
|
expect(sidebarSource).toContain('hover:text-sidebar-accent-foreground')
|
|
})
|
|
|
|
it.each([
|
|
['engajado', 'Engajado'],
|
|
['a_abordar', 'A abordar'],
|
|
['em_disputa', 'Em disputa'],
|
|
['negativo', 'Negativo'],
|
|
] as const)('renders the %s support status with text and semantic state', (status, label) => {
|
|
const html = renderToStaticMarkup(createElement(SupportStatusBadge, { status }))
|
|
|
|
expect(html).toContain(label)
|
|
expect(html).toContain(`data-support-status="${status}"`)
|
|
})
|
|
|
|
it('aggregates every typed dashboard support status exhaustively', () => {
|
|
const view = buildGeneralDashboardViewModel(
|
|
[],
|
|
[
|
|
{ nucleusId: 31, supportStatus: 'engajado' },
|
|
{ nucleusId: 31, supportStatus: 'a_abordar' },
|
|
{ nucleusId: 31, supportStatus: 'em_disputa' },
|
|
{ nucleusId: 31, supportStatus: 'negativo' },
|
|
],
|
|
0,
|
|
new Date('2026-07-18T12:00:00-03:00'),
|
|
[],
|
|
)
|
|
|
|
expect(view.supportCounts).toEqual({
|
|
engajado: 1,
|
|
a_abordar: 1,
|
|
em_disputa: 1,
|
|
negativo: 1,
|
|
})
|
|
})
|
|
|
|
it('qualifies TSE zones instead of showing an ambiguous number', () => {
|
|
const html = renderToStaticMarkup(createElement(TseZoneBadge, { zoneNumber: 12 }))
|
|
|
|
expect(html).toContain('ZE 12')
|
|
expect(html).toContain('aria-label="Zona Eleitoral TSE 12"')
|
|
})
|
|
|
|
it('keeps the confirmed estimate primary and a proposal secondary', () => {
|
|
const html = renderToStaticMarkup(
|
|
createElement(VoteEstimateCard, {
|
|
confirmedEstimate: 1200,
|
|
confirmedBy: 'João S.',
|
|
confirmedAt: '12/07',
|
|
proposedEstimate: 1450,
|
|
proposedBy: 'Maria A.',
|
|
}),
|
|
)
|
|
|
|
expect(html).toContain('1.200 votos')
|
|
expect(html).toContain('Confirmada')
|
|
expect(html).toContain('Sugestão pendente')
|
|
expect(html).toContain('1.450 votos')
|
|
expect(html.indexOf('1.200 votos')).toBeLessThan(html.indexOf('1.450 votos'))
|
|
expect(html).toContain('data-slot="alert"')
|
|
expect(html).not.toContain('text-3xl')
|
|
})
|
|
|
|
it('renders nucleus territory, TSE zones, cadence, and leadership summary', () => {
|
|
const html = renderToStaticMarkup(
|
|
createElement(NucleusCard, {
|
|
name: 'Quilombo Rio das Rãs',
|
|
territory: 'Comunidade · Bom Jesus da Lapa',
|
|
tseZones: [12],
|
|
confirmedVoteEstimate: 1200,
|
|
hasPendingEstimate: true,
|
|
lastUpdateLabel: 'Última atualização há 10 dias',
|
|
isUpdateOverdue: true,
|
|
leadershipCounts: { engaged: 5, toApproach: 3, disputed: 1 },
|
|
}),
|
|
)
|
|
|
|
expect(html).toContain('Quilombo Rio das Rãs')
|
|
expect(html).toContain('Comunidade · Bom Jesus da Lapa')
|
|
expect(html).toContain('ZE 12')
|
|
expect(html).toContain('1.200 votos')
|
|
expect(html).toContain('Sugestão pendente')
|
|
expect(html).toContain('5 engajadas')
|
|
expect(html).toContain('Atualização atrasada')
|
|
})
|
|
|
|
it('makes missing TSE coverage visible as incomplete data', () => {
|
|
const html = renderToStaticMarkup(
|
|
createElement(NucleusCard, {
|
|
name: 'Núcleo em mapeamento',
|
|
territory: 'Região · Bahia',
|
|
tseZones: [],
|
|
}),
|
|
)
|
|
|
|
expect(html).toContain('Sem Zona TSE')
|
|
})
|
|
|
|
it('renders leadership identity with avatar fallback and support status', () => {
|
|
const html = renderToStaticMarkup(
|
|
createElement(LeadershipRow, {
|
|
name: 'Maria Aparecida',
|
|
phone: '71 97654-3210',
|
|
sector: 'Religioso',
|
|
supportStatus: 'engajado',
|
|
href: '/campanha/nucleos/centro?tab=leaderships&leadership=31',
|
|
rowId: 'leadership-row-31',
|
|
}),
|
|
)
|
|
|
|
expect(html).toContain('MA')
|
|
expect(html).toContain('Maria Aparecida')
|
|
expect(html).toContain('71 97654-3210')
|
|
expect(html).toContain('Religioso')
|
|
expect(html).toContain('Engajado')
|
|
expect(html).toContain('href="/campanha/nucleos/centro?tab=leaderships')
|
|
})
|
|
})
|