← Back to libraryQuestion 175 of 273
🎭PlaywrightIntermediate

Authentication & storageState (reusing login)

📌 Definition:

storageState captures a browser context's cookies and localStorage to a JSON file after login, so other tests can start already authenticated by loading that state — instead of logging in through the UI every time.

📖 Detailed Explanation:

Logging in via the UI in every test is slow and a common flakiness source. The standard Playwright pattern is: in a global setup (or a setup project), perform the real login once, then call context.storageState({ path: 'auth/user.json' }). Every other test or project creates its context with { storageState: 'auth/user.json' } and is authenticated from the first line. For multiple roles, save one file per role (admin.json, user.json) and pick per test or per project. You still keep one or two tests that exercise the actual login form for coverage, but the rest skip it. This can cut minutes off a suite and removes the login page as a single point of failure for unrelated tests.

🔑 Key Points:
  • Log in once, save cookies+localStorage with context.storageState({ path })
  • Other tests/projects start authenticated via use: { storageState: file }
  • One file per role (admin/user/guest) for multi-role suites
  • Keep a couple of real login-form tests; skip the UI login everywhere else
  • Set it up in a global setup or a dependency 'setup' project so it runs first
🌍 Real-World Example:

A 200-test suite logged in through the UI in every beforeEach, adding ~3s each — about 10 minutes total. Switching to a setup project that logs in once and saves storageState, then loading it everywhere, removed that entirely: the same 200 tests start authenticated instantly, and login-form coverage still lives in two dedicated tests.

📎 Code Example:
// auth.setup.ts — runs once as a dependency project
import { test as setup } from '@playwright/test';

setup('authenticate', async ({ page }) => {
  await page.goto('/login');
  await page.getByLabel('Email').fill('qa@qapractice.com');
  await page.getByLabel('Password').fill('Secret123');
  await page.getByRole('button', { name: 'Sign in' }).click();
  await page.waitForURL('**/dashboard');
  await page.context().storageState({ path: 'auth/user.json' });
});

// playwright.config.ts (excerpt)
// projects: [
//   { name: 'setup', testMatch: /auth\.setup\.ts/ },
//   { name: 'chromium',
//     use: { ...devices['Desktop Chrome'], storageState: 'auth/user.json' },
//     dependencies: ['setup'] },
// ]
🎯 Scenario-Based Interview Question:

Scenario: Your suite logs in through the UI in every test. It's slow, and worse, whenever the login page has a hiccup ALL tests fail, masking real failures elsewhere. How do you restructure auth?