teqo/tests/unit/municipalityVoteRank.unit.spec.ts
Francisco Ignácio Fontoura Solla 66feecd66b simplify(B13): corrige classe agregada, bolhas em pane proprio e registra debitos
O /simplify da escala relativa do mapa achou um bug semantico real em
computeAggregateTerritorialClass — somava o headroom dos slugs e comparava
contra a mediana de UMA unidade, classificando um grupo grande como expansao
quando ele e marginal. A mediana agora escala por slugs.length, com teste.

As bolhas sairam do centro do bounding box para o centroide real e ganharam
pane proprio (z-index 450), porque o bringToFront do hover as escondia atras
do poligono. Legenda e pintura passaram a compartilhar o mesmo predicado,
a copy dos quantis parou de prometer faixas de igual largura, e o subtitulo
do painel parou de dizer "em 2026" com 2022 selecionado (defeito do B11).

Fonte unica onde havia duas: NO_DATA_FILL, o raio das bolhas e
formatPlacementOrdinal (ex-formatMunicipalityVoteRank, agora client-safe em
electionFormat.ts).

Os quatro debitos maiores que o cleanup viraram o fill-in B13+
(docs/plans/escala-dry-pos-b13.md); a flakiness dos e2e, reproduzida em main
limpo, foi para o ledger com a causa afinada.
2026-07-26 05:12:37 -03:00

69 lines
2.5 KiB
TypeScript

import { describe, expect, it } from 'vitest'
import { federalBaselineMunicipalitySlugs } from '@/lib/bahiaElectionAggregates'
import { formatPlacementOrdinal, formatVoteSharePercent } from '@/lib/electionFormat'
import {
compareMunicipalityVotesForSort,
computeVoteRankByYear,
DEFAULT_VOTE_RANK_YEAR,
getMunicipalityVoteRank,
} from '@/lib/municipalityVoteRank'
describe('municipalityVoteRank', () => {
it('ranks every catalog slug for the default year', () => {
const bySlug = computeVoteRankByYear(DEFAULT_VOTE_RANK_YEAR)
const slugs = federalBaselineMunicipalitySlugs()
expect(bySlug.size).toBe(slugs.length)
expect(bySlug.size).toBe(435)
let shareSum = 0
for (const slug of slugs) {
const entry = bySlug.get(slug)
expect(entry).toBeDefined()
expect(entry!.totalUnits).toBe(435)
expect(entry!.rank).toBeGreaterThanOrEqual(1)
expect(entry!.rank).toBeLessThanOrEqual(435)
expect(entry!.votes).toBeGreaterThanOrEqual(0)
expect(entry!.share).toBeGreaterThanOrEqual(0)
shareSum += entry!.share
}
expect(shareSum).toBeCloseTo(1, 10)
})
it('uses dense ranks when votes tie', () => {
const bySlug = computeVoteRankByYear(DEFAULT_VOTE_RANK_YEAR)
const byVotes = new Map<number, number[]>()
for (const entry of bySlug.values()) {
const ranks = byVotes.get(entry.votes) ?? []
ranks.push(entry.rank)
byVotes.set(entry.votes, ranks)
}
for (const ranks of byVotes.values()) {
expect(new Set(ranks).size).toBe(1)
}
const sortedUniqueVotes = [...byVotes.keys()].sort((a, b) => b - a)
let expectedDense = 1
for (const votes of sortedUniqueVotes) {
const rank = byVotes.get(votes)![0]!
expect(rank).toBe(expectedDense)
expectedDense += 1
}
})
it('returns null for unknown slugs', () => {
expect(getMunicipalityVoteRank('nao-existe')).toBeNull()
})
it('formats rank as colocação only (catalog size lives in the header hint)', () => {
expect(formatPlacementOrdinal(12)).toBe('12º')
expect(formatVoteSharePercent(0.031)).toMatch(/3[,.]1%/)
})
it('sorts zero votes last in either direction', () => {
expect(compareMunicipalityVotesForSort(0, 100, 'desc')).toBeGreaterThan(0)
expect(compareMunicipalityVotesForSort(0, 100, 'asc')).toBeGreaterThan(0)
expect(compareMunicipalityVotesForSort(200, 100, 'desc')).toBeLessThan(0)
expect(compareMunicipalityVotesForSort(50, 100, 'asc')).toBeLessThan(0)
})
})