Pular para o conteúdo
Development

O botão de cancelar que não cancelava

Por Victor Da Luz•
sveltetestingdev-loggreenhouse

O trabalho desta semana no Greenhouse foi um pacote pequeno de UX: renomear um projeto inline, um chip de “negligenciado há Nd” em cards parados na lista de trabalho, preview de imagem ao lado do preview de áudio já existente, e um link “por que essas regras?” de volta para a tela de regras do onboarding. Nada disso parecia arriscado.

O bug que eu quase lancei

A interface de renomear é simples assim: clica num ícone de lápis, o título vira um campo de texto, Enter ou clicar fora salva, Escape cancela. Escrevi a versão óbvia primeiro:

function cancel() {
  editing = false;
}

Parecia certo. Aí sentei para escrever o teste de regressão especificamente para o Escape, e para o teste realmente significar alguma coisa simulei a sequência real: apertar Escape, depois disparar o blur que acontece logo depois (remover um input focado do DOM dispara um evento de blur nele, antes do navegador terminar de desmontá-lo).

Foi aí que quebrou. Minha função save() roda no blur, viu o texto ainda digitado e não confirmado do usuário sentado no valor vinculado (porque cancel() nunca tocava nele, só fechava o editor), e salvou mesmo assim. Escape parecia funcionar num clique manual normal. Não cancelava nada de verdade, só teve sorte de que geralmente nada dá blur num input que você está no meio de remover.

A correção

Uma linha: resetar o valor antes de fechar.

function cancel() {
  value = original;
  editing = false;
}

save() já tinha uma proteção de “não faz nada se nada mudou” (necessária para o caso comum de abrir o campo e dar blur sem digitar nada). Depois que cancel() reseta o valor primeiro, essa proteção torna toda a corrida inofensiva, não importa em que ordem os eventos caiam.

Por que clicar manualmente não teria pego isso

Essa é a parte que ficou comigo. Se eu tivesse só clicado na funcionalidade manualmente, Escape parece completamente correto, nada salva visivelmente. O bug só existe no intervalo entre “o input fecha” e “algum evento perdido do navegador dispara durante esse fechamento”, e navegadores não disparam esse blur de forma confiável em todo caminho de remoção, então até testes manuais repetidos talvez não mostrassem isso. Só encontrei porque escrever um teste de regressão de verdade me forçou a simular a ordem exata dos eventos, não porque eu estava caçando uma corrida.

Também nesta sessão

Verifiquei as quatro partes do pacote contra o app de verdade rodando, via IPC real, não testes mockados, usando o harness WebDriver construído ao longo de sessões anteriores. Mais uma coisinha que vale uma nota de rodapé: você não pode passar um elemento DOM encontrado como argumento de script para o endpoint de execução de script desse harness e esperar que ele continue sendo um elemento real do outro lado, ele é deserializado como um objeto simples. A correção é só reconsultar ele com document.querySelector dentro do próprio script.

Leitura relacionada

Você também pode achar útil

Proton

Proton VPN

VPN comercial com filtragem NetShield e interruptor de desligamento automático.

Como parceiro da Proton, ganho com compras qualificadas dos serviços de privacidade e segurança da Proton (Pass, Mail, VPN, Drive).

Saiba mais
AdGuard

AdGuard para iOS

Bloqueio de anúncios e rastreadores em todo o sistema no iOS, sem necessidade de um servidor DNS separado.

Como afiliado da AdGuard, ganho com compras qualificadas.

Saiba mais
Airalo

eSIM Airalo

eSIM de dados local para viagens - sem necessidade de trocar um SIM físico.

Este é meu link de indicação da Airalo. Você recebe um desconto no seu primeiro eSIM e eu ganho crédito da Airalo para o meu.

Saiba mais