teqo-backup/tests/unit/campaignNavigationUrls.unit.spec.ts

209 lines
6.9 KiB
TypeScript

// @vitest-environment node
import { describe, expect, it } from 'vitest'
import { campaignConceptHref } from '@/lib/campaignIntelligenceConcepts'
import {
CAMPAIGN_AGENDA_HOME,
CAMPAIGN_HOME,
CAMPAIGN_PROFILE_HOME,
LEADER_CONTACTS_HOME,
} from '@/lib/campaignPaths'
import type { CampaignUser } from '@/payload-types'
import {
buildCampaignNavigationLink,
buildCampaignNavigationLinks,
} from '@/utilities/ai/campaignNavigationUrls'
import { buildMunicipalityListHref } from '@/utilities/municipality/municipalityListUrl'
const coordinator = { role: 'coordinator' } as CampaignUser
const advisor = { role: 'advisor' } as CampaignUser
const leader = { role: 'leader' } as CampaignUser
describe('campaignNavigationUrls', () => {
it('builds home and detail paths for staff', () => {
const home = buildCampaignNavigationLink(coordinator.role, { destination: 'home' })
expect(home).toEqual({ ok: true, path: CAMPAIGN_HOME, label: 'Início' })
const municipality = buildCampaignNavigationLink(coordinator.role, {
destination: 'municipality',
slug: 'ilheus',
label: 'Ilhéus',
})
expect(municipality).toEqual({
ok: true,
path: '/campanha/municipios/ilheus',
label: 'Ilhéus',
})
})
it('rejects invalid municipality slugs', () => {
const outcome = buildCampaignNavigationLink(coordinator.role, {
destination: 'municipality',
slug: 'slug-inventado',
})
expect(outcome.ok).toBe(false)
if (!outcome.ok) {
expect(outcome.error).toMatch(/Slug de município inválido/)
}
})
it('accepts the virtual Salvador city slug (B178)', () => {
const outcome = buildCampaignNavigationLink(coordinator.role, {
destination: 'municipality',
slug: 'salvador',
})
expect(outcome).toEqual({ ok: true, path: '/campanha/municipios/salvador', label: 'Município' })
})
it('delegates municipality list filters to buildMunicipalityListHref', () => {
const outcome = buildCampaignNavigationLink(coordinator.role, {
destination: 'municipalityList',
coverage: 'sem_assessor',
priority: 'alta',
advisors: [42],
})
expect(outcome.ok).toBe(true)
if (outcome.ok) {
expect(outcome.path).toBe(
buildMunicipalityListHref(
{ page: 1, coverage: 'sem_assessor', priority: 'alta', advisors: [42] },
1,
),
)
}
})
it('builds the "Sem dobradinha" municipality list filter (B190)', () => {
const outcome = buildCampaignNavigationLink(coordinator.role, {
destination: 'municipalityList',
stateDeputies: ['sem_dobradinha', 7],
label: 'Municípios sem dobradinha',
})
expect(outcome.ok).toBe(true)
if (outcome.ok) {
// Literal URL — canonical relationship params: ids asc, sentinel last.
expect(outcome.path).toBe('/campanha/municipios?stateDeputy=7&stateDeputy=sem_dobradinha')
expect(outcome.label).toBe('Municípios sem dobradinha')
}
})
it('drops invalid stateDeputy filter values instead of failing the link', () => {
const outcome = buildCampaignNavigationLink(coordinator.role, {
destination: 'municipalityList',
stateDeputies: ['sem_dobradinha', -3],
})
expect(outcome.ok).toBe(true)
if (outcome.ok) {
expect(outcome.path).toBe('/campanha/municipios?stateDeputy=sem_dobradinha')
}
})
it('blocks staff destinations for leaders', () => {
const outcome = buildCampaignNavigationLink(leader.role, {
destination: 'municipality',
slug: 'ilheus',
})
expect(outcome.ok).toBe(false)
if (!outcome.ok) {
expect(outcome.alternatives).toEqual([
CAMPAIGN_HOME,
LEADER_CONTACTS_HOME,
CAMPAIGN_PROFILE_HOME,
])
}
})
it('allows leader-safe destinations', () => {
const contacts = buildCampaignNavigationLink(leader.role, { destination: 'leaderContacts' })
expect(contacts).toEqual({
ok: true,
path: LEADER_CONTACTS_HOME,
label: 'Meus contatos',
})
const profile = buildCampaignNavigationLink(leader.role, { destination: 'perfil' })
expect(profile).toEqual({ ok: true, path: CAMPAIGN_PROFILE_HOME, label: 'Perfil' })
})
it('blocks Meus contatos for every non-leader role (C159)', () => {
for (const role of ['coordinator', 'advisor', 'candidate', 'communicator'] as const) {
const outcome = buildCampaignNavigationLink(role, { destination: 'leaderContacts' })
expect(outcome.ok).toBe(false)
if (!outcome.ok) {
expect(outcome.error).toMatch(/exclusiva da liderança/)
expect(outcome.alternatives).toEqual([CAMPAIGN_HOME, CAMPAIGN_PROFILE_HOME])
}
}
})
it('keeps home and perfil available to the communication assessor (C159)', () => {
const home = buildCampaignNavigationLink('communicator', { destination: 'home' })
expect(home).toEqual({ ok: true, path: CAMPAIGN_HOME, label: 'Início' })
const profile = buildCampaignNavigationLink('communicator', { destination: 'perfil' })
expect(profile).toEqual({ ok: true, path: CAMPAIGN_PROFILE_HOME, label: 'Perfil' })
})
it('blocks assessor area for advisors', () => {
const outcome = buildCampaignNavigationLink(advisor.role, {
destination: 'advisor',
id: 1,
})
expect(outcome.ok).toBe(false)
if (!outcome.ok) {
expect(outcome.error).toMatch(/assessores/)
}
})
it('builds concept glossary links with anchors', () => {
const outcome = buildCampaignNavigationLink(coordinator.role, {
destination: 'conceitos',
conceptId: 'captura',
label: 'Captura',
})
expect(outcome).toEqual({
ok: true,
path: campaignConceptHref('captura'),
label: 'Captura',
})
})
it('uses agenda URLs for calendar filters and keeps advanced legacy list links', () => {
const agenda = buildCampaignNavigationLink(coordinator.role, {
destination: 'activityList',
municipality: 12,
tag: 'Comício',
deputyPresent: true,
})
expect(agenda).toEqual({
ok: true,
path: `${CAMPAIGN_AGENDA_HOME}?municipality=12&deputyPresent=1&tag=Com%C3%ADcio`,
label: 'Agenda',
})
const legacy = buildCampaignNavigationLink(coordinator.role, {
destination: 'activityList',
q: 'centro',
tab: 'todos',
})
expect(legacy.ok && legacy.path).toBe('/campanha/atividades?q=centro&tab=todos')
const mixed = buildCampaignNavigationLink(coordinator.role, {
destination: 'activityList',
q: 'centro',
deputyPresent: true,
})
expect(mixed.ok).toBe(false)
})
it('returns partial success in batch mode', () => {
const result = buildCampaignNavigationLinks(coordinator.role, [
{ destination: 'home', label: 'Início' },
{ destination: 'municipality', slug: 'slug-invalido' },
])
expect(result.links).toEqual([{ path: CAMPAIGN_HOME, label: 'Início' }])
expect(result.errors).toHaveLength(1)
expect(result.errors?.[0]?.index).toBe(1)
})
})