🎭 Playwright: Special Elements - Multi-Context and Windows
The Story of the Busy Restaurant Manager 🍽️
Imagine you’re the manager of a super busy restaurant with many rooms. Each room has different guests, different orders, and different waiters. You need to keep track of everything happening in ALL rooms at the same time!
Playwright works the same way when testing websites that open new windows, popups, or need multiple browser sessions running together.
🏠 What Are Browser Contexts?
Think of a browser context like a separate room in our restaurant.
- Each room has its own guests (cookies)
- Each room has its own menu (local storage)
- Each room has its own waiter (session)
- Guests in Room A can’t see what’s happening in Room B!
graph TD B["🌐 Browser"] --> C1["🚪 Context 1<br>User A&#39;s Session] B --> C2[🚪 Context 2<br>User B&#39;s Session"] C1 --> P1["📄 Page 1"] C1 --> P2["📄 Page 2"] C2 --> P3["📄 Page 3"]
Why Do We Need Multiple Contexts?
Real Example: Testing a chat app where User A sends a message to User B.
You need TWO separate “rooms” - one for each user!
🚪 Creating Multiple Browser Contexts
Here’s how we create multiple rooms (contexts):
const { chromium } = require('playwright');
async function twoUsers() {
const browser = await chromium.launch();
// Room 1: Alice's session
const aliceContext = await browser.newContext();
const alicePage = await aliceContext.newPage();
// Room 2: Bob's session
const bobContext = await browser.newContext();
const bobPage = await bobContext.newPage();
// Now Alice and Bob are separate!
await alicePage.goto('https://chat.example.com');
await bobPage.goto('https://chat.example.com');
}
🎯 Key Point: Alice’s cookies don’t mix with Bob’s cookies. They’re in separate rooms!
🔒 Context Isolation - The Magic Wall
What Is Isolation?
Imagine there’s an invisible wall between each room:
- 🧱 Cookies stay in their own room
- 🧱 Login sessions stay separate
- 🧱 Storage data doesn’t leak
- 🧱 Cache is independent
graph LR subgraph Context1["🔵 Context 1"] C1["🍪 Cookies A"] S1["💾 Storage A"] end subgraph Context2["🔴 Context 2"] C2["🍪 Cookies B"] S2["💾 Storage B"] end W["🧱 ISOLATION WALL"] Context1 -.-> W W -.-> Context2
Real Example: Testing Admin vs Regular User
async function testDifferentRoles() {
const browser = await chromium.launch();
// Admin context with special settings
const adminContext = await browser.newContext({
storageState: 'admin-login.json'
});
// Regular user context
const userContext = await browser.newContext({
storageState: 'user-login.json'
});
const adminPage = await adminContext.newPage();
const userPage = await userContext.newPage();
// Admin sees admin dashboard
await adminPage.goto('/dashboard');
// User sees limited dashboard
await userPage.goto('/dashboard');
// They see DIFFERENT things!
}
💡 Why This Matters: You can test “what does an admin see?” and “what does a regular user see?” at the SAME time!
📑 Multiple Pages and Tabs
Pages Are Like Tables in a Room
Inside each room (context), you can have multiple tables (pages).
- Same room = same cookies shared
- Different tables = different web pages open
async function multipleTabs() {
const browser = await chromium.launch();
const context = await browser.newContext();
// Open first tab
const page1 = await context.newPage();
await page1.goto('https://shop.example.com');
// Open second tab (same context!)
const page2 = await context.newPage();
await page2.goto('https://shop.example.com/cart');
// Both tabs share the same login!
// Add item on page1...
await page1.click('.add-to-cart');
// Refresh page2 to see cart updated!
await page2.reload();
}
graph TD CTX["🚪 Single Context<br>Same Session"] --> T1["📄 Tab 1<br>Shop Page"] CTX --> T2["📄 Tab 2<br>Cart Page"] CTX --> T3["📄 Tab 3<br>Checkout"] style CTX fill:#e1f5fe
Getting All Open Pages
// Get all pages in a context
const allPages = context.pages();
console.log(`Open tabs: ${allPages.length}`);
// Find a specific page by URL
const cartPage = allPages.find(
p => p.url().includes('/cart')
);
🎈 Handling Popups
Popups Are Like Surprise Visitors!
When you click a button and a new little window appears, that’s a popup. Playwright can catch these!
Think of it like this: You’re watching the door, and when a surprise guest arrives, you’re ready to greet them.
async function handlePopup() {
const page = await context.newPage();
await page.goto('https://example.com');
// Wait for popup BEFORE clicking
const popupPromise = page.waitForEvent('popup');
// Click the button that opens popup
await page.click('#open-login-popup');
// Catch the popup!
const popup = await popupPromise;
// Now interact with popup
await popup.fill('#username', 'myuser');
await popup.click('#login-btn');
}
The Popup Pattern Explained
graph TD A["📄 Main Page"] -->|Click Button| B["🎈 Popup Opens"] B -->|Playwright Catches| C["📄 Popup Page Object"] C -->|You Can Now| D["Fill Forms"] C -->|You Can Now| E["Click Buttons"] C -->|You Can Now| F["Read Content"]
Important Popup Tips
| Situation | What Happens |
|---|---|
target="_blank" link |
Creates popup |
window.open() call |
Creates popup |
| OAuth login button | Usually popup |
| Payment window | Often popup |
// Handle popup with timeout
const popup = await page.waitForEvent('popup', {
timeout: 5000 // Wait max 5 seconds
});
// Check if popup loaded correctly
await popup.waitForLoadState('domcontentloaded');
console.log('Popup URL:', popup.url());
🪟 New Window Handling
Windows Are Like New Rooms Opening Up
When a website opens a completely new browser window, it’s similar to a popup but bigger!
async function handleNewWindow() {
const page = await context.newPage();
await page.goto('https://docs.example.com');
// Prepare to catch new window
const newWindowPromise = context.waitForEvent('page');
// Click link that opens new window
await page.click('a[target="_blank"]');
// Get the new window
const newWindow = await newWindowPromise;
// Wait for it to load
await newWindow.waitForLoadState();
// Work with new window
const title = await newWindow.title();
console.log('New window title:', title);
}
Multiple Windows - Keep Track!
async function manageWindows() {
const context = await browser.newContext();
// Start with one page
const mainPage = await context.newPage();
await mainPage.goto('https://example.com');
// Track all pages that open
context.on('page', async (newPage) => {
console.log('New window opened!');
console.log('URL:', newPage.url());
});
// Your test continues...
// Every new window triggers the event!
}
graph LR CTX["🚪 Context"] --> M["📄 Main Window"] M -->|Opens| N1["🪟 New Window 1"] M -->|Opens| N2["🪟 New Window 2"] CTX -.->|Listens| E["👂 page Event"]
Closing Windows Properly
// Close a specific window
await newWindow.close();
// Close all windows except main
const pages = context.pages();
for (const p of pages) {
if (p !== mainPage) {
await p.close();
}
}
🎯 Putting It All Together
Complete Example: Testing Multi-User Chat
const { chromium } = require('playwright');
async function testChat() {
const browser = await chromium.launch();
// Two separate users (contexts)
const aliceCtx = await browser.newContext();
const bobCtx = await browser.newContext();
const alicePage = await aliceCtx.newPage();
const bobPage = await bobCtx.newPage();
// Both go to chat
await alicePage.goto('https://chat.app');
await bobPage.goto('https://chat.app');
// Alice logs in
await alicePage.fill('#user', 'alice');
await alicePage.click('#join');
// Bob logs in
await bobPage.fill('#user', 'bob');
await bobPage.click('#join');
// Alice sends message
await alicePage.fill('#msg', 'Hello Bob!');
await alicePage.click('#send');
// Bob sees it!
const msg = await bobPage.textContent('.message');
console.log('Bob received:', msg);
// Clean up
await aliceCtx.close();
await bobCtx.close();
await browser.close();
}
🌟 Quick Reference
| Concept | Restaurant Analogy | Playwright Code |
|---|---|---|
| Browser | The Restaurant | chromium.launch() |
| Context | A Private Room | browser.newContext() |
| Page | A Table | context.newPage() |
| Popup | Surprise Guest | page.waitForEvent('popup') |
| New Window | New Room Opens | context.waitForEvent('page') |
💪 You’ve Got This!
Now you understand:
- ✅ Multiple contexts = separate user sessions
- ✅ Context isolation = data doesn’t mix between sessions
- ✅ Multiple pages = many tabs, same session
- ✅ Popups = catching small windows that appear
- ✅ New windows = handling full new browser windows
Remember: Think of the restaurant! Each room is isolated, but you’re the manager who can see and control everything. 🎭
Happy Testing! 🚀
