Playwright работает с куками напрямую через объект context, но localStorage требует page.evaluate(() => localStorage.getItem('key')): прямого аналога локаторов для хранилища нет. Оба инструмента нужны для тестирования аутентификации. storageState захватывает куки и localStorage в один файл, чтобы каждый тест стартовал уже залогиненным без обращения к форме входа. Эта статья разбирает чтение, запись и очистку куков, предустановку данных в localStorage, проверку истёкших куков и паттерн глобальной подготовки для переиспользования auth-состояния по всему сьюту.
Чтение куков
import { test, expect } from '@playwright/test';
test('login sets session cookie', 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');
// читаем все куки текущей страницы
const cookies = await context.cookies();
const sessionCookie = cookies.find(c => c.name === 'session_token');
expect(sessionCookie).toBeDefined();
expect(sessionCookie!.httpOnly).toBe(true); // JS не может прочитать — хорошо
expect(sessionCookie!.secure).toBe(true); // отправляется только по HTTPS
expect(sessionCookie!.sameSite).toBe('Strict');
});context.cookies() возвращает все куки для всех страниц в текущем контексте. Фильтруй по name, domain или path по необходимости.
Установка куков
Предустанови куки перед навигацией, чтобы тестировать авторизованные состояния без прохождения через форму входа:
test('authenticated user sees dashboard', async ({ page, context }) => {
// ставим auth-куку напрямую, минуем форму входа
await context.addCookies([{
name: 'session_token',
value: 'valid-test-token-abc123',
domain: 'localhost',
path: '/',
httpOnly: true,
secure: false, // false для localhost
sameSite: 'Lax',
}]);
await page.goto('/dashboard');
await expect(page.getByRole('heading', { name: 'Dashboard' })).toBeVisible();
});storageState: он захватывает куки И localStorage в одном файле. Ручная установка куков нужна когда требуется точный контроль над атрибутами конкретной куки.Очистка куков
// очищаем все куки контекста
await context.clearCookies();
// очистка конкретной куки — через /logout эндпоинт который чистит куку на сервере
await page.goto('/logout');Тестирование истёкших куков
test('expired session redirects to login', async ({ page, context }) => {
// ставим истёкшую куку
const yesterday = new Date(Date.now() - 86400000);
await context.addCookies([{
name: 'session_token',
value: 'expired-token',
domain: 'localhost',
path: '/',
expires: yesterday.getTime() / 1000, // Unix timestamp в секундах
}]);
await page.goto('/dashboard');
await expect(page).toHaveURL('/login');
});Работа с localStorage
У Playwright нет прямого API для localStorage: доступ через page.evaluate():
// читаем localStorage
const theme = await page.evaluate(() => localStorage.getItem('theme'));
expect(theme).toBe('dark');
// устанавливаем до взаимодействия с страницей
await page.evaluate(() => {
localStorage.setItem('theme', 'dark');
localStorage.setItem('user_preferences', JSON.stringify({ fontSize: 'large' }));
});
// удаляем конкретный ключ
await page.evaluate(() => localStorage.removeItem('theme'));
// полная очистка
await page.evaluate(() => localStorage.clear());Проверка что localStorage сохраняет настройки
test('dark mode preference persists after refresh', async ({ page }) => {
await page.goto('/settings');
await page.getByRole('switch', { name: 'Dark mode' }).click();
// проверяем что настройка сохранена
const stored = await page.evaluate(() => localStorage.getItem('theme'));
expect(stored).toBe('dark');
// перезагружаем страницу
await page.reload();
// проверяем что настройка восстановлена из localStorage
await expect(page.locator('html')).toHaveAttribute('data-theme', 'dark');
});Предустановка данных в localStorage
test('user with large font preference sees larger text', async ({ page }) => {
await page.goto('/'); // сначала переходим на страницу чтобы установить домен
await page.evaluate(() => {
localStorage.setItem('fontSize', 'large');
});
await page.reload(); // перезагружаем чтобы приложение прочитало настройку
const body = await page.locator('body');
await expect(body).toHaveCSS('font-size', '18px');
});sessionStorage
То же API что у localStorage, другая область видимости: sessionStorage очищается при закрытии вкладки.
// читаем sessionStorage
const cartItems = await page.evaluate(() => sessionStorage.getItem('cart'));
// устанавливаем sessionStorage
await page.evaluate(() => {
sessionStorage.setItem('cart', JSON.stringify([{ id: 1, qty: 2 }]));
});Сохранение и восстановление состояния браузера
Для auth-тестирования в масштабе захватывай всё состояние хранилища (куки + localStorage) в файл:
// global-setup.ts — выполняется один раз перед всеми тестами
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');
// сохраняем куки + localStorage в файл
await page.context().storageState({ path: 'playwright/.auth/user.json' });
await browser.close();
}
export default globalSetup;Переиспользуй в тестах без повторного входа:
// playwright.config.ts
projects: [{
name: 'authenticated',
use: { storageState: 'playwright/.auth/user.json' },
}],Рекомендуемый паттерн для авторизованных тест-сьютов: быстрее, надёжнее, не зависит от работоспособности формы входа.
→ See also: Авторизация в Playwright через storageState (без логина в каждом тесте) | Изоляция тестов: почему каждый тест Playwright должен быть stateless | Глобальная настройка и очистка в Playwright