Playwright tiene una API directa para cookies a través del objeto context, pero localStorage requiere page.evaluate(() => localStorage.getItem('key')) porque no existe un método equivalente al estilo de locators para el almacenamiento. Ambos importan para el testing de autenticación: storageState captura cookies y localStorage juntos en un archivo para que cada test pueda empezar ya con sesión iniciada sin tocar la UI de login. Este artículo cubre lectura, escritura y limpieza de cookies, pre-carga de localStorage, testeo del comportamiento de expiración de cookies, y el patrón de global setup que genera storageState una sola vez y lo reutiliza en toda la suite.
Leer cookies
import { test, expect } from '@playwright/test';
test('el login establece la cookie de sesión', async ({ page, context }) => {
await page.goto('/login');
await page.getByLabel('Email').fill('usuario@ejemplo.com');
await page.getByLabel('Password').fill('contraseña123');
await page.getByRole('button', { name: 'Iniciar sesión' }).click();
await expect(page).toHaveURL('/dashboard');
// Leer todas las cookies de la página actual
const cookies = await context.cookies();
const sessionCookie = cookies.find(c => c.name === 'session_token');
expect(sessionCookie).toBeDefined();
expect(sessionCookie!.httpOnly).toBe(true); // No puede ser leída por JS: bien
expect(sessionCookie!.secure).toBe(true); // Solo se envía por HTTPS
expect(sessionCookie!.sameSite).toBe('Strict');
});context.cookies() devuelve todas las cookies de todas las páginas en el contexto actual. Filtra por name, domain o path según necesites.
Establecer cookies
Pre-carga cookies antes de navegar: útil para testear estados autenticados sin pasar por el flujo de login:
test('el usuario autenticado ve el dashboard', async ({ page, context }) => {
// Establecer la cookie de auth directamente, sin la 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();
});storageState en su lugar: captura cookies Y localStorage en un solo archivo. La inyección manual de cookies es útil cuando necesitas control preciso sobre atributos individuales de una cookie.Limpiar cookies
// Limpiar todas las cookies del contexto
await context.clearCookies();
// Limpiar una cookie específica (navegando a un endpoint de cierre de sesión
// o usando CDP). La mayoría de las apps tienen un endpoint /logout que limpia
// la cookie de sesión en el servidor.
await page.goto('/logout');Testear el comportamiento de expiración de cookies
test('la sesión expirada redirige al login', async ({ page, context }) => {
// Establecer una cookie expirada
const ayer = new Date(Date.now() - 86400000);
await context.addCookies([{
name: 'session_token',
value: 'token-expirado',
domain: 'localhost',
path: '/',
expires: ayer.getTime() / 1000, // Timestamp Unix en segundos
}]);
await page.goto('/dashboard');
await expect(page).toHaveURL('/login');
});Trabajar con localStorage
Playwright no tiene una API directa para localStorage: se accede a través de page.evaluate():
// Leer localStorage
const tema = await page.evaluate(() => localStorage.getItem('theme'));
expect(tema).toBe('dark');
// Establecer localStorage antes de la interacción con la página
await page.evaluate(() => {
localStorage.setItem('theme', 'dark');
localStorage.setItem('user_preferences', JSON.stringify({ fontSize: 'large' }));
});
// Limpiar una clave específica
await page.evaluate(() => localStorage.removeItem('theme'));
// Limpiar todo localStorage
await page.evaluate(() => localStorage.clear());Testear que localStorage persiste preferencias
test('la preferencia de modo oscuro persiste después de recargar', async ({ page }) => {
await page.goto('/settings');
await page.getByRole('switch', { name: 'Modo oscuro' }).click();
// Verificar que se almacenó
const almacenado = await page.evaluate(() => localStorage.getItem('theme'));
expect(almacenado).toBe('dark');
// Recargar la página
await page.reload();
// Verificar que la preferencia se restauró desde localStorage
await expect(page.locator('html')).toHaveAttribute('data-theme', 'dark');
});Pre-cargar localStorage
test('el usuario con preferencia de fuente grande ve texto más grande', async ({ page }) => {
await page.goto('/'); // Navegar primero para establecer el dominio
await page.evaluate(() => {
localStorage.setItem('fontSize', 'large');
});
await page.reload(); // Recargar para que la app lea la preferencia
const body = await page.locator('body');
await expect(body).toHaveCSS('font-size', '18px');
});sessionStorage
Misma API que localStorage, diferente alcance: sessionStorage se limpia cuando se cierra la pestaña:
// Leer sessionStorage
const itemsCarrito = await page.evaluate(() => sessionStorage.getItem('cart'));
// Establecer sessionStorage
await page.evaluate(() => {
sessionStorage.setItem('cart', JSON.stringify([{ id: 1, qty: 2 }]));
});Guardar y restaurar el estado completo del navegador
Para testing de auth a escala, capturá el estado completo del almacenamiento (cookies + localStorage) en un archivo:
// global-setup.ts — se ejecuta una vez antes de todos los tests
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('testusuario@ejemplo.com');
await page.getByLabel('Password').fill('contraseña123');
await page.getByRole('button', { name: 'Iniciar sesión' }).click();
await page.waitForURL('/dashboard');
// Guardar cookies + localStorage en archivo
await page.context().storageState({ path: 'playwright/.auth/user.json' });
await browser.close();
}
export default globalSetup;Luego reutilízalo en los tests sin iniciar sesión:
// playwright.config.ts
projects: [{
name: 'authenticated',
use: { storageState: 'playwright/.auth/user.json' },
}],Este es el patrón recomendado para suites de tests autenticados: más rápido, más confiable, y no depende de que la UI de login funcione.
→ See also: Manejo de Autenticación en Playwright con storageState (Sin Iniciar Sesión en Cada Test) | Aislamiento de Tests: Por qué Cada Test de Playwright Debe ser sin Estado | Configuración y Limpieza Global en Playwright