teqo-backup/docs/plans/c134-e2e-agenda-dia-hardcoded-impl.md
Francisco Solla fe0a7999a9 test(C134): e2e agenda deriva o dia do picker de término de hoje + 1 (sem data hardcoded)
O teste C104 clicava em '17 de agosto de 2026' fixo no picker de término;
quebrava deterministicamente ~2026-09, quando o dia sairia do grid visível.
Agora o alvo é civilDatePlusDays(formatBahiaCivilDate(new Date()), 1) —
sempre dentro do grid (dia do mês ou day 1 do mês seguinte como outside day).
Absorve também o flake pré-existente do redirect canônico ?tab=overview nos
3 sítios de toHaveURL do detalhe (mesmo regex da linha 219, C123).
2026-08-13 15:01:23 -03:00

101 lines
5.9 KiB
Markdown
Raw Permalink Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

# Impl: C134 — e2e agenda: dia hardcoded "17 de agosto de 2026" no teste C104 expira
Status: aprovado
Atualizado em: 2026-08-13
Issue: #712
Intenção: body da Issue (sem plano de intenção linkado — body é a spec)
Appetite restante: herdado (correção de teste, escopo mínimo)
## Leitura da intenção
- **Outcome:** o teste e2e "cria compromisso 'Todo o dia'" (`campaignActivity.e2e.spec.ts`)
não quebra mais deterministicamente ~2026-09: o dia do picker de término é derivado
de "hoje" (`formatBahiaCivilDate(new Date()) + delta`), no mesmo padrão do resto do spec.
- **O que NÃO negociar:** nada de produto; mudança restrita ao spec de e2e.
- **O que reavaliar:** o delta. A intenção pede "+ delta (mesmo padrão do resto do spec)".
Hoje o teste não usa `civilDatePlusDays` no C104; o helper existe em
`tests/e2e/helpers/agendaPeriodLabels.ts` (usado pelo C95/mobile). A questão real é:
o delta precisa manter o dia **dentro do mesmo mês** (comentário atual: "a later day of
the same month") ou basta ser **posterior a hoje**?
## Abordagem recomendada
**Opções consideradas:**
- **A — delta fixo +1 derivado de hoje (recomendada):** `endCivil = civilDatePlusDays(formatBahiaCivilDate(new Date()), 1)`,
label do botão montado de `ptBrMonthNames` + partes do civil date. Sem navegação de mês no picker.
- **B — delta +1 com navegação de mês condicional:** se `endCivil` cai em mês diferente,
clicar o botão de próximo mês antes do dia. Mais passos, cobre o caso em que o dia alvo
não está no grid — mas o grid do react-day-picker (`showOutsideDays`) **sempre** renderiza
o dia 1 do mês seguinte como célula "outside" clicável (seleciona e navega), então B é
complexidade sem cobertura adicional.
- **C — manter hardcode mas avançar a data:** adia a quebra; não resolve o problema de classe.
**Recomendação:** A. O picker é react-day-picker v9 (`Calendar` em `ActivityDateTimeField`,
`defaultMonth` = mês de hoje; `showOutsideDays=true` renderiza o primeiro dia do mês
seguinte como botão clicável no próprio grid — verificado no source do node_modules:
`CalendarDay.outside` só marca `data-outside`, não desabilita). Com delta +1 o alvo está
**sempre** no grid visível: se hoje não é o último dia do mês, é um dia do próprio mês; se é,
vira o dia 1 do mês seguinte na primeira posição da última semana — ainda visível e clicável.
O label do botão é date-fns `PPPP` com pt-BR ("segunda-feira, 17 de agosto de 2026") — o
regex não-ancorado `/17 de agosto de 2026/` do teste casa contra o subconjunto
"dia de mês de ano"; derivamos exatamente esse subconjunto (dia sem zero à esquerda,
nome do mês capitalizado de `ptBrMonthNames`, ano). A asserção do trigger
(`toHaveText(/17\/08\/2026/)`) vira `dd/mm/aaaa` derivado do mesmo `endCivil`.
**Rejeitadas:** B (navegação extra sem ganho: o dia alvo sempre está no grid), C (adieta).
### Componentes / mudanças
- **`campaignActivity.e2e.spec.ts`** (teste C104, ~linhas 308–315): derivar
`endCivil = civilDatePlusDays(formatBahiaCivilDate(new Date()), 1)`; clicar
`endPicker.getByRole('button', { name: new RegExp(pickerDayLabel) })` com o label
montado de `ptBrMonthNames`; asserir o trigger com o `dd/mm/aaaa` derivado.
Atualizar o comentário "same month" → "later day" (pode cruzar mês no último dia).
- **Helpers:** nenhum helper novo — `civilDatePlusDays` e `ptBrMonthNames` já existem e
estão importados/exportados; a montagem do label é 3 linhas locais no spec (mesmo
padrão de `dayLabelFor` do helper — não criar helper novo para 1 uso).
- **Migration:** sem migration.
- **Access / Consent:** sem mudança.
- **UI:** sem mudança.
## Fases verificáveis
1. **Spec** — editar o teste C104 no `campaignActivity.e2e.spec.ts`.
2. **Verificação** — `pnpm test:e2e campaignActivity` (e2e com db de teste do worktree).
3. **Gates** — `tsc --noEmit`, `lint`, `format:check`, `knip` (nenhum símbolo orfanado).
## Rabbit holes / Não escopo (engenharia)
- Não tocar nos outros hardcodes de data de e2e (`campaignAiChatFollowUps` são strings
de conteúdo, não datas de calendário — verificados, sem trabalho).
- Não generalizar para um helper de "label de dia do picker": 1 uso não justifica
camada nova (`civilDatePlusDays` + `ptBrMonthNames` já cobrem).
## Riscos e mitigação
- **Fuso:** `formatBahiaCivilDate` usa hora civil da Bahia (padrão do spec inteiro);
`civilDatePlusDays` opera em UTC puro sobre o civil date — imune a DST. Mesmo
contrato do resto do spec.
- **Último dia do mês:** o dia alvo vira "outside day" — o react-day-picker v9 renderiza
e seleciona outside days normalmente (verificado no source; o unit spec de
`activityOverlay` já clica dias do mês corrente sem restrição de "inside").
Risco residual baixíssimo; se um dia o RDP mudar esse contrato, o teste falha com
"button não encontrado" — mensagem clara, não determinística de data.
## Aceite de engenharia
- [x] Aceite de produto da intenção ainda coberto (teste deriva o dia de hoje + delta)
- [x] Invariantes AGENTS/engineering-standards (mudança só em spec; sem DB, sem schema)
- [x] Testes de domínio previstos (e2e C104 roda no worktree; sem unit — sem lógica de runtime)
## Absorvido durante a execução (débito pré-existente, mesmo arquivo)
- **Flake `?tab=overview` (3 sítios):** `toHaveURL(/\/campanha\/atividades\/[^/?]+$/)` nas
linhas 86/344/397 corria contra o redirect canônico da página de detalhe
(`getActivityDetailTabRedirect` → sempre `?tab=overview` quando o query está vazio):
só passava quando o poll pegava o URL intermediário pré-redirect — quebrou 5× nos runs
de verificação, inclusive no próprio C104 (linha 344). O precedente da correção já
existia na linha 219 do mesmo arquivo (`(?:\?tab=overview)?$`); apliquei o mesmo
regex nos 3 sítios restantes. Débito absorvido (determinismo, mesma classe do C134;
sem nova Issue).