Como usar o Playwright para testes de captura de tela
Crie testes de captura de tela com Playwright usando baselines visuais, projetos de navegador, esperas estáveis, limites de diferença e revisão de atualizações.
14 min de leituraCaptura de sitesPor Temposhot Team
O teste de captura de tela compara a aparência atual de uma página com uma imagem aprovada anteriormente. Se os pixels diferirem além do limite permitido, o teste falha e mostra a diferença. O Playwright Test tem essa função integrada por meio de expect(page).toHaveScreenshot(), sem plugin adicional. O método também é chamado de teste de regressão visual.
Os exemplos deste guia criam uma única suíte: 12 páginas de marketing e de aplicação testadas em 3 projetos de navegador, além de 8 componentes compartilhados. Todos os números abaixo, dos totais de baselines aos limites de diferença, vêm dessa suíte.
Quando o teste de captura de tela vale a pena
Os testes visuais detectam problemas que as asserções de DOM não detectam: uma folha de estilo que não carregou, um botão deslocado para fora da tela, uma fonte que voltou para a fonte padrão do sistema. O custo de manutenção compensa quando:
- a interface tem um design system ou componentes compartilhados usados em muitas páginas
- o CSS muda com frequência e os revisores não conseguem verificar todas as páginas manualmente
- bugs de layout já chegaram à produção
Em páginas com dados ao vivo, anúncios ou conteúdo de usuários, o custo supera o retorno. Cada execução gera um resultado diferente, e a suíte passa a produzir falsos alertas.
Dimensione a suíte antes de escrever os testes
Cada asserção cria uma imagem baseline por projeto. A suíte de exemplo tem 12 asserções de página inteira e 8 asserções de componentes, cada uma executada em 3 projetos. São 60 arquivos PNG para criar, revisar e manter no repositório.
Páginas que ficam fora da suíte
Algumas páginas falham em todas as execuções, independentemente de como o teste é escrito. Um painel com gráficos ao vivo, um feed de publicações de usuários e uma página com banners rotativos se enquadram nesse grupo. Para essas páginas, teste o estado vazio ou um componente com dados fixos.
A equipe do exemplo deixou 5 páginas candidatas fora da suíte: 2 painéis, um índice de blog com publicações novas toda semana, uma página de status e uma página de resultados de busca. Cada uma é coberta por asserções de elementos nas partes compartilhadas. A lista de páginas completas permaneceu em 12.
Uma captura de página inteira de uma página de marketing longa pode chegar a um megabyte ou mais. Sessenta arquivos desse tamanho aumentam o histórico do Git sempre que os baselines mudam. Asserções de componentes geram arquivos menores e mudam com menos frequência, por isso concentram a maior parte da suíte.
Instale o Playwright Test
npm init playwright@latest
O instalador cria playwright.config.ts, uma pasta de testes de exemplo e um workflow de CI opcional, e depois baixa os navegadores. As asserções de captura de tela não exigem nenhum pacote adicional.
Fixe a versão do Playwright
Cada versão do Playwright inclui builds específicos de navegador. Uma atualização do navegador pode alterar levemente a renderização de texto e fazer falhar testes de captura de tela que não mudaram. Fixe uma versão exata em package.json e atualize-a em um pull request separado.
{
"devDependencies": {
"@playwright/test": "1.48.0"
}
}
Quando a versão mudar, regenere todos os baselines no mesmo pull request e revise-os em um único lote. Misturar a atualização de versão com uma mudança de funcionalidade impede identificar qual das duas causou a diferença.
Crie o primeiro teste de captura de tela
// tests/visual.spec.ts
import { test, expect } from '@playwright/test';
test('home page matches baseline', async ({ page }) => {
await page.goto('https://example.com/');
await expect(page).toHaveScreenshot('home.png', { fullPage: true });
});
A primeira execução não tem baseline para comparar. O Playwright grava a captura como imagem esperada e faz o teste falhar, para que o novo baseline seja revisado. Execute npx playwright test novamente e o teste passa com o arquivo salvo.
Quando uma execução posterior apresenta diferença, o Playwright salva a imagem esperada, a captura atual e uma imagem de diferença com os pixels alterados destacados. O relatório HTML gerado por npx playwright show-report mostra as três.
Teste as 12 páginas a partir de uma lista
Escrever 12 testes separados repete as mesmas linhas. Uma lista de páginas com um loop cria um teste nomeado para cada página. O título do teste inclui o nome da página, para que -g possa executar apenas uma delas depois.
// tests/pages.visual.spec.ts
import { test, expect } from '@playwright/test';
const pages = [
{ name: 'home', path: '/' },
{ name: 'pricing', path: '/pricing' },
{ name: 'features', path: '/features' },
{ name: 'docs-index', path: '/docs' },
// ...8 more entries
];
for (const p of pages) {
test(`page ${p.name} matches baseline`, async ({ page }) => {
await page.goto(p.path);
await page.getByRole('heading', { level: 1 }).waitFor();
await page.evaluate(() => document.fonts.ready);
await expect(page).toHaveScreenshot(`${p.name}.png`, { fullPage: true });
});
}
Caminhos relativos exigem um baseURL na configuração. A próxima seção o adiciona junto com um servidor web local.
Execute contra um build local, não contra produção
As páginas de produção mudam por motivos não relacionados ao código testado: edições de conteúdo, testes A/B e scripts de terceiros. Um build local da branch mostra apenas o que a branch alterou. A opção webServer inicia esse build antes dos testes.
// playwright.config.ts
import { defineConfig } from '@playwright/test';
export default defineConfig({
use: { baseURL: 'http://localhost:3000' },
webServer: {
command: 'npm run build && npm run start',
url: 'http://localhost:3000',
reuseExistingServer: !process.env.CI,
timeout: 120_000,
},
});
reuseExistingServer permite que o desenvolvedor mantenha o próprio servidor em execução nas execuções locais. No CI o valor é false, então o job sempre inicia um build novo.
Teste uma parte menor da página
test('pricing card', async ({ page }) => {
await page.goto('https://example.com/pricing');
await expect(page.getByTestId('pro-plan')).toHaveScreenshot('pro-card.png');
});
Asserções de elemento são menores e falham com menos frequência por motivos não relacionados. Uma mudança no cabeçalho não quebra o teste do card de preços. Use asserções de página inteira para algumas páginas principais e asserções de elemento para os componentes.
Escolha a asserção de cada página
Na suíte de exemplo, as 12 páginas se dividem em 4 tipos. Apenas as páginas que as pessoas avaliam como um todo recebem asserção de página inteira. As páginas montadas com componentes compartilhados recebem asserções de elemento nesses componentes.
Adicione projetos de navegador e dispositivo
Os projetos em playwright.config.ts executam os mesmos testes em vários navegadores e perfis de dispositivo.
import { defineConfig, devices } from '@playwright/test';
export default defineConfig({
projects: [
{ name: 'chromium', use: { ...devices['Desktop Chrome'] } },
{ name: 'webkit', use: { ...devices['Desktop Safari'] } },
{ name: 'mobile', use: { ...devices['iPhone 13'] } },
],
});
Cada projeto mantém seus próprios baselines. Os nomes dos arquivos incluem o projeto e a plataforma, por exemplo home-chromium-darwin.png. Três projetos significam três baselines por asserção, então adicione projetos apenas onde os layouts realmente diferem.
Mantenha os baselines em uma pasta previsível
A localização padrão coloca uma pasta -snapshots ao lado de cada arquivo de teste. Com vários arquivos de teste, os baselines ficam espalhados pela árvore de testes. A opção snapshotPathTemplate os move para uma pasta por arquivo de teste dentro de uma raiz compartilhada.
// playwright.config.ts
export default defineConfig({
snapshotPathTemplate: '{testDir}/__screenshots__/{testFilePath}/{arg}{-projectName}{ext}',
});
O template omite o sufixo de plataforma, o que funciona porque todas as execuções usam a mesma imagem Docker Linux. Se algumas execuções ainda ocorrerem no macOS, mantenha {-snapshotSuffix} no template para que as duas plataformas nunca compartilhem o mesmo arquivo.
O revisor abre uma pasta e vê todos os baselines de um arquivo de especificação. Renomear um arquivo de teste move seus baselines para uma nova pasta, então faça a renomeação em um pull request que também regenere os baselines.
Torne o estado da página estável
Uma captura só é comparável se a página chegar ao mesmo estado em todas as execuções.
- Aguarde um elemento pronto, como um título ou uma imagem carregada, antes da asserção.
- Use dados de teste fixos, em vez de contas reais.
- Defina uma viewport fixa na configuração.
- Aguarde as fontes web com
await page.evaluate(() => document.fonts.ready).
O toHaveScreenshot já repete a verificação até que duas capturas consecutivas coincidam, e interrompe as animações CSS por padrão. Esses dois comportamentos eliminam boa parte do ruído de tempo sem código adicional.
Aguarde um sinal, não um tempo fixo
Um page.waitForTimeout(3000) fixo deixa a suíte mais lenta e ainda falha em um runner lento. Aguardar um elemento específico vincula a asserção ao estado da página. A espera pelo título no loop de páginas acima é um exemplo.
Aguardar a rede ficar ociosa é menos confiável do que parece. Páginas que consultam uma API periodicamente ou mantêm uma conexão aberta nunca atingem esse estado, e a espera termina pelo timeout. Prefira um elemento que aparece somente depois que os dados são carregados, como a primeira linha de uma tabela.
Imagens com carregamento lento abaixo da dobra não são carregadas até entrarem na área visível. Uma captura de página inteira desse tipo mostra caixas vazias. Role até o final uma vez antes da asserção, ou desative o carregamento lento no build de teste.
Congele o tempo e bloqueie scripts de terceiros
Datas como "Atualizado há 3 minutos" mudam entre as execuções. A API de relógio fixa a hora que a página vê. Scripts de terceiros de widgets de chat e de analytics podem inserir elementos tardiamente, então o teste os bloqueia no nível de rede.
test.beforeEach(async ({ page }) => {
await page.clock.setFixedTime(new Date('2026-05-01T10:00:00Z'));
await page.route(/(analytics|chat-widget)\./, (route) => route.abort());
});
Bloquear o script também remove o que ele desenha. Isso é intencional aqui: o teste visual cobre o layout do próprio site, não o widget.
Causas de instabilidade e correções
Na suíte de exemplo, o teste falhou sem nenhuma mudança de código 9 vezes nas primeiras duas semanas. Cada falha teve origem em uma de cinco causas. A tabela relaciona cada causa à mudança que a eliminou.
Oculte ou mascare elementos voláteis
await expect(page).toHaveScreenshot('dashboard.png', {
mask: [page.locator('.timestamp'), page.locator('.user-avatar')],
});
As áreas mascaradas são cobertas por uma caixa sólida nas duas imagens, então o conteúdo delas é ignorado. Para elementos que devem desaparecer por completo, passe uma folha de estilo com a opção stylePath e oculte-os nela. Mascarar é melhor do que remover elementos, porque o layout ao redor deles continua sendo verificado.
Mascare por papel, não por layout
Um localizador de máscara que depende de classes de layout quebra quando a marcação muda. Um test ID ou um papel de acessibilidade se mantém estável em redesenhos. Na suíte de exemplo, todo elemento volátil tem um data-testid que começa com volatile-.
await expect(page).toHaveScreenshot('account.png', {
mask: [page.locator('[data-testid^="volatile-"]')],
maskColor: '#FF00FF',
});
Um maskColor chamativo deixa as caixas mascaradas evidentes no relatório. Assim, os revisores identificam de relance quais áreas o teste ignora.
Defina o limite de diferença com cuidado
| Opção | Significado |
|---|---|
maxDiffPixels | Número de pixels que podem diferir. |
maxDiffPixelRatio | Proporção de pixels que pode diferir, de 0 a 1. |
threshold | Quanto a cor de um pixel precisa diferir para contar, de 0 a 1. Padrão 0,2. |
Mantenha as tolerâncias baixas. Um maxDiffPixelRatio de 0,01 permite que 1 por cento dos pixels mude, o que, em uma captura de página inteira de 1280×4000, representa mais de 50.000 pixels. Isso é suficiente para esconder um ícone ausente. Comece com maxDiffPixels em torno de 100 e ajuste apenas quando uma falsa falha específica exigir.
O cálculo de limite para uma captura de página inteira
Os números abaixo usam uma captura de 1280 por 4000 pixels, o tamanho da página de preços do exemplo. Um ícone de 24 por 24 pixels ocupa 576 pixels. Uma tolerância baseada em proporção de 1 por cento permitiria que esse ícone desaparecesse 88 vezes sem gerar falha.
Defina os padrões uma vez na configuração
As opções por chamada são fáceis de esquecer em um teste novo. O bloco expect.toHaveScreenshot na configuração define os padrões de todas as asserções. Testes individuais ainda podem substituí-los.
// playwright.config.ts
export default defineConfig({
expect: {
toHaveScreenshot: {
maxDiffPixels: 100,
threshold: 0.2,
animations: 'disabled',
caret: 'hide',
scale: 'css',
},
},
});
scale: 'css' faz com que um pixel CSS corresponda a um pixel da imagem, então um perfil de dispositivo do tipo Retina não dobra o tamanho da imagem. Os baselines continuam menores e as contagens de pixels acima continuam válidas.
Revise e atualize os baselines
Quando uma mudança de design é intencional, regenere os baselines afetados:
npx playwright test --update-snapshots
Limite as atualizações aos testes que falharam, passando um nome de arquivo ou -g com o título do teste. Uma atualização geral em toda a suíte torna mudanças acidentais fáceis de passar despercebidas.
npx playwright test tests/pages.visual.spec.ts -g "page pricing" --update-snapshots
Leia as três imagens em ordem
Comece pela imagem real, não pela diferença. A imagem real mostra se a página parece quebrada para uma pessoa. Depois, abra a diferença para localizar onde os pixels mudaram, e a imagem esperada para ver o que existia antes.
A diferença marca os pixels alterados em vermelho. Um contorno vermelho fino em torno de cada bloco de texto geralmente indica mudança de fonte ou de suavização de serrilhado, não um bug de layout. Um retângulo vermelho sólido em um ponto geralmente indica que um elemento se moveu, apareceu ou desapareceu.
O relatório HTML também oferece uma visualização em slider entre o esperado e o real. Arrastar o slider sobre a área alterada mostra deslocamentos pequenos, como uma mudança de margem de 2 pixels, com mais clareza do que a diferença.
O ciclo de vida de um baseline
Um baseline passa pelo mesmo ciclo sempre que a página muda de propósito. O ciclo tem um único ponto de decisão: se a diferença é intencional. Todo o resto é mecânico.
Revise as mudanças de baseline no pull request
Um PNG alterado em um pull request é uma mudança de código. O GitHub e a maioria das ferramentas de revisão exibem as diferenças de imagem lado a lado. A lista abaixo é o que os revisores da equipe de exemplo verificam antes de aprovar.
Execute os testes de captura de tela no CI
Baselines gerados no macOS não coincidem com capturas de um runner Linux, porque as fontes e a suavização de serrilhado são diferentes. A configuração confiável é gerar e comparar os baselines no mesmo ambiente, normalmente a imagem Docker oficial do Playwright na mesma versão do pacote instalado.
Uma rotina viável:
- Os desenvolvedores executam a suíte visual dentro da imagem Docker, não diretamente no macOS.
- O CI executa a mesma imagem e envia o relatório HTML como artefato em caso de falha.
- As mudanças intencionais recebem novos baselines, com commit junto com a mudança de código.
- Os revisores verificam as diferenças de imagem no pull request.
Gere baselines no Docker em um Mac
A tag da imagem deve corresponder à versão fixada do Playwright. Uma incompatibilidade reintroduz as diferenças de fonte e de renderização que a imagem deveria eliminar. Execute o comando na raiz do projeto.
docker run --rm --ipc=host -v "$(pwd)":/work -w /work \
mcr.microsoft.com/playwright:v1.48.0-noble \
/bin/bash -c "npm ci && npx playwright test --update-snapshots -g 'page pricing'"
Um job de CI para a suíte visual
O job roda dentro da mesma imagem, então não é necessária uma etapa de download de navegador. Ele envia o relatório somente quando há falhas. O relatório contém as imagens esperada, real e de diferença de cada falha.
# .github/workflows/visual.yml
name: visual
on: pull_request
jobs:
visual:
runs-on: ubuntu-latest
container:
image: mcr.microsoft.com/playwright:v1.48.0-noble
steps:
- uses: actions/checkout@v4
- run: npm ci
- run: npx playwright test tests/*.visual.spec.ts
env:
HOME: /root
- uses: actions/upload-artifact@v4
if: failure()
with:
name: playwright-report
path: playwright-report/
No exemplo, o job completo leva cerca de 5 minutos. Executar as 8 asserções de componentes em cada pull request e as 36 asserções de página apenas na branch principal reduz o job do pull request para menos de 3 minutos.
Onde a automação simples de capturas ainda se aplica
Os testes visuais respondem a uma pergunta: esta página mudou em relação ao seu baseline? Eles não produzem um conjunto de revisão para uma reunião de design, um relatório para cliente ou um arquivo datado, e pessoas fora da engenharia raramente os executam.
Para esses casos, uma ferramenta de captura simples é mais direta. O Temposhot Website Capture recebe uma lista de URLs em um Mac e captura cada página nas predefinições Desktop 1920×1080, Tablet 768×1024 ou Mobile 390×844, sem nenhum código de teste. Para comparar duas capturas a olho, a Compare Images destaca as diferenças no navegador. A entrada comparação de capturas de tela cobre o método geral.
Duas ferramentas para duas perguntas diferentes
A suíte visual e uma ferramenta de captura costumam coexistir na mesma equipe. A engenharia é responsável pela suíte. Designers e gerentes de produto usam as capturas para revisões e arquivos.
Erros comuns
- Fazer commit de baselines gerados em um notebook e compará-los no CI Linux.
- Fazer asserção de páginas inteiras que incluem datas, carrosséis ou anúncios.
- Aumentar os limites para silenciar um teste instável, em vez de corrigir o estado da página.
- Executar
--update-snapshotsem toda a suíte após uma única mudança intencional. - Adicionar muitos projetos de dispositivo antes de os baselines de desktop estarem estáveis.
Triagem de um build visual com falha
Um job visual com falha exige uma decisão, não uma nova execução. Repetir um teste instável até que ele passe oculta a causa e deixa passar a próxima regressão real. A ordem abaixo resolve a maioria das falhas em poucos minutos.
- Baixe o artefato do relatório e conte as asserções que falharam.
- Se todas as asserções de um projeto falharam, verifique primeiro se houve mudança de navegador ou de fonte nesse projeto.
- Se uma página falhou nos 3 projetos, abra a imagem real dela; a causa geralmente está na própria página.
- Se uma página falhou em apenas um projeto, procure um problema de CSS específico do navegador ou uma causa de instabilidade da tabela acima.
- Corrija a causa ou atualize apenas esse baseline, nunca os dois no mesmo commit.
Perguntas frequentes
Onde as imagens de baseline são armazenadas?
Por padrão, em uma pasta ao lado do arquivo de teste, com o nome do arquivo de teste seguido do sufixo -snapshots. A opção de configuração snapshotPathTemplate altera esse local.
Posso comparar capturas de tela sem o Playwright Test?
Sim. Capture com qualquer ferramenta e compare os arquivos com uma ferramenta de diferença de imagens. O Playwright Test adiciona repetições, máscaras e relatórios sobre essa comparação.
Os testes de captura de tela devem rodar em todo pull request?
Para asserções em nível de componente, geralmente sim. Suítes grandes de página inteira podem rodar em um agendamento ou antes das versões, caso tornem o pipeline lento.
Por que um teste passa localmente e falha no CI sem mudança de código?
Os dois ambientes renderizam o texto de forma diferente. Fontes, suavização de serrilhado e o build do navegador podem variar entre macOS e Linux. Execute a suíte na mesma imagem Docker localmente e no CI.
Com quantos projetos uma suíte nova deve começar?
Com um. Comece com um único projeto de desktop até que os baselines fiquem estáveis por uma ou duas semanas. Depois adicione o projeto mobile, porque os layouts mobile são os que mais diferem. Adicione um segundo navegador de desktop por último, e somente se usuários relatarem bugs de layout específicos de um navegador.
A suíte pode testar páginas que exigem login?
Sim. Faça login uma vez em um projeto de configuração, salve a sessão com storageState e carregue esse arquivo nos projetos visuais. Use uma conta de teste dedicada com dados fixos, para que nomes e números na página permaneçam os mesmos em todas as execuções.
Os baselines devem ser armazenados com Git LFS?
Ajuda quando a suíte passa de centenas de imagens ou quando os arquivos PNG mudam com frequência. Para uma suíte de cerca de 60 arquivos, o Git comum geralmente basta. Revise o tamanho do repositório depois de alguns meses de atualizações de baseline.
Referências
Precisa de automação de capturas de tela de sites sem código?
Quando o objetivo é um conjunto de capturas de tela em vez de um resultado de aprovado ou reprovado, o Temposhot Website Capture executa uma lista de URLs localmente em um Mac. Veja automação de capturas de tela de sites ou o início rápido do Website Capture.
playwrightvisual-testingscreenshot-comparisonci
Termos usados aqui: playwright, responsive screenshot, screenshot automation, screenshot comparison, viewport screenshot, visual regression testing
Transforme capturas de tela repetitivas em trabalho pronto
O Temposhot faz capturas por temporizador ou a cada clique e exporta o resultado em PDF, imagens, ZIP ou MP4.
Posts relacionados
- Como automatizar capturas de tela de sites com PlaywrightUse o Playwright para automatizar capturas de tela de sites com JavaScript. Capture uma página, páginas inteiras, visualizações mobile e lotes de URLs com novas tentativas.
- Como automatizar capturas de tela de sites com PuppeteerUse o Puppeteer para automatizar capturas de tela de sites com JavaScript. Capture uma página, páginas inteiras, lotes de URLs e viewports móveis com um script reutilizável.
- Como automatizar capturas de tela de sites sem códigoEscolha uma forma sem código de automatizar capturas de tela de sites: captura por lista de URLs, plataforma de automação com API de capturas, ferramentas de monitoramento ou extensões do navegador.
- Melhores alternativas ao Xnapper para Mac em 2026Compare alternativas ao Xnapper para Mac em captura repetida, captura e gravação com rolagem, fundos gratuitos, guias multiplataforma e o app nativo do sistema.