O Selenium exige gerenciamento separado de drivers de navegador e waits explícitos em quase toda interação. O Cypress não roda cross-browser e coloca a execução paralela atrás de um plano pago. O Playwright resolve tudo isso de forma nativa: um único comando de instalação baixa Chromium, Firefox e WebKit. O auto-waiting significa que os elementos não precisam de chamadas manuais de sleep() antes das interações.

Por que Playwright e não outra coisa

Antes de instalar qualquer coisa, a versão curta: o Playwright é o que a maioria dos projetos novos escolhe em 2026, e com boas razões.

O Selenium foi o padrão por anos. Funciona, mas é lento de configurar, verboso de escrever e exige gerenciamento manual de drivers de navegador. O Cypress é mais fácil mas só suporta navegadores baseados em Chromium e tem paywall para execuções paralelas no CI.

O Playwright lida com apps web modernos nativamente. Ele faz auto-wait para os elementos estarem prontos antes de interagir com eles, então você não precisa de chamadas aleatórias de sleep() nos testes. Suporta Chromium, Firefox e WebKit. Tem um test runner integrado, gravação de vídeo de falhas e um gerador de código que escreve locators para você enquanto você clica na página.

Se você está começando do zero em 2026, o Playwright é a escolha certa.

Instalação

Você precisa do Node.js instalado primeiro. Verifique se você tem:

node --version

Se retornar um número de versão (v18 ou superior está bom), pode continuar. Se não, baixe a versão LTS em nodejs.org.

Agora crie uma nova pasta para o projeto e rode o instalador do Playwright:

mkdir my-first-tests
cd my-first-tests
npm init playwright@latest

O instalador faz algumas perguntas. Para aprendizado, aceite os padrões:

  • TypeScript ou JavaScript → TypeScript
  • Pasta de testes → tests
  • Workflow do GitHub Actions → no (adicione depois)
  • Instalar navegadores do Playwright → yes

O download dos navegadores leva um ou dois minutos. Quando terminar, o projeto fica assim:

my-first-tests/
  tests/
    example.spec.ts
  playwright.config.ts
  package.json

É isso. Sem arquivos de configuração para batalhar, sem gerenciadores de driver, sem test runner separado para instalar.

Rode o teste de exemplo

Antes de escrever qualquer coisa, rode o que veio com a instalação:

npx playwright test

O Playwright roda os testes de exemplo em modo headless (sem janela de navegador visível) e imprime os resultados no terminal. Você verá algo como:

Running 2 tests using 2 workers
  2 passed (3s)

Para ver o que realmente aconteceu, abra o relatório HTML:

npx playwright show-report

Isso abre um navegador com um relatório detalhado de cada teste: quais passos rodaram, quanto tempo cada um levou e screenshots no final.

Rode npx playwright test --headed para assistir o navegador abrir e executar o teste em tempo real. Útil quando você está começando e quer ver o que está acontecendo.

Escreva seu primeiro teste

Delete o arquivo de teste de exemplo e crie um novo. Abra tests/login.spec.ts e escreva isso:

import { test, expect } from '@playwright/test';

test('user can log in', async ({ page }) => {
  await page.goto('https://lab.becomeqa.com');
  await page.getByRole('button', { name: 'Login' }).click();
  await page.getByLabel('Username').fill('admin@becomeqa.com');
  await page.getByLabel('Password').fill('testpass123');
  await page.getByRole('button', { name: 'Submit' }).click();

  await expect(page.getByText('My Travel Items')).toBeVisible();
});

Rode:

npx playwright test tests/login.spec.ts

Você deve ver 1 passed. Aqui está o que cada parte faz.

Entendendo o que você acabou de escrever

