P0 · Chapter 06 / 15

Authentication / Isolation

The 60-second cheat sheet#

storageState is a browser-state snapshot, not a permanent server session. Isolated contexts prevent cookie/local-storage collisions; they do not prevent two tests mutating the same account. Choose account scope using mutation and server-session rules, and include pipeline/shard identity in allocation.

Saved state and authentication choices#

Saved state includes cookies and localStorage; IndexedDB capture is opt-in through the supported indexedDB: true option. sessionStorage is not automatically persisted. Verify your installed Playwright version supports the IndexedDB option. Backend TTL, revocation, password changes or single-session rules can invalidate otherwise intact JSON.

Approach Use when Cost / risk
UI login every test Login UX is the requirement Slow; account/session collision if reused
Setup project + state Many read-only compatible tests Session may expire mid-suite
API login Approved backend supports equivalent session Must prove actual application authentication works separately
Worker account Worker tests cannot conflict with each other Restart/parallel jobs need unique allocation
Test account Destructive or concurrent profile/password mutations More provisioning/cleanup
Supported refresh Application provides refresh protocol Refresh rotation/revocation must be handled correctly

Complete authentication setup#

typescript
// tests/auth.setup.ts; complete module, illustrative UI contract.
import { test as setup, expect } from '@playwright/test';
import { mkdirSync } from 'node:fs';
setup('authenticate sandbox account', async ({ page }) => {
  const email = process.env.QA_EMAIL;
  const password = process.env.QA_PASSWORD;
  if (!email || !password) throw new Error('QA_EMAIL and QA_PASSWORD required');
  await page.goto('/login');
  await page.getByLabel('Email').fill(email);
  await page.getByLabel('Password').fill(password);
  await page.getByRole('button', { name: 'Sign in', exact: true }).click();
  await expect(page).toHaveURL(/\/dashboard(?:\?|$)/);
  await expect(page.getByRole('button', { name: 'Sign out' })).toBeVisible();
  mkdirSync('playwright/.auth', { recursive: true });
  await page.context().storageState({ path: 'playwright/.auth/user.json' });
});
typescript
// playwright.config.ts; complete module. Shared state only for compatible tests.
import { defineConfig, devices } from '@playwright/test';
export default defineConfig({
  testDir: './tests',
  use: { baseURL: process.env.BASE_URL ?? 'http://localhost:3000' },
  projects: [
    { name: 'setup', testMatch: /auth\.setup\.ts/ },
    { name: 'chromium', testMatch: /.*\.spec\.ts/,
      use: { ...devices['Desktop Chrome'], storageState: 'playwright/.auth/user.json' },
      dependencies: ['setup'] },
  ],
});
typescript
// tests/ui/dashboard.spec.ts; complete module using dependent project state.
import { test, expect } from '@playwright/test';
test('authenticated dashboard is visible', async ({ page }) => {
  await page.goto('/dashboard');
  await expect(page.getByRole('heading', { name: 'Dashboard', exact: true })).toBeVisible();
});

Add playwright/.auth/ to .gitignore. Never publish state as a CI artifact; traces may also contain cookies/payloads. Credentials come from validated environment variables or a secret manager. This setup is scoped to one run/job and assumes its account is compatible with all dependent tests. Four jobs do not share their local files automatically.

Single-active-session and identity allocation#

Failure: four GitLab shards log in with one account; each new login revokes earlier sessions. All four storage JSON files look valid, yet earlier jobs get 401 or redirect. Fix account/session allocation, not file writes or timeouts. Lease/provision a distinct account per conflicting shard/worker and across overlapping pipelines. Prefer a central lease service with expiry over static modulus indexing into a small pool.

Identifier Meaning Global uniqueness?
parallelIndex Stable worker slot across replacement workers in a run No
workerIndex Unique worker process index within run; replacement gets another No across jobs
CI_NODE_INDEX / CI_NODE_TOTAL GitLab parallel job/shard partition identity No across pipelines
CI_PIPELINE_ID + shard + slot + UUID Candidate allocation namespace Still needs backend ownership/lease enforcement

Four shards × four workers gives up to sixteen worker slots, and another pipeline doubles contention if accounts are reused. Browser contexts are not extra backend identities. Worker restarts require safe reallocation and cleanup of orphan resources.

Five broken scenarios and fixes#

Broken situation Root cause Correct action
Rewritten state still redirects Backend token expired Fresh login or supported refresh; verify authenticated endpoint
Only first shard fails after others start Single-active-session rule Unique account per concurrent session scope
Password-change test breaks later tests Shared account revoked Dedicated destructive-test account; recreate/re-auth appropriately
Parallel profile tests overwrite names Shared server data Per-test account or distinct owned records
Setup saves login form state Missing/invalid credentials or false success check Validate env, assert dashboard/authenticated UI before save

Long runs can outlive TTL even after fresh setup. Provision appropriate test sessions or refresh through the actual protocol; don't conceal product expiry bugs. State rollback cannot undo every server mutation, audit effect or password revocation.

Scope decision and interview response#

Test-scoped for destructive operations; worker-scoped for independent/read-only resources; mixed strategies for shared infrastructure plus per-test users. Avoid one global “authenticated user” fixture for every type of scenario.

30 seconds: “I reuse storageState only when server-session policy and test mutations allow it. It saves cookies/localStorage and optionally IndexedDB, not sessionStorage by default. It does not extend backend validity. For conflicting tests I allocate accounts per test or worker with pipeline/shard uniqueness and refresh authentication through the supported flow.”

90-second senior answer: parallel auth diagnosis

I inspect whether the failure is missing browser state or a server rejection of existing credentials. If saved state works locally but fails when shards start, I compare login timestamps, account identity, 401 responses and backend revocation logs. A single-session policy explains why a later shard invalidates an earlier one. More timeouts cannot fix that. I use a lease or provisioning mechanism whose namespace includes concurrent pipeline, shard and worker scope, and dedicate accounts for password/deactivation tests. State files remain local and ignored by Git; reports must not leak them. A fresh setup verifies a real authenticated screen before saving. I also consider suite duration against TTL and distinguish session-policy defects from automation setup errors.

Memory hook: snapshot ≠ session lifetime; context ≠ account.

Quick practice · reveal answers#

What does storageState contain?
  • Cookies/localStorage; IndexedDB opt-in where supported.
  • sessionStorage is not automatically persisted.

Pitfall: Assuming it captures every browser or server state.

Full explanation →

Why can saved state expire?
  • Backend TTL, revocation, password change or single-session policy can invalidate it.
  • Re-authenticate/refresh through real protocol.

Pitfall: Rewriting identical expired JSON.

Full explanation →

Why do four shards with one account fail?
  • Later logins may revoke earlier sessions or writes collide.
  • Use independent leased/provisioned accounts across jobs and pipelines.

Pitfall: Treating browser-context isolation as account isolation.

Full explanation →

When do tests need unique accounts?
  • When mutations, password changes or session policy can interfere.
  • Worker sharing is acceptable only with compatible state and ownership.

Pitfall: Sharing a destructive-test account with later read tests.

Full explanation →

Official references and related topics#

Optional online further reading; all core study material is included above.

Back to library index

Prepared for both Senior automation roles. Hypothetical sandbox contracts; no employer systems or credentials. Content: 16 October 2026.