Multi-Browser & Multi-Page Testing
Playwright's multi-browser and multi-page capabilities are unique in the automation landscape. Testing across Chromium, Firefox, and WebKit from a single test run ensures cross-browser compatibility. Multi-page testing enables testing complex user flows involving new tabs, popup windows, and browser context sharing — patterns that Selenium handles clumsily.
Multi-Browser and Multi-Page Patterns
import { test, expect, Browser, BrowserContext, Page } from '@playwright/test';
// ══════════════════════════════════════════════════════════════
// MULTI-BROWSER TESTING (via playwright.config.ts projects)
// Each project runs the same test on a different browser
// ══════════════════════════════════════════════════════════════
// Result after 'npx playwright test':
// ✓ [chromium] › login.spec.ts:5 › login succeeds
// ✓ [firefox] › login.spec.ts:5 › login succeeds
// ✓ [webkit] › login.spec.ts:5 › login succeeds
// ══════════════════════════════════════════════════════════════
// MULTI-PAGE TESTING (new tabs, popups, popup windows)
// ══════════════════════════════════════════════════════════════
test('click "Open Preview" opens new tab with correct content', async ({ page, context }) => {
await page.goto('/products/headphones');
// Capture the new page BEFORE clicking the link
const [newPage] = await Promise.all([
context.waitForEvent('page'), // Wait for new tab to open
page.getByRole('link', { name: 'Open Full Preview' }).click()
]);
// Wait for new page to load
await newPage.waitForLoadState('networkidle');
// Verify the new tab's content
await expect(newPage).toHaveURL(/.*preview/);
await expect(newPage.getByRole('heading')).toContainText('Product Preview');
// Close new tab and return to original
await newPage.close();
await expect(page).toHaveURL(/.*products/headphones/);
});
// ══════════════════════════════════════════════════════════════
// BROWSER CONTEXT ISOLATION
// ══════════════════════════════════════════════════════════════
test('two users acting simultaneously (admin + customer)', async ({ browser }) => {
// Create two completely isolated browser contexts (like incognito)
const adminContext = await browser.newContext({
storageState: 'auth/admin-state.json' // Pre-saved admin login
});
const customerContext = await browser.newContext({
storageState: 'auth/customer-state.json'
});
const adminPage = await adminContext.newPage();
const customerPage = await customerContext.newPage();
// Admin suspends customer account
await adminPage.goto('/admin/users');
await adminPage.getByRole('button', { name: 'Suspend Alice' }).click();
// Verify customer is immediately logged out
await customerPage.reload();
await expect(customerPage).toHaveURL(/.*login/);
await adminContext.close();
await customerContext.close();
});
// ══════════════════════════════════════════════════════════════
// SAVED AUTH STATE (avoid logging in before every test)
// ══════════════════════════════════════════════════════════════
// auth.setup.ts — Run once, save cookies/localStorage
test('save admin login state', async ({ page }) => {
await page.goto('/login');
await page.fill('#email', 'admin@test.com');
await page.fill('#password', 'AdminPass@1');
await page.click('button[type="submit"]');
await expect(page).toHaveURL('/admin/dashboard');
await page.context().storageState({ path: 'auth/admin-state.json' });
});Common Mistakes
- Using page.waitForTimeout() for new tab timing — always use context.waitForEvent('page') to deterministically wait for a new page
- Not isolating test contexts — sharing a browser context between tests causes session pollution; use fresh contexts per test
- Logging in before every test — use saved auth state (storageState) to skip the login flow in every test; 10x faster
- Not testing on WebKit — Safari uses WebKit; many CSS and JS bugs only appear on WebKit; always include it in the project matrix
Tip
Tip
Practice MultiBrowser MultiPage Testing in small, isolated examples before integrating into larger projects. Breaking concepts into small experiments builds genuine understanding faster than reading alone.
Technical diagram.
Practice Task
Note
Practice Task — (1) Write a working example of MultiBrowser MultiPage Testing from scratch without looking at notes. (2) Modify it to handle an edge case (empty input, null value, or error state). (3) Share your solution in the Priygop community for feedback.
Quick Quiz
Common Mistake
Warning
A common mistake with MultiBrowser MultiPage Testing is skipping edge case testing — empty inputs, null values, and unexpected data types. Always validate boundary conditions to write robust, production-ready software testing code.
Key Takeaways
- Playwright's multi-browser and multi-page capabilities are unique in the automation landscape.
- Using page.waitForTimeout() for new tab timing — always use context.waitForEvent('page') to deterministically wait for a new page
- Not isolating test contexts — sharing a browser context between tests causes session pollution; use fresh contexts per test
- Logging in before every test — use saved auth state (storageState) to skip the login flow in every test; 10x faster