teqo-backup/tests/unit/municipalityRelationEditor.unit.spec.tsx
2026-08-05 02:01:40 -03:00

161 lines
5.8 KiB
TypeScript

import { cleanup, fireEvent, render, screen, waitFor } from '@testing-library/react'
import { createElement } from 'react'
import { afterAll, afterEach, beforeAll, describe, expect, it, vi } from 'vitest'
import {
MunicipalityRelationEditor,
type MunicipalityRelationMutationResult,
} from '@/components/campaign/shared/MunicipalityRelationEditor'
import { TooltipProvider } from '@/components/ui/tooltip'
beforeAll(() => {
vi.stubGlobal(
'ResizeObserver',
class {
observe() {}
unobserve() {}
disconnect() {}
},
)
Element.prototype.scrollIntoView = () => {}
})
afterEach(cleanup)
afterAll(() => {
vi.unstubAllGlobals()
Reflect.deleteProperty(Element.prototype, 'scrollIntoView')
})
describe('MunicipalityRelationEditor optimistic reconcile', () => {
it('adopts the newest sent response when concurrent toggles settle out of order', async () => {
const pending: Array<(result: MunicipalityRelationMutationResult) => void> = []
const onToggle = vi.fn(
() =>
new Promise<MunicipalityRelationMutationResult>((resolve) => {
pending.push(resolve)
}),
)
render(
createElement(
TooltipProvider,
null,
createElement(MunicipalityRelationEditor, {
municipalityName: 'Seabra',
currentIDs: [],
options: [
{ id: 1, label: 'Álvaro Lima' },
{ id: 2, label: 'Beatriz Souza' },
],
variant: 'popover',
title: 'Gerenciar relações',
description: 'Descrição.',
searchPlaceholder: 'Buscar…',
searchLabel: 'Buscar relação',
savingMessage: 'Salvando relações.',
saveErrorMessage: 'Falha ao salvar.',
triggerLabel: (entries) => `Editar relações — ${entries.map((entry) => entry.label)}`,
emptyState: createElement('span', null, 'Nenhuma'),
createLabel: (name) => `Criar “${name}”`,
onToggle,
onCreate: vi.fn(),
}),
),
)
fireEvent.click(screen.getByRole('button', { name: /Editar relações/ }))
fireEvent.change(screen.getByLabelText('Buscar relação'), { target: { value: 'alv' } })
fireEvent.click(await screen.findByRole('option', { name: 'Álvaro Lima' }))
fireEvent.click(await screen.findByRole('option', { name: 'Beatriz Souza' }))
expect(onToggle).toHaveBeenNthCalledWith(1, 1, true)
expect(onToggle).toHaveBeenNthCalledWith(2, 2, true)
pending[1]?.({ status: 'success', selectedIDs: [1, 2] })
pending[0]?.({ status: 'success', selectedIDs: [1] })
await waitFor(() =>
expect(screen.getByRole('button', { name: /Álvaro Lima,Beatriz Souza/ })).toBeTruthy(),
)
})
it('does not duplicate a create adopted by server props before the request resolves', async () => {
let resolveCreate: ((result: MunicipalityRelationMutationResult) => void) | undefined
const onCreate = vi.fn(
() =>
new Promise<MunicipalityRelationMutationResult>((resolve) => {
resolveCreate = resolve
}),
)
const element = (currentIDs: number[], options: Array<{ id: number; label: string }>) =>
createElement(
TooltipProvider,
null,
createElement(MunicipalityRelationEditor, {
municipalityName: 'Seabra',
currentIDs,
options,
variant: 'popover',
title: 'Gerenciar relações',
description: 'Descrição.',
searchPlaceholder: 'Buscar…',
searchLabel: 'Buscar relação',
savingMessage: 'Salvando relações.',
saveErrorMessage: 'Falha ao salvar.',
triggerLabel: (entries) => `Editar relações — ${entries.map((entry) => entry.label)}`,
emptyState: createElement('span', null, 'Nenhuma'),
createLabel: (name) => `Criar “${name}”`,
onToggle: vi.fn(),
onCreate,
}),
)
const view = render(element([], []))
fireEvent.click(screen.getByRole('button', { name: /Editar relações/ }))
fireEvent.change(screen.getByLabelText('Buscar relação'), { target: { value: 'Nova Pessoa' } })
fireEvent.click(await screen.findByRole('option', { name: /Criar “Nova Pessoa”/ }))
view.rerender(element([7], [{ id: 7, label: 'Nova Pessoa' }]))
resolveCreate?.({
status: 'success',
selectedIDs: [7],
createdEntry: { id: 7, label: 'Nova Pessoa' },
})
await waitFor(() => {
const trigger = screen.getByRole('button', { name: /Editar relações — Nova Pessoa/ })
expect(trigger.querySelectorAll('[data-slot="avatar"]')).toHaveLength(1)
})
})
it('uses known entries for display without making them selectable', async () => {
render(
createElement(
TooltipProvider,
null,
createElement(MunicipalityRelationEditor, {
municipalityName: 'Seabra',
currentIDs: [7],
knownEntries: [{ id: 7, label: 'Fora da elegibilidade' }],
options: [],
variant: 'popover',
title: 'Gerenciar relações',
description: 'Descrição.',
searchPlaceholder: 'Buscar…',
searchLabel: 'Buscar relação',
savingMessage: 'Salvando relações.',
saveErrorMessage: 'Falha ao salvar.',
triggerLabel: (entries) => `Editar relações — ${entries.map((entry) => entry.label)}`,
emptyState: createElement('span', null, 'Nenhuma'),
createLabel: (name) => `Criar “${name}”`,
onToggle: vi.fn(),
onCreate: vi.fn(),
}),
),
)
fireEvent.click(screen.getByRole('button', { name: /Fora da elegibilidade/ }))
fireEvent.change(screen.getByLabelText('Buscar relação'), {
target: { value: 'Fora da elegibilidade' },
})
expect(screen.queryByRole('option', { name: 'Fora da elegibilidade' })).toBeNull()
})
})