teqo-backup/tests/unit/supporterSchema.unit.spec.ts
Francisco Solla 6de8fad8f6 feat(C112): múltiplos telefones por pessoa — Contact.phones array (ordem = prioridade)
- Contact.phone (text único) → phones (array field Payload, subcampo value):
  fonte única; principal = phones[0]; beforeValidate normaliza, filtra vazios,
  rejeita inválido e duplicado DENTRO da ficha (C111 entre fichas preservado)
- migration add_contact_phones_array: backfill phone → phones[0] (gen_random_uuid)
  antes do drop da coluna + índice em value (hand-edit documentado; snapshot .json
  gerado mantém as colunas C99/C100 já existentes)
- primaryPhoneOf / phoneValuesOf / reorderWithPrimaryPhone em src/lib/phone.ts
  (ordem = prioridade; inline edit = seta o principal preservando o resto)
- findOrCreateContactByPhone aceita phones[] (dedupe pelo primário; reuse faz
  append dos faltantes; locks em todos os números digitados)
- sync conta→ficha (CampaignUser): phone da conta faz APPEND quando a ficha não
  tem (nunca reordena nem remove); tombstone LGPD substitui TODOS os telefones
- convite redemption: telefone digitado vira principal, demais preservados
- import CSV: preview sinaliza compartilhado também por número secundário;
  bulk insere contact_phones (JOIN nas leituras, uuid no insert)
- busca por telefone (apoiadores, pickers, combobox) casa qualquer número
- UI: PhonesFieldEditor compartilhado (repetível mascarado, remover, ↑/↓,
  modo inline com Salvar) nas 5 superfícies — liderança (form/ficha/lista),
  apoiador (form/ficha), dobradinha (ficha), assessor (ficha), wizard
- UI pública (petição/WhatsApp) intocada: zod e forms ficam com phone;
  as ações mapeiam phone → phones[0]
- testes: spec novo campaignMultiPhones (aceite completo), specs C111/C99
  atualizados, e2e wizard/apoiador ajustados
2026-08-11 10:47:02 -03:00

77 lines
2 KiB
TypeScript

// @vitest-environment node
import { describe, expect, it } from 'vitest'
import {
resolveBahiaMunicipality,
supporterCreateSchema,
supporterImportConfirmSchema,
supporterVoteIntentionSchema,
} from '@/lib/schemas/supporter'
describe('supporter schemas', () => {
it('normalizes phones and optional BA municipality', () => {
const parsed = supporterCreateSchema.parse({
name: 'Maria da Silva',
phones: ['+55 (71) 98888-7777'],
city: 'salvador',
consentAccepted: true,
})
expect(parsed.phones).toEqual(['71988887777'])
expect(parsed.city).toBe('Salvador')
expect(resolveBahiaMunicipality('feira de santana')).toBe('Feira de Santana')
expect(resolveBahiaMunicipality('Nowhere')).toBeNull()
})
it('requires highlighted consent when vote intention is set on create', () => {
expect(
supporterCreateSchema.safeParse({
name: 'Maria Silva',
phones: ['71988887777'],
voteIntention: 'certo',
consentAccepted: true,
}).success,
).toBe(false)
expect(
supporterCreateSchema.safeParse({
name: 'Maria Silva',
phones: ['71988887777'],
voteIntention: 'certo',
consentAccepted: true,
voteIntentionConsentAccepted: true,
}).success,
).toBe(true)
})
it('requires operator attestation and a token for import confirm', () => {
expect(
supporterImportConfirmSchema.safeParse({
importToken: 'token',
}).success,
).toBe(false)
expect(
supporterImportConfirmSchema.safeParse({
operatorAttested: true,
}).success,
).toBe(false)
expect(
supporterImportConfirmSchema.parse({
operatorAttested: true,
importToken: 'batch.123.sig',
}).importToken,
).toBe('batch.123.sig')
})
it('requires vote intention consent acceptance', () => {
expect(
supporterVoteIntentionSchema.safeParse({
id: 1,
voteIntention: 'indeciso',
}).success,
).toBe(false)
})
})