teqo-backup/tests/unit/leadershipMunicipalityMembership.unit.spec.ts
Francisco Ignácio Fontoura Solla 812e3a0bee B34: editable município chips on the leaderships list.
The "Municípios" column was dead text; it is now chips that navigate to the
município, with a territory chip when a leadership covers a whole TI, and it is
editable in place. What picks inline vs. Drawer is the `pointer:` media query,
not viewport width: on a fine pointer the editor is always mounted (the "×" sits
at the chip's corner, revealed on hover/focus) and on a coarse pointer a tap
anywhere in the cell opens a Drawer with 44 px targets.

`AdvisorMunicipalityCell` is promoted to `shared/MunicipalityPortfolioCell` and
/campanha/assessores migrates onto it in the same change, so the second call site
is born with the first. One batch action only; the single-id advisor twin is
deleted with its int tests migrated. The anti-goal on always-mounted list inputs
is amended for chip-based relations on fine pointers, not reopened.

Also fixes a latent bug: `CampaignTableHead`'s `description` seam broke any RSC
caller, because its two helpers lived in a 'use client' module.
2026-07-27 22:35:19 -03:00

49 lines
2 KiB
TypeScript

import { describe, expect, it } from 'vitest'
import { nextMunicipalityIdsAfterLeadershipMembership } from '@/lib/leadershipMunicipalityMembership'
import { MAX_LEADERSHIP_MUNICIPALITIES } from '@/lib/schemas/leadership'
describe('nextMunicipalityIdsAfterLeadershipMembership', () => {
it('appends only the municipalities that are not linked yet, and reports them as added', () => {
expect(nextMunicipalityIdsAfterLeadershipMembership([10, 20], [20, 30, 40], true)).toEqual({
next: [10, 20, 30, 40],
added: [30, 40],
changed: [30, 40],
})
})
it('removes the requested municipalities, ignores the ones not linked, and adds nothing', () => {
// `changed` excludes 99: it was never linked, so revalidating it would bust a
// route cache for a município this write did not touch.
expect(nextMunicipalityIdsAfterLeadershipMembership([10, 20, 30], [20, 99], false)).toEqual({
next: [10, 30],
added: [],
changed: [20],
})
})
it('returns null when the leadership is already in the desired state', () => {
expect(nextMunicipalityIdsAfterLeadershipMembership([10, 20], [10, 20], true)).toBeNull()
expect(nextMunicipalityIdsAfterLeadershipMembership([10, 20], [30], false)).toBeNull()
})
it('refuses to remove the last municipality', () => {
expect(() => nextMunicipalityIdsAfterLeadershipMembership([10], [10], false)).toThrow(
'pelo menos um município',
)
expect(() => nextMunicipalityIdsAfterLeadershipMembership([10, 20], [10, 20], false)).toThrow(
'pelo menos um município',
)
})
it('refuses a batch that would cross the cap', () => {
const current = Array.from({ length: MAX_LEADERSHIP_MUNICIPALITIES - 1 }, (_, i) => i + 1)
expect(nextMunicipalityIdsAfterLeadershipMembership(current, [900], true)?.next).toHaveLength(
MAX_LEADERSHIP_MUNICIPALITIES,
)
expect(() => nextMunicipalityIdsAfterLeadershipMembership(current, [900, 901], true)).toThrow(
`no máximo ${MAX_LEADERSHIP_MUNICIPALITIES}`,
)
})
})