O { page, request } em toda função de teste do Playwright é destructuring: ele extrai essas propriedades do objeto de fixture que o framework passa automaticamente. Quando isso fica claro, fixtures customizadas deixam de parecer mágica e começam a parecer o mesmo padrão repetido.

O que é um objeto?

Um objeto é uma coleção de pares chave-valor. Chaves são strings; valores podem ser qualquer coisa:

const usuario = {
  id: 1,
  email: 'alice@exemplo.com',
  role: 'admin',
  ativo: true,
};

Acesse valores com notação de ponto ou de colchetes:

console.log(usuario.email);      // 'alice@exemplo.com'
console.log(usuario['role']);    // 'admin'
console.log(usuario.id);        // 1

Modifique valores da mesma forma:

usuario.email = 'novaalice@exemplo.com';
usuario.role = 'member';

Objetos em dados de teste

A maioria dos dados de teste em testes Playwright é expressa como objetos ou arrays de objetos:

const credenciaisLogin = {
  email: 'qa_teste@exemplo.com',
  password: 'SenhaValida123!',
};

await page.fill('[data-testid="email"]', credenciaisLogin.email);
await page.fill('[data-testid="password"]', credenciaisLogin.password);

Coleções de casos de teste:

const EMAILS_INVALIDOS = [
  { input: '',              descricao: 'vazio' },
  { input: 'nao-e-email',  descricao: 'sem @' },
  { input: 'faltando@',    descricao: 'sem domínio' },
  { input: 'a@b',          descricao: 'sem TLD' },
];

Cada EMAILS_INVALIDOS[0] é um objeto com as propriedades input e descricao.

Destructuring: o que é

Destructuring permite extrair valores de um objeto (ou array) para variáveis individuais em uma linha, em vez de múltiplas linhas.

Sem destructuring:

const usuario = { id: 1, nome: 'Alice', email: 'alice@teste.com', role: 'admin' };

const id    = usuario.id;
const nome  = usuario.nome;
const email = usuario.email;
const role  = usuario.role;

Com destructuring:

const { id, nome, email, role } = usuario;

Mesmo resultado: quatro variáveis com os mesmos valores, em uma linha em vez de quatro.

Renomear durante o destructuring

Se você quer que a variável tenha um nome diferente da chave:

const config = {
  database_host: 'localhost',
  database_port: 5432,
};

const { database_host: host, database_port: porta } = config;

console.log(host);  // 'localhost'
console.log(porta); // 5432

Valores padrão no destructuring

Se uma chave pode não existir, você pode fornecer um padrão:

const produto = { nome: 'Notebook', preco: 999 };

const { nome, preco, desconto = 0 } = produto;
// desconto = 0 (não estava em produto, usa o padrão)

Destructuring em parâmetros de função

O lugar mais comum onde você vai ver destructuring no Playwright é nas funções de teste:

// Sem destructuring
test('usuário consegue fazer login', async (args) => {
  const page = args.page;
  const request = args.request;
  // ...
});

// Com destructuring (padrão padrão do Playwright)
test('usuário consegue fazer login', async ({ page, request }) => {
  // page e request disponíveis diretamente
});

Isso é destructuring de objeto no parâmetro da função. A sintaxe { page, request } extrai essas propriedades do objeto de fixture que o Playwright passa.

É por isso que o código de testes Playwright parece ter "variáveis mágicas": elas estão sendo extraídas por destructuring do objeto de fixture automaticamente.

Fixtures customizadas: destructuring na prática

Quando você cria fixtures customizadas, vai escrever esse padrão:

export const test = base.extend<{ loginPage: LoginPage }>({
  loginPage: async ({ page }, use) => {
    const loginPage = new LoginPage(page);
    await use(loginPage);
  },
});

// Então em um teste:
test('login funciona', async ({ page, loginPage }) => {
  //                           ^^^ desestruturado do objeto de fixture
  await loginPage.login('user@teste.com', 'senha');
});

Entender destructuring faz as fixtures fazerem sentido.

Destructuring aninhado

Objetos podem conter outros objetos. Você pode desestruturar múltiplos níveis de uma vez:

const respostaApi = {
  status: 200,
  data: {
    usuario: {
      id: 123,
      email: 'alice@teste.com',
    },
    token: 'eyJhbGciOiJIUzI1NiJ9...',
  },
};

// Destructuring aninhado
const { status, data: { usuario: { id, email }, token } } = respostaApi;

