O Playwright tem uma API direta para cookies via objeto context, mas o localStorage exige page.evaluate(() => localStorage.getItem('key')) porque não existe um método no estilo de locator para storage. Os dois importam para testes de autenticação: storageState captura cookies e localStorage juntos em um arquivo para que cada teste possa começar já logado sem tocar na UI de login.

Lendo cookies

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

test('login define cookie de sessão', async ({ page, context }) => {
  await page.goto('/login');
  await page.getByLabel('Email').fill('user@example.com');
  await page.getByLabel('Password').fill('password123');
  await page.getByRole('button', { name: 'Log in' }).click();

  await expect(page).toHaveURL('/dashboard');

  // Lê todos os cookies da página atual
  const cookies = await context.cookies();
  const sessionCookie = cookies.find(c => c.name === 'session_token');

  expect(sessionCookie).toBeDefined();
  expect(sessionCookie!.httpOnly).toBe(true); // Não pode ser lido por JS — bom
  expect(sessionCookie!.secure).toBe(true);   // Só enviado via HTTPS
  expect(sessionCookie!.sameSite).toBe('Strict');
});

context.cookies() retorna todos os cookies de todas as páginas no contexto atual. Filtre por name, domain ou path conforme necessário.

Definindo cookies

Pré-popule cookies antes de navegar: útil para testar estados autenticados sem passar pelo fluxo de login.

test('usuário autenticado vê o dashboard', async ({ page, context }) => {
  // Define o cookie de auth diretamente — pula a UI de login
  await context.addCookies([{
    name: 'session_token',
    value: 'valid-test-token-abc123',
    domain: 'localhost',
    path: '/',
    httpOnly: true,
    secure: false, // false para localhost
    sameSite: 'Lax',
  }]);

  await page.goto('/dashboard');
  await expect(page.getByRole('heading', { name: 'Dashboard' })).toBeVisible();
});

Para setup de autenticação de qualidade em produção, use storageState — captura cookies E localStorage em um único arquivo. A injeção manual de cookies é útil quando você precisa de controle preciso sobre atributos individuais do cookie.

Limpando cookies

// Limpa todos os cookies do contexto
await context.clearCookies();

// Para limpar um cookie específico, a maioria dos apps tem um endpoint /logout
// que limpa o cookie de sessão no servidor
await page.goto('/logout');

test('sessão expirada redireciona para login', async ({ page, context }) => {
  // Define um cookie expirado
  const ontem = new Date(Date.now() - 86400000);
  await context.addCookies([{
    name: 'session_token',
    value: 'expired-token',
    domain: 'localhost',
    path: '/',
    expires: ontem.getTime() / 1000, // Unix timestamp em segundos
  }]);

  await page.goto('/dashboard');
  await expect(page).toHaveURL('/login');
});

Trabalhando com localStorage

O Playwright não tem uma API direta para localStorage: você acessa via page.evaluate().

// Ler localStorage
const theme = await page.evaluate(() => localStorage.getItem('theme'));
expect(theme).toBe('dark');

// Definir localStorage antes da interação com a página
await page.evaluate(() => {
  localStorage.setItem('theme', 'dark');
  localStorage.setItem('user_preferences', JSON.stringify({ fontSize: 'large' }));
});

// Limpar uma chave específica
await page.evaluate(() => localStorage.removeItem('theme'));

// Limpar todo o localStorage
await page.evaluate(() => localStorage.clear());

Testando que o localStorage persiste preferências

test('preferência de modo escuro persiste após reload', async ({ page }) => {
  await page.goto('/settings');
  await page.getByRole('switch', { name: 'Dark mode' }).click();

  // Verifica se foi armazenado
  const stored = await page.evaluate(() => localStorage.getItem('theme'));
  expect(stored).toBe('dark');

  // Recarrega a página
  await page.reload();

  // Verifica se a preferência foi restaurada do localStorage
  await expect(page.locator('html')).toHaveAttribute('data-theme', 'dark');
});

Pré-populando localStorage

test('usuário com preferência de fonte grande vê texto maior', async ({ page }) => {
  await page.goto('/'); // Navega primeiro para definir o domínio

  await page.evaluate(() => {
    localStorage.setItem('fontSize', 'large');
  });

  await page.reload(); // Recarrega para o app ler a preferência

  const body = await page.locator('body');
  await expect(body).toHaveCSS('font-size', '18px');
});

sessionStorage

Mesma API do localStorage, escopo diferente: o sessionStorage é limpo quando a aba fecha.

// Ler sessionStorage
const cartItems = await page.evaluate(() => sessionStorage.getItem('cart'));

// Definir sessionStorage
await page.evaluate(() => {
  sessionStorage.setItem('cart', JSON.stringify([{ id: 1, qty: 2 }]));
});

Salvando e restaurando o estado completo do navegador

Para testes de autenticação em escala, capture todo o estado de storage (cookies + localStorage) em um arquivo:

// global-setup.ts — roda uma vez antes de todos os testes
import { chromium } from '@playwright/test';

async function globalSetup() {
  const browser = await chromium.launch();
  const page = await browser.newPage();

  await page.goto('http://localhost:3000/login');
  await page.getByLabel('Email').fill('testuser@example.com');
  await page.getByLabel('Password').fill('password123');
  await page.getByRole('button', { name: 'Log in' }).click();
  await page.waitForURL('/dashboard');

  // Salva cookies + localStorage em arquivo
  await page.context().storageState({ path: 'playwright/.auth/user.json' });
  await browser.close();
}

export default globalSetup;

Depois reutilize nos testes sem precisar logar:

// playwright.config.ts
projects: [{
  name: 'authenticated',
  use: { storageState: 'playwright/.auth/user.json' },
}],

Esse é o padrão recomendado para suites de testes autenticados: mais rápido, mais confiável, e não depende da UI de login estar funcionando.

→ Veja também: Autenticação no Playwright com storageState (Sem Login em Cada Teste) | Isolamento de Testes: Por que Cada Teste Playwright Deve Ser sem Estado | Configuração e Limpeza Global no Playwright