Last updated: March 16, 2026
Use AI to generate Playwright authentication tests using the storageState feature to capture authenticated sessions once, then reuse them across tests. AI assistants generate code that records login state to JSON, loads it in subsequent tests, and eliminates repeated login overhead—reducing test execution time significantly while maintaining proper session management and eliminating inter-test dependencies.
Playwright’s storage state feature solves this problem by capturing authenticated session data once and reusing it across tests. When you combine this capability with AI-generated test code, you get fast, maintainable authentication tests that don’t require repeated login overhead.
Table of Contents
- Prerequisites
- Best Practices for AI-Generated Authentication Tests
- Advanced Storage State Management
- Troubleshooting
Prerequisites
Before you begin, make sure you have the following ready:
- A computer running macOS, Linux, or Windows
- Terminal or command-line access
- Administrator or sudo privileges (for system-level changes)
- A stable internet connection for downloading tools
Step 1: Understand Playwright Storage State
Playwright provides a mechanism to save and load authentication state through the storageState option. Instead of executing login steps in every test, you record the authenticated state once and reuse it. This approach reduces test execution time and eliminates dependencies between tests.
The storage state contains cookies, local storage, and session storage—the complete authentication context needed to restore a logged-in session. Playwright can export this state to a JSON file and load it when creating new browser contexts.
// Recording authentication state
const { chromium } = require('playwright');
(async () => {
const browser = await chromium.launch();
const context = await browser.newContext();
const page = await context.newPage();
// Perform login
await page.goto('https://your-app.com/login');
await page.fill('[data-testid="email"]', 'test@example.com');
await page.fill('[data-testid="password"]', 'securepassword');
await page.click('[data-testid="login-button"]');
await page.waitForURL('https://your-app.com/dashboard');
// Save authenticated state
await context.storageState({ path: 'auth-state.json' });
await browser.close();
})();
Once you have the auth-state.json file, you can launch new contexts with the authenticated state pre-loaded.
Step 2: Prompting AI for Authentication Flow Tests
When working with AI coding assistants to generate authentication tests, specificity matters. A vague prompt produces generic code that won’t match your application’s actual flow. Provide the AI with clear context about your authentication mechanism, the page structure, and how you want to use stored state.
A well-structured prompt includes your login URL, the selectors for login form elements, the expected post-login URL, and instructions to use storage state. Here’s a template that works well:
Create a Playwright test file for authentication flows. Use stored state (auth-state.json)
for tests that require a logged-in user. Include these test cases:
1. Verify that a user with valid credentials can log in successfully
2. Verify that logged-in users can access protected routes
3. Verify that the logout action clears the session properly
Login URL: https://your-app.com/login
Protected URL: https://your-app.com/dashboard
Use these selectors: [data-testid="email"], [data-testid="password"], [data-testid="login-button"], [data-testid="logout-button"]
The AI will generate tests that incorporate the storage state option in the context creation, skipping the login steps for authenticated tests.
Step 3: Generated Test Structure
AI-generated authentication tests with stored state typically follow a consistent pattern. The tests separate concerns between unauthenticated and authenticated scenarios, using the storage state only where appropriate.
const { test, expect } = require('@playwright/test');
test.describe('Authentication Flow', () => {
// Test without authentication
test('unauthenticated user redirected to login', async ({ page }) => {
await page.goto('https://your-app.com/dashboard');
await expect(page).toHaveURL(/.*login/);
});
// Test using stored authentication state
test('authenticated user can access dashboard', async ({ page }) => {
await page.goto('https://your-app.com/dashboard');
await expect(page.locator('[data-testid="user-welcome"]')).toBeVisible();
});
// Test logout functionality
test('user can logout successfully', async ({ page, context }) => {
// Use storage state for authenticated context
await context.addInitScript(() => {
window.localStorage.setItem('authToken', 'test-token');
});
await page.goto('https://your-app.com/dashboard');
await page.click('[data-testid="logout-button"]');
await expect(page).toHaveURL(/.*login/);
});
});
The key is ensuring the AI understands when to use storage state versus when to test unauthenticated behavior.
Step 4: Manage Multiple User Types
Real applications often have different user roles with varying permissions. AI can help generate tests for multiple authenticated states, but you need to specify each role in your prompts. Request separate storage state files for each user type.
Generate tests for a multi-role application:
- Regular user: can view dashboard but cannot access settings
- Admin user: can view dashboard and access settings
Create separate storage state files: regular-user-state.json and admin-user-state.json
The AI will configure Playwright to load the appropriate state file based on the test’s user role.
Best Practices for AI-Generated Authentication Tests
Review the generated code carefully. AI can make mistakes with selectors, timing, or assertions. Verify that the test actually checks what you expect.
Use test fixtures to manage authentication state cleanly. This approach makes it easy to switch between authenticated and unauthenticated states without duplicating code.
// playwright.config.js
module.exports = {
use: {
storageState: ({ params }) => {
if (params.auth === 'admin') return 'admin-state.json';
if (params.auth === 'user') return 'user-state.json';
return undefined;
},
},
};
Keep your storage state files up to date. If your authentication mechanism changes, regenerate the state files and verify that existing tests still pass.
Step 5: Automate State Generation
You can also use AI to generate scripts that create storage state files automatically. This is useful in CI/CD pipelines where you need fresh authentication state before running tests.
Write a Node.js script using Playwright that:
1. Logs in with admin credentials
2. Handles any 2FA steps if present
3. Saves the authenticated state to admin-state.json
4. Logs out and repeats for regular user credentials
This automation ensures your test state remains current without manual intervention.
Advanced Storage State Management
For complex applications with multiple environments and user roles:
// playwright.config.js - Advanced configuration
module.exports = {
use: {
baseURL: process.env.BASE_URL || 'https://localhost:3000',
storageState: ({ browserName }) => {
if (browserName === 'chromium') {
return 'auth-state/chrome-state.json';
}
return 'auth-state/firefox-state.json';
},
contextOptions: {
permissions: ['notifications'],
},
},
projects: [
{
name: 'auth-setup',
testMatch: '**/auth.setup.ts',
},
{
name: 'authenticated-tests',
dependencies: ['auth-setup'],
use: { storageState: 'auth-state/admin-state.json' },
},
{
name: 'user-tests',
dependencies: ['auth-setup'],
use: { storageState: 'auth-state/user-state.json' },
},
],
};
Step 6: Authentication Setup Script
AI can generate setup scripts that create multiple authentication states:
// tests/auth.setup.ts
import { test as setup } from '@playwright/test';
setup.describe.configure({ mode: 'parallel' });
setup('authenticate as admin', async ({ page, context }) => {
// Admin login
await page.goto('https://your-app.com/login');
await page.fill('[data-testid="email"]', 'admin@example.com');
await page.fill('[data-testid="password"]', 'admin-password');
await page.click('[data-testid="login-button"]');
// Wait for dashboard and verify admin features
await page.waitForURL('**/dashboard');
await page.waitForSelector('[data-testid="admin-panel"]');
// Save authenticated state
await context.storageState({ path: 'auth-state/admin-state.json' });
});
setup('authenticate as regular user', async ({ page, context }) => {
// Regular user login
await page.goto('https://your-app.com/login');
await page.fill('[data-testid="email"]', 'user@example.com');
await page.fill('[data-testid="password"]', 'user-password');
await page.click('[data-testid="login-button"]');
// Wait for dashboard (without admin features)
await page.waitForURL('**/dashboard');
// Save authenticated state
await context.storageState({ path: 'auth-state/user-state.json' });
});
setup('authenticate as guest', async ({ page, context }) => {
// Guest login (temporary access)
await page.goto('https://your-app.com/login');
await page.click('[data-testid="guest-login"]');
// Handle any guest-specific flows
await page.waitForURL('**/guest-dashboard');
// Save authenticated state
await context.storageState({ path: 'auth-state/guest-state.json' });
});
Step 7: Handling OAuth and Social Login
For applications using OAuth providers, AI can generate appropriate test patterns:
test('OAuth authentication workflow', async ({ browser }) => {
// Create a new context without existing storage state
const context = await browser.newContext();
const page = await context.newPage();
// Intercept OAuth redirect to mock provider
await page.goto('https://your-app.com/login');
await page.click('[data-testid="github-login"]');
// Handle OAuth popup
const [popup] = await Promise.all([
page.waitForEvent('popup'),
page.click('[data-testid="github-login"]'),
]);
// Mock GitHub login (would use real credentials in integration tests)
await popup.goto('https://github.com/login?client_id=...');
await popup.fill('[name="login"]', 'github-user');
await popup.fill('[name="password"]', process.env.GITHUB_TEST_PASSWORD);
await popup.click('[name="commit"]');
// Return to main app
await page.waitForURL('**/dashboard');
// Verify OAuth token is stored
const cookies = await context.cookies();
const hasAuthToken = cookies.some(c => c.name === 'auth_token');
expect(hasAuthToken).toBe(true);
// Save state for future tests
await context.storageState({ path: 'auth-state/oauth-state.json' });
await context.close();
});
Step 8: Test Session Expiration and Renewal
AI assists in generating tests for auth state lifecycle:
test('session expiration handling', async ({ page, context }) => {
// Use saved auth state
const savedState = require('../auth-state/user-state.json');
// Manually expire tokens by modifying storage
await page.goto('https://your-app.com');
await page.addInitScript((stateJSON) => {
const state = JSON.parse(stateJSON);
// Expire all tokens
state.cookies.forEach(c => {
if (c.name.includes('auth') || c.name.includes('session')) {
c.expires = Math.floor(Date.now() / 1000) - 3600; // 1 hour ago
}
});
// Apply to storage
Object.entries(state.origins[0].localStorage || {}).forEach(([key, value]) => {
localStorage.setItem(key, value);
});
}, JSON.stringify(savedState));
// Navigate to protected route
await page.goto('https://your-app.com/protected');
// Should redirect to login
await expect(page).toHaveURL(/.*login/);
});
test('token refresh flow', async ({ page }) => {
await page.goto('https://your-app.com/api-test');
// Intercept API calls to capture refresh token logic
const responses = [];
page.on('response', response => {
if (response.url().includes('/api/auth/refresh')) {
responses.push(response);
}
});
// Make request that should trigger refresh
const response = await page.request.get(
'https://your-app.com/api/protected',
{ headers: { 'Authorization': 'Bearer expired_token' }}
);
// Verify refresh was called
expect(responses.length).toBeGreaterThan(0);
expect(response.status()).toBe(200);
});
Step 9: Parallel Test Execution with Different Auth States
test.describe('Parallel tests with different auth', () => {
test('admin operations', async ({ page, storageState }) => {
// Uses admin-state.json from config
await page.goto('https://your-app.com/settings');
await expect(page.locator('[data-testid="user-management"]')).toBeVisible();
});
test('regular user operations', async ({ page, storageState }) => {
// Uses user-state.json from config
await page.goto('https://your-app.com/dashboard');
await expect(page.locator('[data-testid="user-management"]')).not.toBeVisible();
});
test('guest operations', async ({ page, storageState }) => {
// Uses guest-state.json from config
await page.goto('https://your-app.com');
await expect(page.locator('[data-testid="upgrade-prompt"]')).toBeVisible();
});
});
Step 10: Debugging Storage State Issues
When storage state tests fail, use AI-assisted debugging:
test('debug storage state', async ({ page, context }) => {
// Log all cookies and storage
const cookies = await context.cookies();
const storageData = await page.evaluate(() => ({
localStorage: { ...localStorage },
sessionStorage: { ...sessionStorage },
}));
console.log('Cookies:', JSON.stringify(cookies, null, 2));
console.log('Storage:', JSON.stringify(storageData, null, 2));
// Verify expected auth markers exist
expect(cookies.some(c => c.name === 'session_id')).toBe(true);
expect(storageData.localStorage['user_id']).toBeDefined();
});
Troubleshooting
Configuration changes not taking effect
Restart the relevant service or application after making changes. Some settings require a full system reboot. Verify the configuration file path is correct and the syntax is valid.
Permission denied errors
Run the command with sudo for system-level operations, or check that your user account has the necessary permissions. On macOS, you may need to grant terminal access in System Settings > Privacy & Security.
Connection or network-related failures
Check your internet connection and firewall settings. If using a VPN, try disconnecting temporarily to isolate the issue. Verify that the target server or service is accessible from your network.
Frequently Asked Questions
How long does it take to use ai to generate playwright authentication flow?
For a straightforward setup, expect 30 minutes to 2 hours depending on your familiarity with the tools involved. Complex configurations with custom requirements may take longer. Having your credentials and environment ready before starting saves significant time.
What are the most common mistakes to avoid?
The most frequent issues are skipping prerequisite steps, using outdated package versions, and not reading error messages carefully. Follow the steps in order, verify each one works before moving on, and check the official documentation if something behaves unexpectedly.
Do I need prior experience to follow this guide?
Basic familiarity with the relevant tools and command line is helpful but not strictly required. Each step is explained with context. If you get stuck, the official documentation for each tool covers fundamentals that may fill in knowledge gaps.
Is this approach secure enough for production?
The patterns shown here follow standard practices, but production deployments need additional hardening. Add rate limiting, input validation, proper secret management, and monitoring before going live. Consider a security review if your application handles sensitive user data.
Where can I get help if I run into issues?
Start with the official documentation for each tool mentioned. Stack Overflow and GitHub Issues are good next steps for specific error messages. Community forums and Discord servers for the relevant tools often have active members who can help with setup problems.
Related Articles
- Which AI Is Better for Writing Playwright End-to-End Tests
- Best AI Tools for Writing Playwright Tests 2026
- Best AI Tools for Writing Playwright E2E Tests 2026
- Best AI Tools for Writing Playwright Tests
- How to Use AI to Generate Playwright Keyboard Navigation Built by theluckystrike — More at zovo.one