console.log(status); // 200
console.log(id);     // 123
console.log(email);  // 'alice@teste.com'
console.log(token);  // 'eyJhbGciOiJIUzI1NiJ9...'

Parece complexo no início. Na prática, você raramente vai mais de 2 níveis de profundidade. Se ficar complicado, faça destructuring em etapas:

const { data } = respostaApi;
const { usuario, token } = data;
const { id, email } = usuario;

Mesmo resultado, mais fácil de ler.

Destructuring de array (resumo)

Arrays usam [] em vez de {}:

const coordenadas = [-23.5505, -46.6333];

const [latitude, longitude] = coordenadas;
// latitude = -23.5505, longitude = -46.6333

Pule elementos com vírgulas:

const [primeiro, , terceiro] = ['a', 'b', 'c'];
// primeiro = 'a', terceiro = 'c'

O operador spread com objetos

O operador spread (...) copia todas as propriedades de um objeto para outro:

const usuarioBase = {
  role: 'member',
  ativo: true,
};

const usuarioAdmin = {
  ...usuarioBase,      // copia role e ativo
  email: 'admin@teste.com',
  role: 'admin',       // sobrescreve o valor do spread
};

// { role: 'admin', ativo: true, email: 'admin@teste.com' }

Em dados de teste: criando variantes

const usuarioPadrao = {
  email: 'teste@exemplo.com',
  password: 'SenhaValida1',
  role: 'member',
  ativo: true,
};

const usuarioAdmin    = { ...usuarioPadrao, role: 'admin' };
const usuarioInativo  = { ...usuarioPadrao, ativo: false };
const emailCustomizado = { ...usuarioPadrao, email: 'custom@exemplo.com' };

Esse padrão, "objeto base + overrides", é muito comum em factories de dados de teste. Você define o estado válido padrão uma vez e usa spread para criar variantes.

Padrões práticos em testes Playwright

Destructuring do corpo da resposta de API

const response = await request.post('/api/users', {
  data: { email: 'novo@teste.com', password: 'SenhaValida1' },
});

const { id, email, role, created_at } = await response.json();

expect(id).toBeTruthy();
expect(email).toBe('novo@teste.com');
expect(role).toBe('member');

Passando parâmetros de objeto para helpers

async function preencherFormularioLogin(page: Page, { email, password }: { email: string; password: string }) {
  await page.fill('[data-testid="email"]', email);
  await page.fill('[data-testid="password"]', password);
}

// No ponto de chamada:
await preencherFormularioLogin(page, { email: 'user@teste.com', password: 'senha' });

Mesclando configuração de teste

const configBase = {
  baseURL: 'https://lab.becomeqa.com',
  timeout: 30000,
};

const configCi = {
  ...configBase,
  timeout: 60000,             // mais lento no CI
  video: 'retain-on-failure',
};

Erros comuns

Destructuring de undefined:

const response = await fetch('/api/usuario');
const { id } = await response.json(); // Se a resposta for null/undefined, isso lança erro

Sempre verifique se o valor existe antes de fazer destructuring de objetos profundamente aninhados.

Modificar cópias com spread não afeta o original... mas em objetos aninhados sim:

const original = { nome: 'Alice', dados: { pontuacao: 100 } };
const copia = { ...original };
copia.nome = 'Bob';               // original.nome continua 'Alice'
copia.dados.pontuacao = 200;      // original.dados.pontuacao também muda!

O spread é uma cópia rasa. Objetos aninhados ainda são referências compartilhadas.

Resumo

| Sintaxe | O que faz |

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

| const { a, b } = obj | Extrai a e b de obj |

| const { a: x } = obj | Extrai a de obj, nomeia como x |

| const { a = 5 } = obj | Extrai a, padrão 5 se ausente |

| async ({ page, request }) => {} | Desestrutura fixtures do Playwright |

| { ...obj, chave: 'valor' } | Faz spread de obj, sobrescreve ou adiciona chave |

Objetos e destructuring estão em todo lugar no JavaScript e TypeScript modernos. Quando os padrões ficam familiares, o código de testes Playwright fica muito mais fácil de ler e escrever.

→ Veja também: JavaScript para QA Engineers: O Mínimo para Começar a Automatizar | Arrays JavaScript: map, filter, find e forEach — O Guia de Campo do QA | TypeScript para QA: Por que os Tipos Estáticos Melhoram Seus Testes