playwright.config.ts define o timeout dos testes, contagem de retries, workers paralelos, comportamento de trace e baseURL para toda a suite. Os padrões gerados pelo npm init playwright@latest funcionam localmente, mas têm dois problemas no CI. workers: undefined causa contenção de recursos em runners compartilhados, e a ausência de forbidOnly: !!process.env.CI permite que um test.only() commitado pule silenciosamente a suite inteira.

O config mínimo

Após npm init playwright@latest, você recebe algo assim:

import { defineConfig, devices } from '@playwright/test';

export default defineConfig({
  testDir: './tests',
  fullyParallel: true,
  forbidOnly: !!process.env.CI,
  retries: process.env.CI ? 2 : 0,
  workers: process.env.CI ? 1 : undefined,
  reporter: 'html',
  use: {
    baseURL: 'http://localhost:3000',
    trace: 'on-first-retry',
  },
  projects: [
    { name: 'chromium', use: { ...devices['Desktop Chrome'] } },
    { name: 'firefox',  use: { ...devices['Desktop Firefox'] } },
    { name: 'webkit',   use: { ...devices['Desktop Safari'] } },
  ],
});

Vamos detalhar cada seção.

Opções de nível superior

testDir

Onde o Playwright procura arquivos de teste.

testDir: './tests',

Pode ser um caminho relativo ou absoluto. Arquivos que correspondem a /.spec.ts ou /.test.ts dentro dessa pasta são detectados automaticamente.

fullyParallel

fullyParallel: true,

Quando true: todos os testes rodam em paralelo entre workers, inclusive testes no mesmo arquivo.

Quando false (padrão anterior): testes dentro de um arquivo rodam em sequência, mas arquivos diferentes rodam em paralelo.

Para a maioria dos projetos: use true. Os testes devem ser independentes o suficiente para rodar em qualquer ordem.

forbidOnly

forbidOnly: !!process.env.CI,

Se um teste tiver test.only(), isso faz o run inteiro falhar. O padrão !!process.env.CI significa que só é enforçado no CI, então você pode usar test.only() localmente durante o debug, mas não pode commitar acidentalmente.

Sempre use isso em configs de produção.

retries

retries: process.env.CI ? 2 : 0,

Quantas vezes tentar um teste que falhou antes de marcá-lo como falha definitiva. No CI: tenta mais duas vezes (ajuda com testes flaky). Localmente: sem retries (você vê as falhas imediatamente).

Se um teste falha na primeira tentativa mas passa no retry, o Playwright o marca como "flaky" no relatório.

workers

workers: process.env.CI ? 1 : undefined,

Quantos workers paralelos (instâncias de navegador) rodar.

  • undefined (padrão): usa 50% dos núcleos de CPU
  • 1: roda todos os testes em sequência, útil no CI para evitar contenção de recursos
  • 4: roda 4 workers em paralelo

No desenvolvimento local com uma máquina rápida, undefined funciona bem. No CI, 1 é mais seguro a menos que você tenha um runner potente.

timeout

timeout: 30_000,  // 30 segundos por teste

Tempo máximo que um único teste pode rodar antes de ser marcado como falha. O padrão é 30 segundos. Para testes lentos ou máquinas de CI lentas, aumente para 60 segundos.

expect.timeout

expect: {
  timeout: 5_000,  // 5 segundos para cada assertion expect()
},

As assertions do Playwright fazem retry automático: continuam tentando até a condição ser atendida ou esse timeout expirar. O padrão é 5 segundos.

O bloco use (opções compartilhadas de teste)

use contém opções que se aplicam a todos os testes (a menos que sejam sobrescritas por um projeto).

use: {
  baseURL: 'http://localhost:3000',
  
  // Configurações do navegador
  headless: true,
  viewport: { width: 1280, height: 720 },
  
  // Gravação
  trace: 'on-first-retry',     // 'on-first-retry' | 'retain-on-failure' | 'always' | 'off'
  video: 'retain-on-failure',  // 'retain-on-failure' | 'always' | 'off'
  screenshot: 'only-on-failure',
  
  // Timeouts
  actionTimeout: 10_000,       // Timeout para cada ação (click, fill, etc.)
  navigationTimeout: 30_000,   // Timeout para page.goto() e page.waitForURL()
  
  // HTTP
  ignoreHTTPSErrors: true,     // Útil para ambientes de staging com certs autoassinados
  
  // Locale
  locale: 'pt-BR',
  timezoneId: 'America/Sao_Paulo',
},

baseURL

Quando definida, page.goto('/login') resolve para http://localhost:3000/login. Torna os testes portáveis entre ambientes.

Use variáveis de ambiente:

baseURL: process.env.BASE_URL || 'http://localhost:3000',

trace

Os traces do Playwright capturam uma gravação completa de cada ação (snapshot do DOM, screenshots, chamadas de rede) para debug. Opções:

  • 'on-first-retry' — captura no primeiro retry de um teste que falhou (recomendado)
  • 'retain-on-failure' — captura em qualquer teste que falhou (mais armazenamento)
  • 'always' — captura tudo (muito armazenamento, mais lento)
  • 'off' — sem traces

Para visualizar: npx playwright show-trace trace.zip.

video

  • 'retain-on-failure' — salva vídeo só para testes que falharam (recomendado)
  • 'always' — salva vídeo de tudo
  • 'off' — sem vídeo

projects — Múltiplas configurações de navegador

Projects permitem rodar os mesmos testes em navegadores, viewports ou configurações diferentes.

