O problema dos waits fixos
Uma instrução como waitForTimeout(3000) parece resolver problemas de sincronização porque introduz uma pausa previsível no teste. O problema é que a aplicação não possui a mesma velocidade em todas as execuções.
Se o estado esperado chegar em 500 ms, o teste desperdiça 2,5 segundos. Se chegar em 3,2 segundos, o teste falha mesmo tendo recebido quase todo o tempo necessário. O valor escolhido não representa uma condição do sistema; representa apenas uma estimativa.
Espere aquilo que realmente importa
No Playwright, assertions e locators já possuem mecanismos de espera. Em vez de pausar e depois verificar, descreva diretamente a condição que precisa ser satisfeita:
await expect(page.getByText('Pedido pronto')).toBeVisible();A intenção fica explícita: o teste continua quando “Pedido pronto” estiver visível, dentro do limite configurado.
Quando o estado é numérico
O cenário de Progress Bar do laboratório é um exemplo mais interessante. O teste precisa observar um valor que muda ao longo do tempo, interromper o progresso e depois aguardar 100%.
Nesse tipo de situação, polling permite consultar o estado repetidamente sem definir uma pausa arbitrária entre etapas:
await expect.poll(async () => {
const value = await progressBar.getAttribute('aria-valuenow');
return Number(value);
}).toBe(100);O timeout continua existindo como limite de segurança. Isso é diferente de usá-lo como mecanismo de sincronização. O teste não espera todo o limite: ele termina a espera assim que a condição é atendida.
Eventos também são estados observáveis
Quando uma ação dispara uma nova aba, download ou outro evento, a sincronização deve começar antes da ação para evitar uma corrida:
const [popup] = await Promise.all([
page.waitForEvent('popup'),
page.locator('#tabButton').click(),
]);O teste registra primeiro o evento que espera e executa a ação no mesmo bloco. Isso reduz o risco de o evento acontecer antes de a espera estar ativa.
Uma regra prática
Antes de adicionar um sleep, pergunte: qual mudança observável indica que a aplicação está pronta para o próximo passo? Visibilidade, atributo, texto, resposta de rede, URL, evento ou valor são respostas melhores do que “dois segundos”.
Timeout não é o inimigo
Eliminar waits fixos não significa executar sem timeouts. Um teste precisa de limites para não aguardar indefinidamente. A diferença está no papel de cada mecanismo:
- wait fixo: define quanto tempo o teste deve obrigatoriamente ficar parado;
- timeout: define por quanto tempo uma condição pode ser aguardada antes de ser considerada falha;
- assertion/polling: define qual estado precisa ser alcançado.
Resultado
Sincronização orientada a estado tende a produzir testes mais rápidos, legíveis e resistentes às variações normais entre máquina local e CI. Mais importante: quando ocorre uma falha, a mensagem está relacionada à condição de negócio ou de interface que não foi satisfeita, e não a uma pausa escolhida arbitrariamente.