A nossa perspetiva, em termos simples: somos a Geonode e vendemos proxies, e o Playwright é frequentemente executado através deles para fins de scraping e testes geográficos. A verdade é que a esmagadora maioria dos tempos de espera do Playwright não tem nada a ver com proxies. Um seletor que não encontra nada, um elemento coberto por um banner de cookies, uma animação que nunca termina — tudo isto produz o mesmo erro, quer o tráfego passe diretamente, quer passe por seis intermediários. Existe um caso genuíno relacionado com proxies, abordado no final: as ligações residenciais acrescentam latência real, pelo que as predefinições ajustadas para testes locais produzem falhas falsas. Mas verifique primeiro o seletor. Se o seu teste falhar exatamente da mesma forma sem o proxy configurado, o proxy não é o problema.
Os seis tempos de espera e os seus valores predefinidos
A primeira coisa a compreender é que se trata de mecanismos distintos, com valores predefinidos diferentes, e saber qual deles foi acionado indica-lhe onde deve procurar.
| Tempo de espera | Valor predefinido | Definido através de |
|---|---|---|
| Teste | 30 000 ms | testConfig.timeout, test.setTimeout() |
| Expect | 5 000 ms | testConfig.expect.timeout, opção por asserção |
| Ação | Sem limite de tempo | testOptions.actionTimeout, opção por chamada |
| Navegação | Sem limite de tempo | testOptions.navigationTimeout, opção por chamada |
| gancho beforeAll / afterAll | 30 000 ms | test.setTimeout() dentro do gancho |
| Global | Nenhum | testConfig.globalTimeout |
Valores retirados da documentação sobre tempos de espera do Playwright.
Dois destes valores surpreendem as pessoas.
A ação e a navegação não têm tempo de espera por predefinição. Estão limitadas apenas pelo tempo de espera do teste. Assim, um page.click() sem qualificação irá aguardar até ao tempo restante do teste, e o erro que se obtém é o tempo de espera do teste ter expirado, em vez de ser o clique. É por isso que a mensagem indica 30 000 ms, mesmo que ninguém tenha configurado um clique de 30 segundos.
O tempo limite global não tem qualquer valor predefinido. A documentação descreve o seu objetivo como sendo a prevenção do «uso excessivo de recursos quando tudo corre mal» — vale a pena defini-lo na CI para que uma suíte bloqueada falhe, em vez de ocupar um executor indefinidamente.
O que significa realmente «Timeout de 30 000 ms excedido»
Essa mensagem específica refere-se ao tempo limite do teste, e o tempo limite do teste é um limite máximo, e não um diagnóstico. Algo dentro desse período demorou demasiado tempo, e a mensagem indica o limite, não o culpado.
Classificado de acordo com a frequência com que cada um é a verdadeira causa:
1. Um localizador não encontrou nada. O seletor está errado, ou o elemento ainda não apareceu, ou está dentro de um iframe ou shadow root que não foi tido em conta. O Playwright espera pacientemente por algo que nunca irá existir.
2. O elemento existe, mas não é interativo. Está coberto por uma sobreposição, um banner de cookies ou um cabeçalho fixo. Está desativado. Ainda está em animação. O Playwright espera que se torne clicável, o que nunca acontece.
3. Uma navegação nunca foi concluída. Um pedido de rede que fica bloqueado, um ciclo de redirecionamento ou uma condição de «waitUntil» — em particular, «networkidle» — que uma página com ligações persistentes nunca irá satisfazer.
4. Uma asserção nunca se tornou verdadeira. Um «expect» a verificar uma condição que a aplicação não atinge.
5. O teste faz, de facto, demasiado. É real e o menos comum.
A ordem é importante porque a correção difere completamente. Apenas o caso 5 é resolvido aumentando o tempo de espera. Nos outros quatro, aumentá-lo significa esperar mais tempo pela mesma falha.
A «actionability» é a razão pela qual o seu clique fica em espera
Compreender isto elimina grande parte da confusão, porque explica o que o Playwright está a fazer durante esses trinta segundos.
A documentação sobre a capacidade de ação afirma que o Playwright «realiza uma série de verificações de capacidade de ação nos elementos antes de executar as ações, para garantir que estas se comportam conforme o esperado», e que «aguarda automaticamente até que todas as verificações relevantes sejam bem-sucedidas e só então executa a ação solicitada». Quando as verificações não são concluídas a tempo, «a ação falha com o erro “TimeoutError”».
As verificações necessárias variam consoante a ação, e essa diferença é diagnóstica:
| Ação | Verificações necessárias |
|---|---|
click, dblclick, check, uncheck, tap, setChecked | visível, estável, recebe eventos, ativado |
hover, dragTo | visível, estável, recebe eventos |
fill, clear | visível, ativado |
selectOption | visível, ativado |
screenshot, selectText | visível |
scrollIntoViewIfNeeded | estável |
blur, focus, press, pressSequentially, dispatchEvent, setInputFiles | nenhum |
Duas coisas saltam imediatamente à vista nesta tabela.
Um click que atinge o tempo limite enquanto fill no mesmo elemento funciona aponta para «estável» ou «recebe eventos» — o elemento está em movimento ou há algo por cima dele. Animações e sobreposições são os suspeitos habituais.
As ações sem verificações são uma saída de emergência e um sinal de alerta. Se locator.click() atingir o tempo limite, mas dispatchEvent('click') funcionar, não corrigiu nada — contornou a verificação que lhe indicava que um utilizador real também não conseguiria clicar nesse elemento. Por vezes, isso é aceitável. Normalmente, significa que existe um problema genuíno de sobreposição que o seu teste simplesmente deixou de detetar.
Alterar cada tempo limite no local correto
A configuração existe em vários níveis, e colocá-la no nível errado produz resultados confusos.
Configuração global, em playwright.config.ts
:
export default defineConfig({
timeout: 60_000,
globalTimeout: 60 * 60 * 1000,
expect: { timeout: 10_000 },
use: {
actionTimeout: 15_000,
navigationTimeout: 30_000,
},
});
Repare onde cada uma se encontra. timeout
e globalTimeout
são configurações de nível superior; expect.timeout
está em expect
; actionTimeout
e navigationTimeout
estão em use
, porque são opções de teste e não configuração do executor. Colocá-las no nível errado é ignorado silenciosamente.
Por teste:
test('slow one', async ({ page }) => {
test.setTimeout(120_000);
// ...
});
**test.slow()
** triplica o tempo de espera predefinido — um bom valor predefinido para um teste que se sabe ser genuinamente longo, sem ter de escolher um número arbitrário.
Por asserção:
await expect(page.getByRole('status')).toHaveText('Done', { timeout: 30_000 });
Por ação:
await page.getByRole('button', { name: 'Export' }).click({ timeout: 15_000 });
**Em beforeAll
e afterAll
**, que têm o seu próprio limite de 30 segundos, chame test.setTimeout()
dentro do próprio hook.
Para um fixture lento, atribua-lhe o seu próprio tempo de espera em test.extend()
, em vez de aumentar o tempo de espera de todos os testes que o utilizam:
export const test = base.extend<{ seeded: void }>({
seeded: [async ({}, use) => {
await seedDatabase();
await use();
}, { timeout: 60_000 }],
});
O princípio geral: definir o âmbito mais restrito que resolva o problema. Aumentar o tempo de espera global dos testes para acomodar um teste lento faz com que todos os outros testes demorem mais tempo a falhar, o que custa tempo real na CI.
O que conta para o tempo limite do teste
Um aspeto frequentemente mal compreendido, que explica por que razão os testes atingem o tempo limite «antes de fazerem qualquer coisa».
A documentação é explícita: «O tempo gasto pela função de teste, pelas configurações de fixtures e pelos hooks de beforeEach está incluído no tempo limite do teste.»
Assim, um beforeEach que inicia sessão, insere dados e navega consome os mesmos 30 segundos de que o corpo do seu teste necessita. Um teste que parece atingir o tempo limite logo na primeira linha pode ter demorado 28 segundos na configuração.
Por predefinição, os fixtures partilham o tempo limite do teste, o que constitui a mesma armadilha sob uma forma diferente — um fixture demorado esgota o tempo disponível de todos os testes que dependem dele. Atribua aos fixtures lentos o seu próprio tempo limite, em vez de aumentar o tempo limite do teste em todos os casos.
A desmontagem é separada: as desmontagens dos fixtures e os ganchos de «afterEach» recebem o seu próprio tempo após a conclusão da função de teste, pelo que uma desmontagem lenta não consome o tempo do teste.
A implicação prática para a depuração: quando um teste atinge o tempo limite, analise toda a cadeia — fixtures, beforeEach e o corpo do teste — e não apenas a linha para a qual o erro aponta.
Diagnostique antes de aumentar
Uma sequência que resolve a maioria dos tempos de espera em poucos minutos.
Execute com o visualizador de rastreios. Esta é a ferramenta de maior valor e é subutilizada:
npx playwright test --trace on
npx playwright show-trace trace.zip
O rastreio mostra todas as ações, a sua duração, instantâneos do DOM antes e depois, e a atividade de rede. Um localizador que não encontrou correspondência fica imediatamente visível; o mesmo acontece com o banner de cookies que aparece por cima do seu botão.
Execute em modo «headed» e «slowed down» quando quiser observar o que acontece:
npx playwright test --headed --debug
Verifique se o localizador resolve o problema:
console.log(await page.getByRole('button', { name: 'Save' }).count());
O valor zero indica um problema no seletor, e nenhum valor de tempo limite irá resolvê-lo.
Verifique se se trata de um problema de estabilidade utilizando uma ação sem verificação como diagnóstico — não como solução. Se dispatchEvent('click')
funcionar onde click()
entra em timeout, algo está a cobrir ou a mover o elemento.
**Verifique se existe uma espera de tipo «networkidle
».** As páginas com beacons de análise, websockets ou polling podem nunca atingir o estado de inatividade da rede. É preferível aguardar pelo que realmente lhe interessa:
await expect(page.getByRole('heading', { name: 'Dashboard' })).toBeVisible();
Leia a mensagem de erro na íntegra. Os erros de tempo limite do Playwright incluem o localizador, a contagem de elementos resolvidos e qual a verificação de acionabilidade que estava pendente. Este último detalhe costuma identificar o problema de forma direta.
Aumentar os tempos de espera geralmente agrava a instabilidade
A parte contraintuitiva, mas importante.
Um teste instável é aquele cujo resultado depende do tempo. Aumentar o tempo de espera alarga a janela em que o teste é aprovado, pelo que a instabilidade torna-se mais rara — e, consequentemente, mais difícil de reproduzir, mais difícil de diagnosticar e mais lenta quando o teste falha.
Entretanto, o custo é suportado a cada falha. Um conjunto de 200 testes com um tempo limite de 30 segundos demora, no máximo, 100 minutos a falhar completamente; com 120 segundos, demora 400. Na CI, isso traduz-se em dinheiro real e tempo de espera real.
O que realmente resolve a instabilidade:
Aguarde pelo estado, não pelo tempo. waitForTimeout está quase sempre errado. Verifique a condição que lhe interessa e deixe o Playwright a fazer a sondagem.
Utilize verificações «web-first». expect(locator).toBeVisible() tenta novamente automaticamente. expect(await locator.isVisible()).toBe(true) verifica uma vez e falha na primeira falha — uma fonte subtil, mas muito comum, de instabilidade.
Lide com sobreposições de forma determinística. Ignore os banners de cookies num fixture, em vez de esperar que eles desapareçam.
Desative as animações na sua configuração sempre que possível, em vez de esperar que elas terminem.
Aguarde a resposta específica da rede da qual depende, em vez de esperar que a rede fique silenciosa.
Estabilize os dados. Os testes que dependem de um estado mutável partilhado são instáveis por razões que nenhum tempo limite resolve.
Quando deve realmente declarar um tempo de espera: quando a operação é realmente lenta e não há como contornar isso — um upload de um ficheiro grande, um relatório que demora um minuto a gerar, um perfil de rede deliberadamente limitado. Nesses casos, declare-o de forma restrita, apenas para esse teste ou essa asserção, e não altere os valores predefinidos.
Tempos de espera ao executar através de um proxy
O caso em que a exceção é legítima e a configuração correspondente.
O Playwright aceita definições de proxy na configuração de rede:
export default defineConfig({
use: {
proxy: {
server: 'http://proxy.example.com:9000',
username: 'user',
password: 'pass',
},
},
});
Ou por contexto, que é o que se pretende quando testes diferentes necessitam de locais de saída diferentes:
const context = await browser.newContext({
proxy: { server: 'http://proxy.example.com:9000' },
});
Três consequências práticas.
Os proxies residenciais acrescentam uma latência real. O tráfego sai através de uma ligação de consumidor real, pelo que várias centenas de milissegundos adicionais por pedido são normais e não constituem uma falha. Uma página que faz oitenta pedidos acumula essa latência oitenta vezes. As predefinições ajustadas para o localhost produzirão falhas que parecem ser causadas por proxies avariados, mas que, na realidade, se devem apenas à distância.
A resposta correta é medir em vez de adivinhar: execute o conjunto de testes através do proxy, analise os tempos de rastreio e defina navigationTimeout
e actionTimeout
com base no que observar, deixando uma margem generosa.
A largura de banda é o verdadeiro custo, e é enorme num navegador. O Playwright descarrega todas as imagens, tipos de letra, scripts e vídeos em pré-carregamento. Num tráfego residencial medido a 0,79 $/GB, isto domina todas as outras despesas. Bloquear tipos de recursos desnecessários é a maior poupança possível:
await page.route('**/*.{png,jpg,jpeg,webp,gif,woff,woff2,mp4}', r => r.abort());
Isso reduz rotineiramente a maior parte do tráfego total e, como efeito secundário, acelera os seus testes.
Um bloqueio não é um tempo limite. Se um alvo apresentar uma página de desafio, o Playwright atingirá o tempo limite à espera de um elemento que não se encontra nessa página — o que se assemelha exatamente a um tempo limite, mas não o é. Tira uma captura de ecrã em caso de falha e verifica o que foi realmente renderizado:
use: { screenshot: 'only-on-failure', trace: 'retain-on-failure' }
Este é o padrão de falha silenciosa que descrevemos em por que é importante testar proxies: o pedido foi bem-sucedido, a página foi renderizada, mas era a página errada.
Perguntas frequentes
Qual é o tempo de espera predefinido no Playwright?
30 000 ms para um teste e para os hooks beforeAll/afterAll, e 5 000 ms para as asserções expect. Os tempos de espera para ações e navegação não têm um valor predefinido e são limitados apenas pelo tempo de espera do teste; é por isso que um clique lento reporta os 30 segundos do teste, em vez do seu próprio limite.
Como posso aumentar o tempo limite de um teste do Playwright?
Chame test.setTimeout(120_000) dentro do teste, ou test.slow() para triplicar o valor predefinido. É preferível utilizar estas opções em vez de aumentar o tempo limite global, o que torna todos os outros testes mais lentos a falhar.
Por que razão o meu teste do Playwright atinge o tempo limite quando o elemento está na página?
Normalmente, porque não é acionável. O comando «click» requer que o elemento esteja visível, estável, a receber eventos e ativado — por isso, um elemento coberto por um banner, ou ainda em animação, será detetado, mas nunca clicado. A mensagem de erro indica qual a verificação que ficou pendente.
Qual é a diferença entre o tempo limite do teste e o tempo limite de espera?
O tempo limite do teste é o tempo total disponível para a função de teste, configurações de fixture e ganchos «beforeEach» combinados, sendo o valor predefinido de 30 segundos. O tempo limite de espera é o tempo durante o qual uma única asserção «web-first» realiza sondagens, sendo o valor predefinido de 5 segundos. Uma asserção que falhe após 5 segundos deve ser considerada como tempo limite de espera, e não como tempo limite do teste.
Devo utilizar o waitForTimeout no Playwright?
Quase nunca. Um tempo de espera fixo é ou demasiado curto, tornando o teste instável, ou demasiado longo, tornando o conjunto de testes lento — normalmente ambas as situações em máquinas diferentes. Em vez disso, aguarde pela condição com uma asserção «web-first», que repete a tentativa automaticamente até ao tempo limite.
Porque é que o networkidle nunca é resolvido?
Porque a página continua a fazer pedidos — beacons de análise, WebSockets, sondagens, ligações de longa duração. O networkidle requer silêncio, e muitas aplicações modernas nunca ficam em silêncio. Em vez disso, aguarde pelo elemento ou resposta específicos que lhe interessam.
Aumentar o tempo de espera resolve os testes instáveis?
Isso apenas as oculta. A instabilidade torna-se mais rara, mais difícil de reproduzir e mais lenta a falhar, enquanto cada falha genuína no conjunto de testes demora agora mais tempo. Corrija a causa — aguarde pelo estado em vez de pelo tempo, utilize asserções de nova tentativa, elimine sobreposições de forma determinística e desative animações.
Preciso de tempos de espera mais longos ao utilizar um proxy?
Muitas vezes sim, para navegação e ações, porque os proxies residenciais acrescentam uma latência real por pedido que se acumula ao longo dos muitos pedidos que uma página faz. Meça-a através do proxy com o rastreio ativado e defina os valores com base no que observar, em vez de aumentar tudo de forma preventiva.
Conclusão
A mensagem indica que o teste excedeu os 30 segundos, e isso é mais um limite do que uma explicação. Algo no seu interior aguardou uma condição que nunca se concretizou e, em quatro de cada cinco casos, essa condição é um seletor que não corresponde a nada ou um elemento que nunca se tornou passível de ação.
Portanto, a sequência que poupa tempo é a seguinte: ler o erro na íntegra, que identifica a verificação de acionabilidade pendente; abrir o rastreio, que mostra o DOM no momento da falha; confirmar se o localizador é resolvido; e só então considerar o número. Lançar um timeout é a correção correta para exatamente uma causa — uma operação que demora genuinamente mais do que o limite — e a correção errada para as outras quatro, nas quais apenas adia a falha para mais tarde.
Quando o fizer, aplique-o de forma restrita. Por teste, por asserção, por fixture. Aumentar os valores predefinidos globais para acomodar um único upload lento torna cada falha na suíte mais dispendiosa, e o tempo de CI é o único recurso que nunca volta.
