Loading source
Pulling the file list, source metadata, and syntax-aware rendering for this listing.
Source from repo
Comprehensive Playwright testing guide covering E2E, component, API, visual, accessibility, and security tests.
Files
Skill
Size
Entrypoint
Format
Open file
Syntax-highlighted preview of this file as included in the skill package.
advanced/multi-context.md
1# Multi-Tab, Window & Popup Testing23This file covers **single-user scenarios** with multiple browser tabs, windows, and popups. For **multi-user collaboration testing** (multiple users interacting simultaneously), see [multi-user.md](multi-user.md).45## Table of Contents671. [Popup Handling](#popup-handling)82. [New Tab Navigation](#new-tab-navigation)93. [OAuth Flows](#oauth-flows)104. [Multiple Windows](#multiple-windows)115. [Tab Coordination](#tab-coordination)1213## Popup Handling1415### Basic Popup1617```typescript18test("handle popup window", async ({ page }) => {19await page.goto("/");2021// Start waiting for popup before triggering it22const popupPromise = page.waitForEvent("popup");23await page.getByRole("button", { name: "Open Support Chat" }).click();24const popup = await popupPromise;2526// Wait for popup to load27await popup.waitForLoadState();2829// Interact with popup30await popup.getByLabel("Message").fill("Need help");31await popup.getByRole("button", { name: "Send" }).click();3233await expect(popup.getByText("Message sent")).toBeVisible();3435// Close popup36await popup.close();37});38```3940### Popup with Authentication4142```typescript43test("popup login flow", async ({ page }) => {44await page.goto("/dashboard");4546const popupPromise = page.waitForEvent("popup");47await page.getByRole("button", { name: "Connect Account" }).click();48const popup = await popupPromise;4950await popup.waitForLoadState();5152// Complete login in popup53await popup.getByLabel("Email").fill("[email protected]");54await popup.getByLabel("Password").fill("password123");55await popup.getByRole("button", { name: "Log In" }).click();5657// Popup should close automatically after auth58await popup.waitForEvent("close");5960// Main page should reflect connected state61await expect(page.getByText("Account connected")).toBeVisible();62});63```6465### Handle Blocked Popups6667```typescript68test("handle popup blocker", async ({ page }) => {69await page.goto("/share");7071// Listen for console messages about blocked popup72page.on("console", (msg) => {73if (msg.text().includes("popup blocked")) {74console.log("Popup was blocked");75}76});7778const popupPromise = page.waitForEvent("popup").catch(() => null);79await page.getByRole("button", { name: "Share to Twitter" }).click();80const popup = await popupPromise;8182if (!popup) {83// Popup blocked - app should show fallback84await expect(page.getByText("Copy share link instead")).toBeVisible();85}86});87```8889## New Tab Navigation9091### Link Opens in New Tab9293```typescript94test("external link opens in new tab", async ({ page, context }) => {95await page.goto("/resources");9697// Wait for new page in context98const pagePromise = context.waitForEvent("page");99await page.getByRole("link", { name: "Documentation" }).click();100const newPage = await pagePromise;101102await newPage.waitForLoadState();103104expect(newPage.url()).toContain("docs.example.com");105await expect(newPage.getByRole("heading", { level: 1 })).toBeVisible();106107// Original page still there108expect(page.url()).toContain("/resources");109110await newPage.close();111});112```113114### Intercept New Tab115116```typescript117test("prevent new tab for testing", async ({ page }) => {118await page.goto("/links");119120// Remove target="_blank" to keep navigation in same tab121await page.evaluate(() => {122document.querySelectorAll('a[target="_blank"]').forEach((a) => {123a.removeAttribute("target");124});125});126127// Now link opens in same tab128await page.getByRole("link", { name: "External Site" }).click();129130// Can test the destination page131await expect(page).toHaveURL(/external-site\.com/);132});133```134135## OAuth Flows136137### Google OAuth Popup138139```typescript140test("Google OAuth login", async ({ page }) => {141await page.goto("/login");142143const popupPromise = page.waitForEvent("popup");144await page.getByRole("button", { name: "Sign in with Google" }).click();145const popup = await popupPromise;146147await popup.waitForLoadState();148149// Handle Google's OAuth flow150await popup.getByLabel("Email or phone").fill("[email protected]");151await popup.getByRole("button", { name: "Next" }).click();152153await popup.getByLabel("Enter your password").fill("password");154await popup.getByRole("button", { name: "Next" }).click();155156// Wait for redirect back and popup close157await popup.waitForEvent("close");158159// Verify logged in on main page160await expect(page.getByText("Welcome, Test User")).toBeVisible();161});162```163164### Mock OAuth (Recommended)165166```typescript167test("mock OAuth flow", async ({ page, context }) => {168// Mock the OAuth callback instead of real flow169await page.route("**/auth/callback**", async (route) => {170// Simulate successful OAuth171const url = new URL(route.request().url());172url.searchParams.set("code", "mock-auth-code");173await route.fulfill({174status: 302,175headers: { Location: "/dashboard" },176});177});178179// Mock token exchange180await page.route("**/api/auth/token", (route) =>181route.fulfill({182json: {183access_token: "mock-token",184user: { name: "Test User", email: "[email protected]" },185},186}),187);188189await page.goto("/login");190await page.getByRole("button", { name: "Sign in with Google" }).click();191192// Should redirect to dashboard without actual OAuth193await expect(page).toHaveURL("/dashboard");194await expect(page.getByText("Welcome, Test User")).toBeVisible();195});196```197198### OAuth Fixture199200> **For comprehensive OAuth mocking patterns** (fixtures, multiple providers, SAML SSO), see [third-party.md](third-party.md#oauthsso-mocking). This section focuses on popup window handling mechanics for OAuth flows.201202## Multiple Windows203204### Test Across Multiple Windows205206```typescript207test("sync between windows", async ({ context }) => {208// Open two pages209const page1 = await context.newPage();210const page2 = await context.newPage();211212await page1.goto("/dashboard");213await page2.goto("/dashboard");214215// Make change in first window216await page1.getByRole("button", { name: "Add Item" }).click();217await page1.getByLabel("Name").fill("New Item");218await page1.getByRole("button", { name: "Save" }).click();219220// Should sync to second window (if app supports real-time sync)221await expect(page2.getByText("New Item")).toBeVisible({ timeout: 10000 });222});223```224225### Different Users in Different Windows226227> **For multi-user collaboration patterns** (admin/user interactions, real-time collaboration, role-based testing, concurrent actions), see [multi-user.md](multi-user.md). This file focuses on single-user scenarios with multiple tabs/windows/popups.228229## Tab Coordination230231### Switch Between Tabs232233```typescript234test("manage multiple tabs", async ({ context }) => {235const page1 = await context.newPage();236await page1.goto("/editor");237238const page2 = await context.newPage();239await page2.goto("/preview");240241// Edit in first tab242await page1.bringToFront();243await page1.getByLabel("Content").fill("Hello World");244245// Check preview in second tab246await page2.bringToFront();247await page2.reload(); // If preview needs refresh248await expect(page2.getByText("Hello World")).toBeVisible();249});250```251252### Close All Tabs Except One253254```typescript255test("cleanup tabs after test", async ({ context }) => {256const mainPage = await context.newPage();257await mainPage.goto("/");258259// Open several popups during test260for (let i = 0; i < 3; i++) {261const popup = await context.newPage();262await popup.goto(`/popup/${i}`);263}264265// Close all except main page266for (const page of context.pages()) {267if (page !== mainPage) {268await page.close();269}270}271272expect(context.pages()).toHaveLength(1);273});274```275276## Anti-Patterns to Avoid277278| Anti-Pattern | Problem | Solution |279| ----------------------- | ------------------------------ | ------------------------------------------ |280| Not waiting for popup | Race condition | Use `waitForEvent("popup")` before trigger |281| Testing real OAuth | Slow, flaky, needs credentials | Mock OAuth endpoints |282| Assuming popup opens | May be blocked | Handle both open and blocked cases |283| Not closing extra pages | Resource leak | Close pages in cleanup |284285## Related References286287- **Authentication**: See [fixtures-hooks.md](../core/fixtures-hooks.md) for auth patterns288- **Network**: See [network-advanced.md](network-advanced.md) for mocking OAuth289