teqo-backup/tests/unit/campaignWizardShell.unit.spec.tsx
Cursor Agent 0c605f4375
OPS13 — remove className/Tailwind literal pins from unit specs
Replace layout/spacing utility string assertions with stable contract
pins (data-slot, data-retracted, exported constant identity, ARIA).
Delete cosmetic-only padding/bleed/grid pins per plan remover-pins-classname.

Closes #240

Co-authored-by: Francisco Solla <fsolla@users.noreply.github.com>
2026-08-02 16:47:27 +00:00

193 lines
5.9 KiB
TypeScript

import { cleanup, render, screen, within } from '@testing-library/react'
import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest'
vi.mock('next/navigation', () => ({
useRouter: () => ({
push: vi.fn(),
replace: vi.fn(),
}),
}))
import { CampaignWizardShell } from '@/components/campaign/shared/CampaignWizardShell'
import { CampaignMobileTopBar } from '@/components/campaign/shell/CampaignMobileTopBar'
import { CampaignWizardChromeProvider } from '@/components/campaign/shell/CampaignWizardChromeContext'
import { SidebarProvider } from '@/components/ui/Sidebar'
import { WIZARD_MUNICIPALITY_STEP_TITLE, wizardFlowChromeAriaLabel } from '@/lib/campaignWizardCopy'
import { stubMatchMedia } from '../helpers/matchMedia'
const renderWizardShell = (props: React.ComponentProps<typeof CampaignWizardShell>) =>
render(
<SidebarProvider>
<CampaignWizardChromeProvider>
<CampaignMobileTopBar />
<CampaignWizardShell {...props} />
</CampaignWizardChromeProvider>
</SidebarProvider>,
)
describe('CampaignWizardShell', () => {
beforeEach(() => {
stubMatchMedia()
})
afterEach(() => {
cleanup()
vi.unstubAllGlobals()
})
it('registers wizard chrome on the mobile top bar for continue steps', () => {
renderWizardShell({
flowTitle: 'Ajustar votos',
stepTitle: 'Quantos votos?',
isEntryStep: false,
previousHref: '/campanha/acoes/atualizar-votos',
dismissHref: '/campanha',
municipalityLabel: 'Cairu',
children: 'Corpo',
})
expect(screen.getByRole('link', { name: /Voltar/ }).getAttribute('href')).toBe(
'/campanha/acoes/atualizar-votos',
)
const topBar = document.querySelector(
'[data-slot="campaign-mobile-top-bar"][data-mode="wizard"]',
)
expect(topBar).toBeTruthy()
expect(
within(topBar as HTMLElement).getByLabelText('Município em atualização: Cairu'),
).toBeTruthy()
})
it('registers entry chrome without back control', () => {
renderWizardShell({
flowTitle: 'Ajustar votos',
stepTitle: WIZARD_MUNICIPALITY_STEP_TITLE,
isEntryStep: true,
previousHref: '/campanha',
dismissHref: '/campanha',
children: 'Corpo',
})
expect(screen.getByRole('link', { name: 'Sair da ação' })).toBeTruthy()
expect(screen.queryByRole('link', { name: /Voltar/ })).toBeNull()
})
it('labels main landmark with the step title', () => {
const { container } = renderWizardShell({
flowTitle: 'Ajustar votos',
stepTitle: 'Pergunta',
isEntryStep: true,
previousHref: '/campanha',
children: 'Corpo',
})
const main = within(container).getByRole('main')
const heading = within(container).getByRole('heading', { level: 1, name: 'Pergunta' })
expect(main.getAttribute('aria-labelledby')).toBe(heading.id)
})
it('omits step heading when stepTitle is null and labels main with flow title', () => {
const { container } = renderWizardShell({
flowTitle: 'Ajustar votos',
stepTitle: null,
isEntryStep: true,
previousHref: '/campanha',
children: 'Corpo',
})
expect(screen.queryByRole('heading', { level: 1 })).toBeNull()
const main = within(container).getByRole('main')
expect(main.getAttribute('aria-labelledby')).toBeNull()
expect(main.getAttribute('aria-label')).toBe(wizardFlowChromeAriaLabel('Ajustar votos'))
})
it('skips title focus when contentFocus is none', () => {
const { rerender } = render(
<SidebarProvider>
<CampaignWizardChromeProvider>
<CampaignMobileTopBar />
<CampaignWizardShell
flowTitle="Ajustar votos"
stepTitle="Passo 1"
isEntryStep
previousHref="/campanha"
contentFocus="none"
>
<input aria-label="Primeiro campo" />
</CampaignWizardShell>
</CampaignWizardChromeProvider>
</SidebarProvider>,
)
rerender(
<SidebarProvider>
<CampaignWizardChromeProvider>
<CampaignMobileTopBar />
<CampaignWizardShell
flowTitle="Ajustar votos"
stepTitle="Passo 2"
isEntryStep
previousHref="/campanha"
contentFocus="none"
>
<input aria-label="Primeiro campo" />
</CampaignWizardShell>
</CampaignWizardChromeProvider>
</SidebarProvider>,
)
expect(document.activeElement).not.toBe(
screen.getByRole('heading', { level: 1, name: 'Passo 2' }),
)
})
it('moves focus to the step title when stepTitle changes', () => {
const { rerender } = render(
<SidebarProvider>
<CampaignWizardChromeProvider>
<CampaignMobileTopBar />
<CampaignWizardShell
flowTitle="Ajustar votos"
stepTitle="Passo 1"
isEntryStep
previousHref="/campanha"
>
Corpo
</CampaignWizardShell>
</CampaignWizardChromeProvider>
</SidebarProvider>,
)
rerender(
<SidebarProvider>
<CampaignWizardChromeProvider>
<CampaignMobileTopBar />
<CampaignWizardShell
flowTitle="Ajustar votos"
stepTitle="Passo 2"
isEntryStep
previousHref="/campanha"
>
Corpo
</CampaignWizardShell>
</CampaignWizardChromeProvider>
</SidebarProvider>,
)
expect(document.activeElement).toBe(screen.getByRole('heading', { level: 1, name: 'Passo 2' }))
})
it('shows municipality caption in main on desktop breakpoint classes', () => {
const { container } = renderWizardShell({
flowTitle: 'Ajustar votos',
stepTitle: 'Pergunta',
isEntryStep: false,
previousHref: '/campanha/acoes/atualizar-votos',
municipalityLabel: 'Cairu',
children: null,
})
const caption = container.querySelector('main p.md\\:block')
expect(caption?.textContent).toBe('Cairu')
})
})