Skip to main content

Logging In Every Test Is Slow and Flaky

Short answer

Filling email and password in beforeEach burns CI minutes and flakes on MFA, rate limits, and captcha. Prefer API token Arrange or storageState from setup projects—UI login only when the login flow itself is under test. Pair with per-role, per-worker isolation from shared auth pollution.

Part of Common E2E testing gotchas.

Symptom

  • Suite runtime dominated by login screens
  • Random failures on "Sign in" button or OTP field
  • CI hits Auth0/Firebase rate limits after parallel workers multiply logins
  • Captcha or bot detection appears only in CI
  • Tests pass headed, fail headless on SSO redirect timing

Root cause

Auth is treated as Act in every spec instead of Arrange:

  • beforeEach navigates to /login and types credentials
  • No storageState; every worker repeats full OAuth dance
  • Shared staging user across workers triggers lockout
  • Waiting for post-login redirect with waitForTimeout instead of URL or probe

Login UI is slow, external, and order-sensitive—the opposite of what parallel E2E needs.

Fix: session in Arrange, not Act

const runId = `login-${test.info().workerIndex}-${Date.now()}`;

test('dashboard loads for viewer', async ({ page, request }) => {
const { cookieHeader } = await request.post('/api/test/seed-user', {
data: { runId, role: 'viewer' },
}).then(r => r.json());

await page.context().setExtraHTTPHeaders({ Cookie: cookieHeader });
await page.goto('/dashboard');

await expect(page.getByRole('heading', { name: 'Dashboard' })).toBeVisible();
});

Seed routes create the user and return session material in one round trip—see seed routes and probe Assert.

2. Playwright storageState from setup projects

When UI login is unavoidable (smoke) or you need real IdP cookies:

// playwright.config.ts
projects: [
{ name: 'setup-viewer', testMatch: /auth\.setup\.ts/ },
{
name: 'viewer',
dependencies: ['setup-viewer'],
use: { storageState: '.auth/viewer.json' },
},
],
// auth.setup.ts — run once per role, not per spec
import { test as setup } from '@playwright/test';

setup('authenticate as viewer', async ({ page }) => {
await page.goto('/login');
await page.getByLabel('Email').fill(process.env.E2E_VIEWER_EMAIL!);
await page.getByLabel('Password').fill(process.env.E2E_VIEWER_PASSWORD!);
await page.getByRole('button', { name: 'Sign in' }).click();
await page.waitForURL('/dashboard');
await page.context().storageState({ path: '.auth/viewer.json' });
});

Official patterns: Playwright authentication. Generate per-role files—never one admin storageState for the whole suite.

3. When UI login belongs in a spec

Test thisSkip UI login
MFA enrollment flowDashboard CRUD
OAuth provider pickerRBAC on export button
"Forgot password" emailCart checkout
Session expiry bannerProbe-gated API actions

Extract login specs to a small smoke project; everything else uses Arrange.

4. Probe auth, do not trust nav alone

await expect.poll(async () => {
const res = await request.get('/api/test/probe-session');
return (await res.json()).role;
}).toBe('viewer');

UI may render admin chrome from cache while APIs return 403—probes are authoritative for RBAC.

Anti-patterns

Anti-patternWhy it failsBetter approach
Login in every beforeEachSlow; rate limits; captchaSeed route or storageState setup
One shared staging password in CILockout under parallel workersUnique user per runId
waitForTimeout(5000) after Sign inRace on slow SSOwaitForURL or probe session
Skip auth Arrange in "read-only" testsSession refresh racesFresh seed per spec anyway
UI login for API-only coverageWasted minutesrequest fixture with bearer token

TestChimp workflow

/testchimp init scaffolds POST /api/test/seed-user with role and runId so SmartTests stop replaying login forms. /testchimp test on PRs converts legacy beforeEach login blocks to seed Arrange when markdown scenarios list required roles—agents preserve probe Assert while removing redundant UI auth.

Frequently asked questions

Should every Playwright test log in through the UI?

No—only when the login or SSO flow is what you are testing. For most specs, seed a session via API or load storageState from a setup project. UI login every time is slow and flaky under parallel CI.

storageState vs API seed—which is faster?

API seed is usually fastest—one POST returns cookies or headers without rendering login. storageState is fine when setup projects capture real IdP cookies for smoke specs; regenerate per role, not per spec.

Our tests hit Auth0 rate limits in CI—why?

Parallel workers each running full UI login multiply token requests. Use test tenants, programmatic user creation, seed routes, or setup-project storageState so workers do not share one staging account.

Can I use storageState globally in playwright.config?

Only if every project needs the same role—and parallel workers still race on refresh. Prefer per-role projects or per-runId seed routes; see shared auth pollution gotcha.

Login passes locally but fails headless in CI?

SSO redirects, captcha, and MFA often appear only in CI. Move auth to Arrange via seed or documented setup projects; reserve headed capture for rare smoke runs.

How do fixtures help avoid repeated login?

Custom fixtures can call seed-user once per test and set cookies before page.goto. Playwright default is fresh context per test—use that plus Arrange instead of chaining login across specs.

Does /testchimp init replace login beforeEach blocks?

It scaffolds seed-user and probe-session patterns so agents and SmartTests use API Arrange by default. /testchimp test on PRs refactors specs that still type credentials when scenarios only require a role.

When is UI login still required?

When testing MFA enrollment, OAuth provider selection, password reset email, or session expiry UX. Keep those in a dedicated smoke project; bulk functional specs should not pay the login tax.

Apply these patterns in your repo

Run `/testchimp init` to connect TestChimp to your repo, then `/testchimp test` on PRs to turn these patterns into maintained SmartTests. Use `/testchimp evolve` when you want to expand coverage as your app grows.

Start free on TestChimp · Book a demo