Q2 of 42 · Playwright
How does Playwright's storageState work and when would you use it?
Short answer
Short answer: storageState is a JSON snapshot of cookies and origin storage that Playwright can save after a one-time login and inject into every test's browser context. It's the canonical way to skip UI login in large suites.
Detail
storageState solves the same problem as Cypress's cy.session: log in once, reuse the authenticated state across every test, and skip the UI login. The mechanics are different — Playwright serializes cookies and origins[].localStorage to a JSON file, and any subsequent browser.newContext({ storageState: 'auth.json' }) starts already authenticated.
The standard pattern uses the project setup dependency. You define a setup project that runs first, performs a real login (UI or API), and writes auth.json. Other projects depend on setup and read that file. Playwright won't re-run setup if the file is still valid, so login happens once per CI run, not per test.
For multiple roles, write multiple files — admin.json, viewer.json — and have each test or project pick the one it needs. For more isolation, you can scope storageState to a specific test using test.use({ storageState: '...' }).
Two pitfalls worth raising in an interview: tokens that expire mid-run (regenerate auth.json more often than the token TTL, or refresh them in globalSetup), and SameSite/secure cookie issues when running against a different origin in CI than you originally captured. Keep auth.json out of source control — it's a credential.
// EXAMPLE
playwright.config.ts
import { defineConfig } from '@playwright/test';
export default defineConfig({
projects: [
{
name: 'setup',
testMatch: /global\.setup\.ts/,
},
{
name: 'chromium',
use: { storageState: 'playwright/.auth/admin.json' },
dependencies: ['setup'],
},
],
});
// global.setup.ts
import { test as setup } from '@playwright/test';
setup('authenticate', async ({ request }) => {
const res = await request.post('/api/login', {
data: { email: process.env.E2E_EMAIL, password: process.env.E2E_PASSWORD },
});
const { token } = await res.json();
await request.storageState({
path: 'playwright/.auth/admin.json',
});
});