Pular para o conteúdo

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.

Quatro estatísticas da suíte de exemplo: 12 páginas, 3 projetos de navegador, 36 baselines de página, que são 12 páginas vezes 3 projetos, e 60 arquivos PNG de baseline no total, incluindo 8 componentes.

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.

Comparação entre asserções de página inteira e de elemento por tipo de página: páginas de entrada e de preços recebem verificação de página inteira, páginas de documentação e de aplicação recebem verificações de elementos em componentes compartilhados.

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.

Árvore de arquivos de uma pasta tests com uma pasta screenshots, uma subpasta por arquivo de especificação e baselines PNG nomeados por página e projeto, como home-chromium.png.

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.

  1. Aguarde um elemento pronto, como um título ou uma imagem carregada, antes da asserção.
  2. Use dados de teste fixos, em vez de contas reais.
  3. Defina uma viewport fixa na configuração.
  4. 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.

Comparação de cinco causas de instabilidade com o que a imagem de diferença mostra e a correção: fontes, datas relativas, imagens com carregamento lento, widgets de chat ou analytics e cursor de texto piscando.

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çãoSignificado
maxDiffPixelsNúmero de pixels que podem diferir.
maxDiffPixelRatioProporção de pixels que pode diferir, de 0 a 1.
thresholdQuanto 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.

Quatro estatísticas de uma captura: 5.120.000 pixels no total, 51.200 pixels permitidos com proporção 0,01, 576 pixels em um ícone de 24 por 24 pixels, 100 pixels permitidos com maxDiffPixels 100.

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',
    },
  },
});
Painel de configurações com os padrões de asserção de captura de tela: maxDiffPixels 100, threshold 0,2, animations desativado, caret oculto, scale css, página inteira definida por teste.

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.

Fluxo de cinco caixas: o teste falha com diferença, o revisor abre as imagens esperada, real e de diferença, decide se a mudança é intencional, atualiza apenas esse teste, faz commit do novo PNG junto com o código.

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.

Checklist de seis pontos de revisão: cada PNG alterado corresponde a uma mudança descrita, nenhuma página não relacionada foi alterada, os 3 projetos foram atualizados, as máscaras ainda cobrem os elementos voláteis, nenhuma imagem quebrada, quantidade de arquivos inalterada.

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:

  1. Os desenvolvedores executam a suíte visual dentro da imagem Docker, não diretamente no macOS.
  2. O CI executa a mesma imagem e envia o relatório HTML como artefato em caso de falha.
  3. As mudanças intencionais recebem novos baselines, com commit junto com a mudança de código.
  4. 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'"
Cinco passos numerados: verificar a versão do Playwright fixada, baixar a imagem Docker com a mesma tag de versão, executar a atualização de um teste com -g, abrir o novo PNG e compará-lo com o antigo, fazer commit do PNG junto com a mudança de código.

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/
Linha do tempo de uma execução de CI: checkout em 0:00, instalação em 0:15, build e início do servidor em 1:00, 60 asserções a partir de 2:30, envio do relatório em caso de falha em 5:10.

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.

Comparação entre testes visuais do Playwright e Temposhot Website Capture quanto à pergunta respondida, a quem executa, configuração, páginas com login, saída e local de execução.

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-snapshots em 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.

  1. Baixe o artefato do relatório e conte as asserções que falharam.
  2. Se todas as asserções de um projeto falharam, verifique primeiro se houve mudança de navegador ou de fonte nesse projeto.
  3. Se uma página falhou nos 3 projetos, abra a imagem real dela; a causa geralmente está na própria página.
  4. 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.
  5. Corrija a causa ou atualize apenas esse baseline, nunca os dois no mesmo commit.
Lista de faça e não faça para uma suíte visual: fixar a versão, gerar baselines no Docker, atualizar um teste por vez; não aumentar limites, não fazer asserção de páginas ao vivo, não misturar atualização de versão com funcionalidades.

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