Blog # 18 🔍 "Oops, It Opened in a New Tab!" — Handling Multiple Windows in Playwright Like a Pro:

Published on April 23, 2025

Ever clicked a link in a web app and suddenly you're in a new tab or window?  

That exact moment is where most automation tests go, “Wait, where did my browser go?”

Well, not anymore. 😎  

Today, I’m going to walk you through how to handle multiple tabs/windows using Playwright and yes, it’s way easier than you think!

🧪 The Real-Life Scenario

While playing around with OrangeHRM's demo site(https://opensource-demo.orangehrmlive.com/), I noticed something interesting:

There’s a link on the login page that says "OrangeHRM Website" — and when you click it… boom! New tab opens with the company's homepage.

Perfect use case to try out multiple window handling in Playwright! 🧠

🚀 Warm-up: Managing Multiple Pages Manually

First, let’s create two separate tabs and visit two different pages. Simple but useful!

test('Handle pages like a multitasking pro', async () => {
  const browser = await chromium.launch();
  const context = await browser.newContext();

  // Create two blank pages in the same browser context
  const page1 = await context.newPage();
  const page2 = await context.newPage();

  // Let's see how many pages we've got (spoiler: 2)
  const allPages = context.pages();
  console.log(`Number of pages: ${allPages.length}`); // Output: 2

  // Navigate each page to different URLs
  await page1.goto('https://opensource-demo.orangehrmlive.com/web/index.php/auth/login');
await page2.goto('https://www.orangehrm.com/'); // Validate titles like a boss await expect(page1).toHaveTitle('OrangeHRM'); await expect(page2).toHaveTitle('Human Resources Management Software | OrangeHRM HR Software'); });

📌 We created two pages (tabs), navigated to different URLs, and validated the titles. Classic multitasking!

💥 Real Magic: Catching the Popup Like a Ninja

Let’s get to the fun part — clicking a link that opens a new tab and making sure your test doesn’t freak out.

test('Handle surprise windows without screaming', async () => {
  const browser = await chromium.launch();
  const context = await browser.newContext();
  const page = await context.newPage();

  await page.goto('https://opensource-demo.orangehrmlive.com/web/index.php/auth/login');

  // Set up a listener BEFORE clicking the link
  const [childPage] = await Promise.all([
    context.waitForEvent('page'), // Wait for the new page event
    page.locator('a[href="http://www.orangehrm.com"]').click() // Click the link
  ]);

  // Wait for the new page to finish loading
  await newPage.waitForLoadState();

  console.log(`New page URL: ${newPage.url()}`);
  await expect(newPage).toHaveTitle('Human Resources Management Software | OrangeHRM HR Software');
});

🧠 What Just Happened?

Let’s break that down:

`context.waitForEvent('page')`    | Listens for a new tab to open 

`Promise.all([...])`                     | Ensures the click and event listener sync up 

`childPage.waitForLoadState()`   | Waits for the new tab to fully load 

`childPage.url()`+`toHaveTitle()`| Verifies the content of the new tab 

It’s like having Spidey-sense for popups! 🕷️

🔑 Bonus Tip

You can also do this instead if you like things a bit more step-by-step:

const pagePromise = context.waitForEvent('page');
await page.click('text=OrangeHRM Website');
const childPage = await pagePromise;

Both approaches are valid — use whichever feels more readable for your project!

 🧩 Why This Is Super Useful

✅ External links (terms and conditions, privacy policies)  

✅ Payment gateway flows (Wise, PayPal, etc.)  

✅ OAuth logins (Sign in with Google, GitHub, etc.)  

✅ Any situation where a new window gets launched 🚀

 🏁 Final Thoughts

Working with multiple windows used to be a huge headache in automation, but Playwright makes it feel like magic🪄.

🙋‍♀️ Next time an app throws a new tab at you, channel your inner Playwright wizard. Wave your code wand, murmur context.waitForEvent('page'), and watch the chaos unfold—calmly, from your terminal. 🧙♂️