Playbook de Erros E2E — OLP
SSOT para resolução de erros em testes. Toda falha encontrada DEVE ser catalogada aqui com causa raiz, classificação e padrão derivado.
Última atualização: 2026-04-04
1. Protocolo de Diagnóstico
Workflow obrigatório antes de classificar qualquer falha de teste:
text
┌─────────────────────────────────────────────────────┐
│ 1. Rodar o teste contra PRODUÇÃO no sandbox │
│ BASE_URL=https://olp.digital npx playwright ... │
│ │
│ 2. Passou em prod? │
│ ├─ SIM → Teste está correto. │
│ │ Problema é no staging (dados/infra). │
│ │ → Corrigir staging, NÃO o teste. │
│ │ │
│ └─ NÃO → Bug no teste (seletor, import, lógica)│
│ → Corrigir o teste, catalogar aqui. │
│ │
│ 3. NUNCA relaxar assertions para aceitar │
│ resultados vazios ou incorretos. │
└─────────────────────────────────────────────────────┘Checklist antes de dar veredito
- [ ] Teste rodou contra produção?
- [ ] DOM real foi inspecionado (browser tools ou screenshot)?
- [ ] Se falha é timing: qual é o tempo real da operação em staging?
- [ ] Se falha é dados: staging tem os mesmos dados que produção?
- [ ] Se falha é seletor: seletor bate com o DOM real em produção?
2. Catálogo de Erros e Resoluções
ERR-001 — ReferenceError: BASE_URL is not defined
| Campo | Valor |
|---|---|
| Arquivo | e2e/loading-gate.spec.ts |
| Erro | ReferenceError: BASE_URL is not defined (3 testes falharam) |
| Causa raiz | Import faltando — o spec importava apenas TIMEOUTS mas usava BASE_URL |
| Classificação | Bug de teste (import) |
| Resolução | Adicionar BASE_URL ao import: import { BASE_URL, TIMEOUTS } from './fixtures/constants' |
| Padrão derivado | Todo spec que usa navegação DEVE importar BASE_URL de fixtures/constants |
ERR-002 — waitForURL timeout após login em SPA
| Campo | Valor |
|---|---|
| Arquivo | e2e/login-senha.spec.ts |
| Erro | page.waitForURL(regex) expira mesmo com login bem-sucedido |
| Causa raiz | SPA não muda a URL após login — o React Router renderiza o Dashboard na mesma rota |
| Classificação | Bug de teste (premissa errada sobre navegação SPA) |
| Resolução | Substituir waitForURL por page.waitForSelector('nav button') para verificar que o Dashboard hidratou |
| Padrão derivado | Em SPAs, nunca usar waitForURL para confirmar login. Verificar a presença de elemento do Dashboard (sidebar/nav) |
ERR-003 — Seletor input[name="senha"] não encontrado
| Campo | Valor |
|---|---|
| Arquivo | e2e/login-senha.spec.ts |
| Erro | locator.fill: Error: strict mode violation ou element not found |
| Causa raiz | O input de senha no DOM real usa id="senha" sem atributo name |
| Classificação | Bug de teste (seletor errado) |
| Resolução | Corrigir seletor para input#senha |
| Padrão derivado | Preferir seletores #id sobre [name]. Sempre inspecionar o DOM real em produção (browser tools ou screenshot) antes de criar seletores |
ERR-004 — Toggle de senha não funciona com click()
| Campo | Valor |
|---|---|
| Arquivo | e2e/login-senha.spec.ts |
| Erro | Após click() no botão de toggle, o input continua type="password" |
| Causa raiz | O componente usa padrão hold-to-reveal (mousedown mostra, mouseup esconde) em vez de toggle simples |
| Classificação | Bug de teste (premissa errada sobre interação) |
| Resolução | Usar dispatchEvent('mousedown') para revelar e dispatchEvent('mouseup') para ocultar |
| Padrão derivado | Verificar o padrão de interação real do componente (click vs hold vs hover) antes de escrever o teste. Testar manualmente no browser primeiro |
ERR-005 — Login timeout 15s insuficiente
| Campo | Valor |
|---|---|
| Arquivo | e2e/fixtures/constants.ts |
| Erro | Login expira antes de completar no staging |
| Causa raiz | Argon2id (hash de senha) é computacionalmente caro por design. No staging com menos recursos, pode levar >15s |
| Classificação | Staging infra (timing) |
| Resolução | Aumentar TIMEOUTS.login de 15s para 30s |
| Padrão derivado | TIMEOUTS.login DEVE ser ≥30s. Para cold start de Edge Functions, usar ≥20s. Timeouts devem considerar o pior caso do staging, não o caso médio de produção |
ERR-006 — Templates não carregam ao clicar olimpíada (hook)
| Campo | Valor |
|---|---|
| Arquivo | Componente de templates do especialista/coordenador |
| Erro | Lista de templates sempre vazia ao selecionar olimpíada |
| Causa raiz | Hook deprecated retornava [] as Template[] hardcoded. O listarTemplates() imperativo populava o cache do React Query, mas o componente lia a propriedade estática vazia |
| Classificação | Bug de código (hook deprecated, não reativo) |
| Resolução | Usar hook reativo (useComunicacaoTemplates(hubId)) que lê diretamente do cache React Query |
| Padrão derivado | Se dados não aparecem na UI, verificar se o hook lê do cache React Query de forma reativa (subscription). Hooks imperativos que populam cache sem retornar dados são anti-padrão |
ERR-007 — Templates vazio no staging (dados)
| Campo | Valor |
|---|---|
| Arquivo | Seed/dados do staging |
| Erro | Teste de comunicação falha porque não há templates associados à escola de teste |
| Causa raiz | O seed do staging não incluía dados de template_hubs e templates_mensagem para a escola "Monteiro Lobato" |
| Classificação | Staging dados (seed incompleto) |
| Resolução | Adicionar templates ao seed do staging |
| Padrão derivado | Se o teste espera dados (templates, olimpíadas, alunos), o seed do staging DEVE ter esses dados. Nunca aceitar empty state como correto se produção tem dados |
ERR-008 — Notificação ntfy nunca enviada no CI
| Campo | Valor |
|---|---|
| Arquivo | .github/workflows/ci.yml |
| Erro | Step "Notificar falha E2E via ntfy" termina em 0s sem enviar nada, sem erro visível |
| Causa raiz | Workflow usava secrets.NTFY_TOPIC mas o secret no GitHub se chama NTFY_TOPIC_URL. Variável vazia → if [ -n ] falha silenciosamente |
| Classificação | Bug de infra (nome de secret divergente entre Supabase e GitHub Actions) |
| Resolução | Trocar para secrets.NTFY_TOPIC_URL, adicionar warning explícito se ausente, e aceitar tanto hash quanto URL completa |
| Padrão derivado | Nomes de secrets entre ambientes (Supabase Vault vs GitHub Actions) podem divergir. Sempre validar com log/warning se o secret está vazio antes de usá-lo. Nunca assumir que ausência de erro = execução bem-sucedida |
ERR-009 — Seletor .cursor-pointer genérico causa clique em elemento errado
| Campo | Valor |
|---|---|
| Arquivo | e2e/comunicacao-coordenador.spec.ts |
| Erro | Teste "clicar em olimpíada carrega templates" falha — clica em elemento errado ou não encontra card |
| Causa raiz | Seletor [data-testid="olimpiada-card"], .cursor-pointer — o data-testid nunca foi adicionado ao componente, e .cursor-pointer é genérico demais (casa com checkboxes, labels, skeletons) |
| Classificação | Bug de teste (seletor) + Bug de código (data-testid ausente) |
| Resolução | Adicionar data-testid="olimpiada-card" no componente comunicacao-templates.tsx e usar apenas [data-testid="olimpiada-card"] no teste |
| Padrão derivado | Componentes interativos clicados por E2E DEVEM ter data-testid. Nunca usar classes CSS utilitárias (.cursor-pointer, .flex, etc.) como seletor principal |
3. Padrões Derivados — Checklist
Regras consolidadas extraídas do catálogo. Obrigatórias para testes novos e revisão de existentes.
3.1 Seletores
- [ ] Preferir
#idsobre[name]ou[data-testid] - [ ] Inspecionar DOM real em produção antes de definir seletores
- [ ] Nunca assumir que atributo
nameexiste — verificar - [ ] Para formulários: usar o
iddo input que está nohtmlFordo label
3.2 Navegação SPA
- [ ] Não usar
waitForURLpara confirmar login ou navegação interna - [ ] Verificar hidratação via presença de elemento do Dashboard (
nav button, sidebar) - [ ] Usar
waitUntil: 'domcontentloaded'em vez de'load'(evita espera por polling/realtime)
3.3 Timing
- [ ]
TIMEOUTS.login≥ 30s (Argon2id) - [ ] Cold start Edge Functions ≥ 20s
- [ ] Considerar pior caso do staging, não média de produção
- [ ] Se timeout falha, medir tempo real antes de aumentar arbitrariamente
3.4 Interações
- [ ] Verificar padrão de interação real (click vs hold vs hover vs drag)
- [ ] Testar manualmente no browser antes de escrever automação
- [ ] Para hold-to-reveal: usar
mousedown/mouseup - [ ] Para dropdowns shadcn: usar
click+waitForSelectorno popover
3.5 Dados e Staging
- [ ] Se teste espera dados, seed do staging DEVE ter esses dados
- [ ] Nunca aceitar empty state se produção tem dados — corrigir seed
- [ ] Validar que escola/usuário de teste tem as relações necessárias (olimpíadas, templates, turmas)
3.6 Hooks e React Query
- [ ] Se dados não renderizam, verificar se hook é reativo (subscription do React Query)
- [ ] Hooks imperativos que populam cache sem retornar dados são anti-padrão
- [ ] Após fix em hook, rodar teste E2E para confirmar que dados aparecem
4. Como Adicionar Novo Erro
Ao encontrar uma nova falha de teste:
- Seguir o Protocolo de Diagnóstico (Seção 1)
- Criar entrada no Catálogo (Seção 2) com o próximo ID sequencial (
ERR-008, etc.) - Preencher todos os campos: Arquivo, Erro, Causa raiz, Classificação, Resolução, Padrão derivado
- Adicionar padrão à checklist da Seção 3 se for uma regra nova
- Atualizar a data no cabeçalho do documento
Template para nova entrada
markdown
### ERR-XXX — [Descrição curta]
| Campo | Valor |
|-------|-------|
| **Arquivo** | `e2e/xxx.spec.ts` |
| **Erro** | [Mensagem de erro exata] |
| **Causa raiz** | [O que realmente causou] |
| **Classificação** | Bug de teste / Staging infra / Staging dados / Bug de código |
| **Resolução** | [O que foi feito para corrigir] |
| **Padrão derivado** | [Regra para testes futuros] |ERR-010 — password-history-helper.test.ts regex desatualizada
| Campo | Valor |
|---|---|
| Arquivo | supabase/functions/_shared/__tests__/password-history-helper.test.ts |
| Erro | assertExists(match) falha — match é null |
| Causa raiz | Constante renomeada de HISTORY_LIMIT para HISTORY_SAVE_LIMIT + HISTORY_CHECK_LIMIT durante refatoração. Teste usa regex readTextFile e ficou desatualizado. |
| Classificação | Bug de teste (regex) |
| Resolução | Atualizar regex para HISTORY_SAVE_LIMIT e valor esperado 5 |
2026-04-24 — reorder_active_carousel viola CHECK 23514 com posições negativas
| Campo | Detalhe |
|---|---|
| Sintoma | Toast "Falha ao preparar reordenação" ao mover header em ATIVOS (drag interno) ou ao publicar via drag de RASCUNHO→ATIVO no meio da lista. |
| Erro Postgres | 23514 — new row for relation "headers_novidades" violates check constraint "headers_novidades_ordem_no_carrossel_check" |
| Causa raiz | Estratégia 2-pass usava posições negativas (-1..-N) como temporárias para evitar colisão no UNIQUE INDEX parcial idx_headers_novidades_posicao_ativa. O CHECK (ordem_no_carrossel IS NULL) OR (ordem_no_carrossel >= 1) rejeita qualquer INT < 1. |
| Por que não foi pego antes | Testes de contrato Deno usam mock do Supabase client (CHECK não dispara). UI exigia drag RASCUNHO→ATIVO encadeado com reorder interno (≥2 ativos) para acionar o caminho — combinação não coberta por E2E. |
| Fix | Pass 1 passa a setar ordem_no_carrossel = NULL via UPDATE ... .in(ids). NULL é compatível com CHECK e fora do escopo do UNIQUE parcial (WHERE ordem_no_carrossel IS NOT NULL). Bônus: 1 round-trip em vez de N. Pass 2 inalterado. |
| Classificação | Bug arquitetural (estratégia incompatível com schema), não bug de teste. |
| Padrão derivado | Antes de usar valores "sentinela" (negativos, zero, MAX_INT) para liberar índices únicos, validar contra todos os CHECK constraints da coluna. CHECK + UNIQUE parcial = NULL costuma ser a única zona segura. |
| Prevenção | Adicionar teste de integração contra DB real validando reorder com conjunto ≥2 ativos (follow-up). |
2026-05-09 — deno-tests cai com 522 do esm.sh
| Campo | Detalhe |
|---|---|
| Sintoma | Job deno-tests no CI falha sem qualquer mudança de código. Erro: error: Import 'https://esm.sh/@supabase/supabase-js@2.49.1' failed: 522 <unknown status code> em _shared/password-history-helper.ts. Ocorre antes de qualquer Deno.test() rodar. |
| Causa raiz | esm.sh é CDN público atrás de Cloudflare. 522 = "origin unreachable" — indisponibilidade transitória do CDN. deno cache aborta com exit 1, derrubando todo o job. SPOF externo, não regressão. |
| Classificação | Bug de infra (dependência externa), não bug de código nem de teste. |
| Fix | Migrar import para npm:@supabase/supabase-js@2.49.1 (ou import type quando usado só como tipo). Registry oficial é cacheado pelo runner e ordens de magnitude mais estável. |
| Prevenção | (1) Guard estático scripts/audit/no-esm-sh-imports.ts no lint-and-build; (2) sentinela Deno _shared/__tests__/no-esm-sh-imports.test.ts; (3) diagnóstico automático scripts/ci/diagnose-esm-sh-failure.sh em if: failure() no deno-tests. |
| Refs | EDGE_FUNCTION_IMPORTS.md, docs/audits/AUDIT_TEST_BREAKAGE_2026-05-09.md |
Referências
- TESTING_MASTER.md — Estratégia de testes (pirâmide, camadas)
- PROBLEM_SOLVING.md — Metodologia de resolução de problemas
- STAGING_REFERENCE.md — Dados e config do staging