projects: [
  // Navegadores desktop
  {
    name: 'chromium',
    use: { ...devices['Desktop Chrome'] },
  },
  {
    name: 'firefox',
    use: { ...devices['Desktop Firefox'] },
  },
  {
    name: 'webkit',
    use: { ...devices['Desktop Safari'] },
  },
  
  // Mobile
  {
    name: 'mobile-chrome',
    use: { ...devices['Pixel 7'] },
  },
  {
    name: 'mobile-safari',
    use: { ...devices['iPhone 14'] },
  },
],

devices['Desktop Chrome'] é um preset que define viewport, user agent e outros padrões específicos do navegador. Lista completa: npx playwright show-report ou a documentação do Playwright.

Projects para ambientes diferentes

projects: [
  {
    name: 'staging',
    use: {
      ...devices['Desktop Chrome'],
      baseURL: 'https://staging.myapp.com',
    },
    testMatch: '**/*.spec.ts',
  },
  {
    name: 'production',
    use: {
      ...devices['Desktop Chrome'],
      baseURL: 'https://myapp.com',
    },
    testMatch: '**/smoke/*.spec.ts',  // Só smoke tests em produção
  },
],

Setup projects (setup global)

projects: [
  {
    name: 'setup',
    testMatch: /.*\.setup\.ts/,  // Arquivos como auth.setup.ts
  },
  {
    name: 'chromium',
    use: { ...devices['Desktop Chrome'] },
    dependencies: ['setup'],  // Roda o setup primeiro
  },
],

Usado para rodar o setup de autenticação uma vez antes de todos os testes e reutilizar o estado de auth salvo.

reporter

Em que formato gerar os resultados dos testes:

reporter: [
  ['html'],                                // Relatório HTML em playwright-report/
  ['junit', { outputFile: 'results.xml' }], // JUnit XML para CI
  ['list'],                                 // Output ao vivo no console
],

Para desenvolvimento local: 'html' é ótimo. Abra playwright-report/index.html para um relatório visual completo.

Para CI: adicione 'junit' para que ferramentas de CI (GitHub Actions, GitLab CI, Jenkins) consigam parsear os resultados.

globalSetup e globalTeardown

Para setup que roda uma vez antes de toda a suite de testes começar:

globalSetup: './global-setup.ts',
globalTeardown: './global-teardown.ts',

// global-setup.ts
import { chromium } from '@playwright/test';

export default async function globalSetup() {
  const browser = await chromium.launch();
  const page = await browser.newPage();
  
  // Faz login e salva o estado de auth
  await page.goto('http://localhost:3000/login');
  await page.fill('[data-testid="email"]', 'admin@test.com');
  await page.fill('[data-testid="password"]', 'AdminPass1');
  await page.click('[data-testid="submit"]');
  await page.context().storageState({ path: 'auth.json' });
  
  await browser.close();
}

Os testes passam a usar o estado de auth salvo sem precisar logar novamente:

use: {
  storageState: 'auth.json',
},

Um config pronto para produção

import { defineConfig, devices } from '@playwright/test';
import dotenv from 'dotenv';

dotenv.config();

export default defineConfig({
  testDir: './tests',
  fullyParallel: true,
  forbidOnly: !!process.env.CI,
  retries: process.env.CI ? 2 : 0,
  workers: process.env.CI ? 2 : undefined,
  
  timeout: 45_000,
  expect: { timeout: 8_000 },
  
  reporter: [
    ['html'],
    ...(process.env.CI ? [['junit', { outputFile: 'test-results/results.xml' }] as const] : []),
  ],
  
  use: {
    baseURL: process.env.BASE_URL || 'http://localhost:3000',
    trace: 'on-first-retry',
    video: 'retain-on-failure',
    screenshot: 'only-on-failure',
    actionTimeout: 10_000,
    navigationTimeout: 30_000,
  },
  
  projects: [
    {
      name: 'setup',
      testMatch: /auth\.setup\.ts/,
    },
    {
      name: 'chromium',
      use: {
        ...devices['Desktop Chrome'],
        storageState: 'playwright/.auth/user.json',
      },
      dependencies: ['setup'],
    },
    {
      name: 'api',
      testMatch: '**/api/**/*.spec.ts',
      use: { storageState: undefined },  // Testes de API não precisam de auth de navegador
    },
  ],
});

Rodando projetos específicos

# Todos os testes no chromium
npx playwright test --project=chromium

# Só o projeto de setup
npx playwright test --project=setup

# Múltiplos projetos
npx playwright test --project=chromium --project=firefox

Resumo das opções mais importantes

| Opção | Recomendação |

|--------|--------------|

| testDir | './tests' |

| fullyParallel | true |

| forbidOnly | !!process.env.CI |

| retries | CI ? 2 : 0 |

| timeout | 30_000–60_000 |

| trace | 'on-first-retry' |

| video | 'retain-on-failure' |

| baseURL | A partir de variável de ambiente |

| reporter | HTML localmente, + JUnit no CI |

Vale dedicar 30 minutos para acertar o config no início do projeto. Um playwright.config.ts bem configurado economiza horas de debug e troubleshooting no CI.

→ Veja também: Instalando Playwright: Guia de Configuração Passo a Passo (2026) | Configuração de Ambiente no Playwright: Local, Staging e Produção | Execução Paralela no Playwright: Workers, Shards e Sharding para Velocidade | Variáveis de Ambiente em Testes Playwright: Um Guia Completo