Playwright interview question
How do you avoid logging in through the UI in every Playwright test?
Short answer
Log in once in a setup project, save cookies and local storage with storageState to a file, and have test projects depend on that setup and load the file via use.storageState. Prefer API login for speed, use one account per worker if tests mutate server-side state, and keep auth files out of git.
Answer
Log in once, save the authenticated browser state, and start every test already logged in. Playwright’s storageState captures cookies and localStorage (and, optionally, IndexedDB) for a context and can restore them into new contexts. The recommended structure is a setup project that authenticates and writes the state to a file, and test projects that depend on it and load that file. Logging in through the UI is then tested once, by a dedicated login test, not repeated hundreds of times.
1. A setup project that saves state
// tests/auth.setup.ts
import { test as setup, expect } from '@playwright/test';
const authFile = 'playwright/.auth/user.json';
setup('authenticate', async ({ page }) => {
await page.goto('/login');
await page.getByLabel('Email').fill(process.env.E2E_USER!);
await page.getByLabel('Password').fill(process.env.E2E_PASSWORD!);
await page.getByRole('button', { name: 'Sign in' }).click();
await expect(page.getByRole('button', { name: 'Account' })).toBeVisible(); // confirm logged in
await page.context().storageState({ path: authFile });
});
2. Wire it in the config
// playwright.config.ts
import { defineConfig, devices } from '@playwright/test';
export default defineConfig({
projects: [
{ name: 'setup', testMatch: /.*\.setup\.ts/ },
{
name: 'chromium',
use: { ...devices['Desktop Chrome'], storageState: 'playwright/.auth/user.json' },
dependencies: ['setup'],
},
],
});
Project dependencies (Playwright 1.31+) are preferred over globalSetup because the setup appears in the report and trace, and can use fixtures. Add playwright/.auth to .gitignore: the file contains live session tokens.
Faster: authenticate via API
If the app’s session is cookie-based, the request fixture can log in without a browser and save the same state:
setup('authenticate via API', async ({ request }) => {
const res = await request.post('/api/login', {
data: { email: process.env.E2E_USER, password: process.env.E2E_PASSWORD },
});
expect(res.ok()).toBeTruthy();
await request.storageState({ path: authFile }); // cookies set by the response
});
If the app keeps a token in localStorage instead, you need a page to set it, or use the UI flow once.
Multiple roles
Create one state file per role (admin.json, user.json) in setup, then choose per file or per describe block:
test.use({ storageState: 'playwright/.auth/admin.json' });
Tests that should be logged out can override with test.use({ storageState: { cookies: [], origins: [] } }).
Parallel workers and shared accounts
One shared account is fine when tests only read. If tests change server-side state for the user (cart contents, settings, password), parallel workers using the same account interfere. Then use one account per worker, typically a worker-scoped fixture that picks or creates an account using test.info().parallelIndex and saves a separate state file for it.
Limits and gotchas
sessionStorageis not included instorageState. Apps that keep auth there need an init script to restore it.- IndexedDB is only captured if you pass
indexedDB: truetostorageState()(Playwright 1.51+), relevant for some Firebase-style auth. - Tokens expire. Regenerate state on each run (the setup project does), rather than committing a file that works until next week.
- MFA and SSO: use a test tenant with MFA disabled for test accounts, a TOTP secret to generate codes in setup, or a backend test endpoint, as agreed with the security team.
What interviewers look for
storageStatewith a setup project anddependencies.- API-based login to cut time further.
- Awareness of account sharing across parallel workers and per-role states.
- Practical limits: sessionStorage, expiry, secrets out of git.
A strong answer covers
- Uses storageState to save and reuse cookies and local storage
- Setup project with dependencies in the config
- Logs in via API instead of UI where possible
- Handles parallelism: separate accounts per worker or role when state is mutated
- Notes limits: sessionStorage not saved, token expiry, keep files out of git