test('user can log in', async ({ page }) => { test é a função que define um caso de teste. O primeiro argumento é o nome: escreva como uma frase descrevendo o que o usuário faz, não o que o código faz. async significa que a função é assíncrona, necessário porque toda ação de navegador leva tempo. { page } é a fixture de página do Playwright, que dá uma aba de navegador nova para trabalhar. await page.goto('https://lab.becomeqa.com')

Abre a URL. await diz ao JavaScript para esperar essa ação terminar antes de ir para a próxima linha. Você vai usar await na frente de toda ação do Playwright.

page.getByRole('button', { name: 'Login' })

Esse é um locator. Ele encontra um elemento na página. getByRole encontra elementos pelo seu papel ARIA e nome acessível. Encontra um botão com o texto "Login". É o tipo de locator mais confiável porque está vinculado a como o elemento realmente funciona, não à sua classe CSS ou posição.

await page.getByLabel('Username').fill('admin@becomeqa.com')

Encontra o campo de input associado a um label "Username" e digita o email nele.

await expect(page.getByText('My Travel Items')).toBeVisible()

Essa é a assertion, a parte que realmente verifica algo. Confirma que o texto "My Travel Items" está visível na página após o login. Se o login falhar e esse texto não aparecer, o teste falha.

Locators: como encontrar elementos

Locators são como você diz ao Playwright com qual elemento interagir. São vários tipos, cada um com um caso de uso diferente.

getByRole é a escolha preferida. Usa papéis ARIA, que descrevem o significado semântico dos elementos:

page.getByRole('button', { name: 'Submit' })   // um botão com label Submit
page.getByRole('link', { name: 'Home' })        // um link com label Home
page.getByRole('textbox', { name: 'Search' })   // um input de texto com label Search

getByLabel funciona para inputs de formulário que têm um label visível:

page.getByLabel('Email address')
page.getByLabel('Password')

getByPlaceholder quando não há label mas há texto de placeholder:

page.getByPlaceholder('Enter your email')

getByText encontra elementos pelo conteúdo de texto visível:

page.getByText('My Travel Items')
page.getByText('Sign out')

getByTestId usa um atributo data-testid. Útil quando desenvolvedores os adicionam especificamente para testes:

page.getByTestId('submit-button')

Evite locators baseados em classes CSS ou XPath. Eles quebram sempre que um desenvolvedor renomeia uma classe ou reestrutura o HTML. Locators baseados em papel sobrevivem a essas mudanças.

Actions: interagindo com a página

Com um locator em mãos, você pode realizar ações nele:

await locator.click()                    // clicar em um elemento
await locator.fill('algum texto')        // limpar e digitar em um input
await locator.type('algum texto')        // digitar caractere por caractere (para inputs que reagem a teclas)
await locator.check()                    // marcar um checkbox
await locator.selectOption('value')      // selecionar de um dropdown
await locator.hover()                    // passar o mouse sobre um elemento
await locator.press('Enter')             // pressionar uma tecla do teclado

Uma coisa importante: você quase nunca precisa esperar pelos elementos manualmente. O Playwright espera automaticamente que um elemento esteja visível e habilitado antes de realizar uma ação. Isso se chama auto-waiting e é o que elimina a maioria da flakiness nos testes Playwright.

Assertions: verificando o resultado

Uma assertion é o que faz um teste realmente testar algo. Sem assertions, você está apenas clicando em uma página.

// Verificar visibilidade
await expect(page.getByText('Welcome')).toBeVisible();
await expect(page.getByRole('dialog')).toBeHidden();

// Verificar conteúdo de texto
await expect(page.getByRole('heading')).toHaveText('My Travel Items');
await expect(page.getByRole('heading')).toContainText('Travel');

// Verificar valores de input
await expect(page.getByLabel('Email')).toHaveValue('test@example.com');

// Verificar URL
await expect(page).toHaveURL('https://lab.becomeqa.com/dashboard');

// Verificar título da página
await expect(page).toHaveTitle('BecomeQA Lab');

// Verificar contagem de elementos
await expect(page.getByRole('row')).toHaveCount(5);

As assertions do Playwright também fazem auto-wait. toBeVisible() não apenas verifica agora. Tenta novamente por até 5 segundos por padrão, esperando o elemento aparecer. Isso significa que você também não precisa de waits explícitos antes das assertions.

Um teste mais completo

Aqui está um segundo teste que continua após o login:

import { test, expect } from '@playwright/test';

test('user can log in', async ({ page }) => {
  await page.goto('https://lab.becomeqa.com');
  await page.getByRole('button', { name: 'Login' }).click();
  await page.getByLabel('Username').fill('admin@becomeqa.com');
  await page.getByLabel('Password').fill('testpass123');
  await page.getByRole('button', { name: 'Submit' }).click();

  await expect(page.getByText('My Travel Items')).toBeVisible();
});

test('travel items table has data', async ({ page }) => {
  // fazer login primeiro
  await page.goto('https://lab.becomeqa.com');
  await page.getByRole('button', { name: 'Login' }).click();
  await page.getByLabel('Username').fill('admin@becomeqa.com');
  await page.getByLabel('Password').fill('testpass123');
  await page.getByRole('button', { name: 'Submit' }).click();

  // verificar a tabela
  await expect(page.getByRole('table')).toBeVisible();
  const rows = page.getByRole('row');
  await expect(rows).toHaveCount(6); // cabeçalho + 5 linhas de dados
});

Rode os dois testes:

npx playwright test tests/login.spec.ts

Você vai notar que os passos de login estão duplicados entre os testes. Tudo bem por enquanto. Com mais testes, você vai mover o setup repetido para um bloco beforeEach ou uma fixture, mas esse é um tópico intermediário.

Cada teste no Playwright roda em um contexto de navegador completamente isolado por padrão. Mesmo que um teste faça login, o próximo começa do zero. Isso é intencional, porque testes que compartilham estado são muito mais difíceis de debugar.

O arquivo playwright.config.ts

Abra playwright.config.ts. Os padrões funcionam bem, mas duas configurações valem conhecer:

export default defineConfig({
  testDir: './tests',
  timeout: 30000,          // tempo máximo por teste em ms
  retries: 0,              // quantas vezes tentar novamente um teste que falhou
  use: {
    baseURL: 'https://lab.becomeqa.com',   // adicione isso
    trace: 'on-first-retry',
  },
  projects: [
    { name: 'chromium', use: { ...devices['Desktop Chrome'] } },
    { name: 'firefox',  use: { ...devices['Desktop Firefox'] } },
    { name: 'webkit',   use: { ...devices['Desktop Safari'] } },
  ],
});

Se você definir baseURL, pode escrever page.goto('/') em vez da URL completa nos testes. Adicione ao config e atualize os testes:

await page.goto('/');   // em vez de 'https://lab.becomeqa.com'

Por padrão o Playwright roda os testes nos três navegadores (Chromium, Firefox, WebKit). Para rodar apenas no Chromium enquanto aprende:

npx playwright test --project=chromium

Quando um teste falha

Vamos quebrar nosso teste de propósito. Mude o texto esperado para algo que não existe:

await expect(page.getByText('This text does not exist')).toBeVisible();

Rode e você verá:

1 failed
  login.spec.ts:10:3 › user can log in ✗

    Error: expect(locator).toBeVisible()
    Locator: getByText('This text does not exist')
    Expected: visible
    Received: hidden
    Timeout: 5000ms

O erro diz exatamente o que estava esperando e que nunca ficou visível. Agora verifique o trace:

npx playwright show-report

Clique no teste que falhou. Você verá um detalhamento passo a passo com screenshots em cada ação. Esse é o trace viewer, uma das features mais úteis do Playwright para debugar falhas.

→ Veja também: Instalando Playwright: Guia de Configuração Passo a Passo (2026) | Locators do Playwright: getByRole, getByLabel, getByText, getByTestId Comparados | Assertions no Playwright: O Guia Completo | Playwright Trace Viewer: Depure Testes com Falha Como um Profissional

Corrija o teste de volta para 'My Travel Items' antes de continuar.

FAQ

Preciso saber TypeScript antes de começar?

Não. O TypeScript que você vai escrever para testes é mínimo, basicamente async/await e tipos básicos. O curso cobre exatamente o que você precisa conforme avança.

Devo usar JavaScript em vez de TypeScript?

TypeScript é a melhor escolha mesmo que você seja novo nele. Ele pega erros antes de você rodar o teste. A sintaxe extra é pequena e os benefícios são imediatos.

Como encontro o locator certo para um elemento?

Use a ferramenta codegen do Playwright: npx playwright codegen https://lab.becomeqa.com. Abre um navegador, grava seus cliques e gera o código do locator automaticamente. Use para explorar, depois limpe o que ele gerou.

Meu teste passa localmente mas falha no GitHub Actions. Por quê?

Geralmente é um problema de timing ou um await faltando. Rode com --headed localmente para ver o que acontece, depois verifique se você esqueceu algum await.