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.
69 lines
2.5 KiB
TypeScript
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)
|
|
})
|
|
})
|