๐ Teaching Flow
1. Story Intro (5 min)
๐ "Imagine running 50 tests. Without session storage, every test will login โ logout โ login again. That's wasted time. Instead, we login once, store the session (cookies + local storage), and reuse it in all tests."
Analogy:
Like a movie ticket โ once you buy it, you don't stand in the queue again for every scene. You just walk in with the ticket (session).
Like a movie ticket โ once you buy it, you don't stand in the queue again for every scene. You just walk in with the ticket (session).
What is Session Storage in Playwright? (10 min)
- Definition: Saved login state = cookies + localStorage.
- Playwright concept:
storageState.json. - Use case: Avoid repetitive logins, speed up test runs.
Key Benefits:
- ๐ Faster test execution (no repeated logins)
- ๐งน Cleaner test code (no login boilerplate)
- ๐พ Persistent authentication state
- ๐ Reusable across multiple test files
One-time Login Script (15 min)
We'll create a utility script that logs in once and saves session automatically.
// utils/global-setup.js
import { chromium } from '@playwright/test';
async function globalSetup(config) {
const browser = await chromium.launch();
const page = await browser.newPage();
// Navigate & login
await page.goto('https://learn-playwright.great-site.net/');
await page.fill('#email', 'qa@example.com');
await page.fill('#passwd', 'password123');
await page.click('#SubmitLogin');
// Wait for login to complete
await page.waitForURL('**/my-account');
// Save session state to file
await page.context().storageState({ path: 'storageState.json' });
await browser.close();
}
export default globalSetup;
Pro Tip: Always wait for navigation after login to ensure the session is fully established before saving.
Hooking into Playwright Config (15 min)
// playwright.config.js
import { defineConfig } from '@playwright/test';
import globalSetup from './utils/global-setup.js';
export default defineConfig({
globalSetup, // Run this before tests
use: {
storageState: 'storageState.json', // Reuse saved session
headless: true,
},
projects: [
{
name: 'chromium',
use: { ...devices['Desktop Chrome'] },
},
],
});
Now:
- This script runs before any test starts.
- Every test reuses
storageState.json. - No need to explicitly login inside test cases.
Example Test Using Session (15 min)
// tests/account.spec.js
import { test, expect } from '@playwright/test';
test('Account page is accessible with session', async ({ page }) => {
await page.goto('https://learn-playwright.great-site.net/my-account');
await expect(page.locator('.account')).toContainText('QA Tester');
});
test('Order history is accessible', async ({ page }) => {
await page.goto('https://learn-playwright.great-site.net/order-history');
await expect(page.locator('h1')).toContainText('Order history');
});
test('Addresses page works', async ({ page }) => {
await page.goto('https://learn-playwright.great-site.net/addresses');
await expect(page.locator('h1')).toContainText('My addresses');
});
โก Notice: No login code here. The session is automatically applied.
Utility Discussion (10 min)
Why global setup is better than calling login inside every test:
- โ Faster test execution - Login once vs. 50 times
- โ Cleaner test files - Focus on test logic, not setup
- โ Reduced flakiness - Less network calls = fewer failures
- โ Better maintainability - Change login logic in one place
Tradeoffs:
- โ Faster test execution.
- โ Cleaner test files.
- โ ๏ธ If credentials expire, must regenerate session.
- โ ๏ธ Session state can become stale over time.
- โ ๏ธ Harder to test different user roles in same test run.
๐งโ๐ป Interactive Questions & Answers
1. What is the difference between cookies and localStorage?
Answer:
- Cookies: Small text files stored by the browser, sent with every HTTP request to the server. Used for authentication tokens, session IDs, and user preferences. Have expiration dates and can be set as HTTP-only for security.
- localStorage: Browser storage that persists data locally on the user's device. Not sent with HTTP requests automatically. Used for storing larger amounts of data like user preferences, cached data, or application state.
- In Playwright: Both are captured in storageState.json to recreate the complete browser state.
2. Why is storing session faster than login in every test?
Answer:
- Network calls: Login requires multiple HTTP requests (form submission, authentication, redirects). Session reuse skips these.
- Time savings: Login typically takes 2-5 seconds per test. With 50 tests, that's 100-250 seconds saved.
- Server load: Reduces load on authentication servers.
- Reliability: Fewer network calls = fewer potential failure points.
3. What will happen if storageState.json is deleted?
Answer:
- Tests will fail: Playwright will try to load the non-existent file and throw an error.
- Solution: Re-run the global setup to regenerate the file:
npx playwright test --global-setup - Best practice: Add storageState.json to .gitignore and regenerate it in CI/CD pipelines.
- Fallback: Tests should handle missing session gracefully or have a fallback login mechanism.
4. Can you store multiple user sessions? (Yes, by generating multiple files like adminState.json, userState.json)
Answer:
- Yes, absolutely! You can create multiple storage state files for different user types.
- Example files:
adminState.json,userState.json,guestState.json - Usage: Specify different storage states in different test projects or dynamically load them.
- Implementation: Create separate global setup scripts for each user type.
5. Code Snippet Q: What will this do?
await page.context().storageState({ path: 'user.json' });
await page.context().storageState({ path: 'admin.json' });
Answer:
The second line overwrites the previous state. To have multiple users, you need multiple contexts, not overwrite the same context.
Correct approach:
// Create separate contexts for different users
const userContext = await browser.newContext();
const adminContext = await browser.newContext();
// Login as user and save state
const userPage = await userContext.newPage();
// ... login as user ...
await userContext.storageState({ path: 'user.json' });
// Login as admin and save state
const adminPage = await adminContext.newPage();
// ... login as admin ...
await adminContext.storageState({ path: 'admin.json' });
6. How do you handle session expiration in your tests?
Answer:
- Detection: Check for login redirects or authentication errors in tests.
- Regeneration: Automatically regenerate storageState.json when expired.
- Fallback: Implement retry logic with fresh login if session fails.
- Monitoring: Set up alerts when session regeneration happens frequently.
7. What's the difference between globalSetup and beforeEach?
Answer:
- globalSetup: Runs once before ALL tests start. Perfect for expensive operations like login.
- beforeEach: Runs before each individual test. Good for test-specific setup.
- Use globalSetup for: Login, database seeding, environment setup.
- Use beforeEach for: Page navigation, test data preparation, cleanup.
8. How would you test different user roles in the same test suite?
Answer:
// playwright.config.js
export default defineConfig({
projects: [
{
name: 'user-tests',
use: { storageState: 'userState.json' },
testMatch: '**/user-*.spec.js'
},
{
name: 'admin-tests',
use: { storageState: 'adminState.json' },
testMatch: '**/admin-*.spec.js'
}
]
});
๐ฏ Day 2 Homework Tasks
๐ข Beginner
Task 1
Write a script that logs in once and saves state to userState.json.
Task 2
Run a test that directly visits Order History page without login.
๐ก Intermediate
Task 3
Create a second state file adminState.json for admin user.
Task 4
Write tests switching between userState.json and adminState.json.
๐ด Advanced
Task 5
Build a utility class SessionManager with methods:
saveSession(user, filePath)loadSession(filePath)validateSession(filePath)regenerateSession(user, filePath)
Task 6
Modify config to dynamically pick session based on npx playwright test --user=admin.
Best Practices & Tips
Session File Management:
- Add
storageState.jsonto.gitignore - Regenerate sessions in CI/CD pipelines
- Use environment variables for credentials
- Implement session validation before test runs
Security Considerations:
- Never commit session files with real credentials
- Use test-specific user accounts
- Rotate credentials regularly
- Implement proper cleanup after test runs
Performance Optimization:
- Use headless mode for session generation
- Implement session caching strategies
- Monitor session expiration times
- Use parallel test execution with different sessions
โ Outcomes
- Understand session storage & storageState.json
- Build a global setup utility for login
- Run tests without explicit login
- Implement multiple user sessions
- Create SessionManager utility class
- Prepare base for E2E order placement (Day 3)