Skip to contentPlayForge

How do I log in once and reuse the session in Playwright?

Written by Jatin Thacker, Automation Architect · Updated

Sign in once in a setup project, save the browser state with page.context().storageState({ path }), and point your other projects at that file with storageState and dependencies: ['setup']. Every test then starts already signed in, without going through the login form.

This answer is one piece of Chapter 25: Frames, Tabs, Files and Login State.

  • Working with Iframes & Shadow DOM
  • Multiple Tabs, Windows & Popups
  • File Uploads, Downloads & Screenshots
  • Authentication & Storage State
  • Practice and interview questions: Advanced Playwright Techniques

Signing in through the form in every test is slow, and it makes every test fail when only the login page is broken. Playwright's recommended pattern is to do it once.

A setup project is a project whose only job is to run first. Its test fills the form, waits until the sign-in has finished, then calls storageState(). That writes the context's cookies and local storage to a JSON file (IndexedDB too, if you pass indexedDB: true). The test project lists dependencies: ['setup'], so setup always runs before it, and sets storageState to the file, so each new context is created with that state loaded.

The practice app keeps its session in local storage under meridian.session. The saved file held exactly that one entry. Session storage is not saved, so an app that keeps its login there needs extra code.

Keep the file in playwright/.auth and add that folder to .gitignore: it holds a real session.

In the run, the setup project signed in as the demo manager first. The test then opened orders.html, which redirects to the login page when signed out, and found the orders table.

auth.setup.js(https://practice.learnplayforge.com)
import path from 'node:path'; import { test as setup, expect } from '@playwright/test'; const authFile = path.join(import.meta.dirname, 'playwright/.auth/user.json'); setup('sign in', async ({ page }) => { await page.goto('/login.html?seed=42'); await page.getByLabel('Work email').fill('ops@meridian.test'); await page.getByLabel('Password').fill('Passw0rd!'); await page.getByRole('button', { name: 'Sign in' }).click(); await page.waitForURL('**/index.html**'); await expect(page.getByTestId('current-user')).toBeVisible(); await page.context().storageState({ path: authFile }); }); // playwright.config.js import path from 'node:path'; import { defineConfig, devices } from '@playwright/test'; export default defineConfig({ testDir: '.', reporter: 'list', use: { baseURL: 'https://practice.learnplayforge.com' }, projects: [ { name: 'setup', testMatch: /.*\.setup\.js/ }, { name: 'chromium', use: { ...devices['Desktop Chrome'], storageState: path.join(import.meta.dirname, 'playwright/.auth/user.json'), }, dependencies: ['setup'], }, ], }); // orders.spec.js import { test, expect } from '@playwright/test'; test('orders page opens already signed in', async ({ page }) => { await page.goto('/orders.html?seed=42'); await expect(page).toHaveURL(/\/orders\.html/); await expect(page.getByTestId('orders-table')).toBeVisible(); });
Output
Running 2 tests using 1 worker

  ok 1 [setup] › auth.setup.js:6:1 › sign in (1.6s)
  ok 2 [chromium] › orders.spec.js:3:1 › orders page opens already signed in (1.2s)

  2 passed (5.2s)
Test yourself
An app keeps its login token in sessionStorage. You save state with storageState() and load it in a new test. What happens?

Practised in the full course: the drill Auth: storageState.

See the chapter

AI can write the test now. The job is knowing whether it checks anything. That's what the course teaches, one chapter at a time.

163 lessons · 226 drills · start free

← All questions

Training a team? Talk to us.