Scale Your Automation Testing with AI

  • CheckRun end-to-end parallel tests & reduce test execution time by 5x
  • CheckGenerate tests scripts using natural language with KaneAI
  • CheckAccelerate your testing process with Autoheal, SmartWait & RCA
...

Handling Frames and Windows in Playwright: A Complete Guide

Learn how to handle frames and windows in Playwright, including interacting with frames, multiple tabs, pop-ups, and nested pages efficiently.

Last Modified on: December 1, 2025

  • Share:

Frames and windows are commonly used to display third-party content, embedded pages, or external resources within a website. In automation testing, handling frames and windows in Playwright can be challenging because tests must correctly switch between different browsing contexts, whether it's an iframe, a new tab, or a separate browser window.

Playwright streamlines this process through clean, intuitive APIs that make switching between frames, interacting with pop-ups, and managing multiple tabs both reliable and efficient. Understanding how to handle frames and windows in Playwright is essential for building stable and effective end-to-end test automation.

Overview

How to Handle Frames in Playwright?

Handling frames in Playwright involves accessing elements inside <iframe>containers instead of the main page context. Since frames load external or embedded content, Playwright provides dedicated APIs to locate, switch, and interact with them safely. Understanding how to handle frames in Playwright ensures your tests can work with nested UI components, embedded forms, and multi-frame layouts.

Steps to Handle Frames in Playwright:

  • Identify Frames on the Page: Inspect the DOM to locate existing <iframe> elements and determine which frame contains the target elements.
  • Retrieve the Required Frame: Use page.frame() or page.frameLocator() to access the correct frame or nested frame.
  • Interact With Elements Inside the Frame: Perform actions such as fill, click, or assertions using the frame’s locator context.
  • Handle Dynamic or Optional Frames: Use optional chaining or conditional checks to ensure the frame exists before interacting with it.
  • Work With Nested Frames: Chain frame locators (e.g., frame.frameLocator()) to reach and operate inside multi-level iframes.
  • Return to the Main Page When Needed: Resume interactions on the primary page by switching back to the original page object.
  • Validate Content Inside and Outside Frames: Confirm field inputs, UI updates, or behavior within both framed and non-framed sections.

How to Handle Windows in Playwright?

Handling windows in Playwright is simple because each new tab or pop-up is treated as a separate Page object. You can wait for the new windows using the page.waitForEvent("popup"), interact with them just like any other page, and switch between multiple tabs seamlessly. This makes it easy to test user flows that involve pop-ups or external links.

Steps to Handle Windows in Playwright:

  • Navigate to the Main Page: Use page.goto() to load the primary webpage where the window-triggering action occurs.
  • Trigger a Window-Opening Action: Perform an interaction such as clicking a link or button that opens a new browser window.
  • Capture the New Window Event: Use Promise.all() with page.waitForEvent("popup") to detect and capture the newly opened window.
  • Access the New Window Page Object: Store the popup page returned by Playwright so you can interact with it like a standard page.
  • Interact With Elements Inside the New Window: Perform actions such as clicking buttons, filling fields, extracting text, or validating navigation.
  • Switch Back to the Original Page if Needed: Use the saved reference to return to the first window and continue test execution.
  • Validate Navigation and User Journey Across Windows: Confirm URLs, UI visibility, and workflow accuracy across all active windows.

What Are Advanced Techniques for Handling Windows in Playwright?

Advanced window-handling techniques in Playwright ensure your automation remains stable when dealing with multiple tabs, pop-ups, and dynamic page transitions. By waiting for specific load states and identifying the correct window context, you can reliably interact with complex multi-page workflows.

  • Handling Windows in Playwright Using domcontentloaded: Ensures the new window’s HTML structure is fully parsed before performing interactions or retrieving elements within the page.
  • Handling Windows in Playwright Using load: Waits for all linked resources, scripts, stylesheets, and media to finish loading, providing a fully rendered and stable window.
  • Handling Windows in Playwright Using networkidle: Confirms no ongoing network activity, making it ideal for dynamic pages that fetch background data after opening.
  • Interacting with Multiple Pages in Playwright: Identifies each open tab by URL, allowing targeted actions only on the specific window required for the test.

What Are Frames in Playwright?

A Frame is an <iframe> or the main document that is part of a page. The frame can let you create a new experience inside the page. These new experiences are then accessible through interactions with the page. Every time you need to inspect the page, you can access the current frame tree using the frame.childFrames() and page.mainFrame() methods. (It’s not automatically displayed when the page loads.)

Three events on the page object regulate the lifecycle of the frame object:

  • page.on('frameattached'): This event is triggered only once when a new frame is successfully attached to the page.
  • page.on('framenavigated'): This event is triggered every time a frame navigates or loads a different URL inside the page.
  • page.on('framedetached'): This event is triggered only once when an existing frame is removed or detached from the page.

How to Handle Frames in Playwright?

When handling frames and windows in Playwright, you must pay special attention to iframe-based content, as elements inside frames cannot be accessed through the main page context. Properly handling iframes in Playwright ensures your automation script interacts with all nested and dynamic elements reliably.

To understand how to handle frames and windows in Playwright, let’s consider a practical test scenario.

Test Scenario:

Let's use https://letcode.in/frame to demonstrate how handling iframes in Playwright works. When you inspect this page, you can see multiple <iframe> tags embedded within the document.

Code Implementation:

import { expect, test } from "@playwright/test";
test("Interact with frames", async ({ page }) => {
    await page.goto("https://letcode.in/frame");
    const allframes = page.frames();
    console.log("No.of frames: " + allframes.length);
})

Code Walkthrough:

  • Create a Test File: In VS Code, create a file named frames.test.ts. Import expect and test from @playwright/test. Define a test block called "Interact with frames".
  • Navigate to the Website: Use Playwright to open the target URL. Inspect the page to check the number of frames present on the page.
  • Note that dynamic content, such as advertisements, may affect the frame count, so enter an approximate value in your test script.

Viewing Test Reports:

There are two ways to view the test results:

  • Playwright HTML Report: Open the Playwright reports and view the HTML report. Click on stdout to see detailed information, including the number of frames.
  • view test report
  • Console Output: Configure playwright.config.ts to match the test by name and update package.json if needed. Execute the test script in the terminal to see the number of frames directly in the console.
  • console output

How to Interact With Frames in Playwright?

A page can have one or more frame objects attached via the <iframe> HTML tag. Each page has a main frame, and page-level interactions like click are assumed to work within the main frame. The <iframe> tag allows for the attachment of additional frames to a page.

To interact with elements inside these frames, you can access the frame objects. Frame objects can be obtained using the page.frame() API. You can also use page.frames() to get all frames or frame.childFrames() to access nested frames.

A page can have one or more frame objects attached to the iframe HTML tag. Each page has a main frame and page-level interactions like “click” that are assumed to work within the main frame.

For a practical demo on handling frames and windows in Playwright, we will use the same website as before, and interact with the “First Name” and “Last Name” fields.

Test Scenario:

  • Navigate to https://letcode.in/frame in your browser.
  • Right-click on the page and select Inspect, or press F12.
  • Hover over the “First Name” or “Last Name” input fields in the Elements tab. The entire frame will be highlighted in blue, indicating which &lg;iframe> contains the elements.
  • Once you identify the frame, use Playwright’s frame locator to interact with elements inside the frame during your test.

Code Implementation:

import { expect, test } from "@playwright/test";
test("Interact with frames", async ({ page }) => {
    await page.goto("https://letcode.in/frame");
    const allframes = page.frames();
    console.log("No.of frames: " + allframes.length);
    const myFrame = page.frame("firstFr")
if (myFrame != null) {
      await myFrame.fill("", "")
  }
await myFrame?.fill("input[name='fname']", "yourname")
await myFrame?.fill("input[name='lname']", "yourexample")
   expect(await myFrame?.locator("p.has-text-info").textContent()).toContain("You have entered")
})
LambdaTest Puppeteer Screenshot

Code Walkthrough:

  • Get the Frame: Use page.frame() and provide the frame’s name. Hover in the inspector to select the correct frame.
  • Copy the Locator: From the inspector, copy the locator for the “First Name” field.
  • Fill the Field: Use the fill function to enter the test data into the “First Name” input field.
  • Check for Frame Existence: If the frame may not be available, use the optional chaining operator ?. to ensure the fill function runs only when the frame exists.
  • Alternatively, you can use an if statement to check if the frame exists before running the fill function.
  • Fill the Last Name Field: Inspect the “Last Name” field and use the fill function to pass the locator and test data.

How to Handle Frames in Playwright with Frame Locators?

When you need to test iframes, you can use a frame locator, one of the Playwright locators, to access the iframe and interact with elements inside it. The frame locator helps you target elements within the frame efficiently.

Code Implementation:

test("Interact with frames", async ({ page }) => {


    await page.goto("https://letcode.in/frame");
    const allframes = page.frames();
    console.log("No.of frames: " + allframes.length);
    const frame = page.frameLocator("#firstFr")
    await frame.locator("input[name='fname']").fill("Koushik");
    await frame.locator("input[name='lname']").fill("Chatterjee");

Code Walkthrough:

  • Use the Frame ID: In VS Code, use the id from the inspector instead of the frame name or URL to identify the frame.
  • Select the Frame: The frame locator allows you to interact with the first, last, or nth frame on the page.
  • Fill the First Name Field: Use the .fill() function to pass the locator and enter the test data for the “First Name” field.
  • Fill the Last Name Field: Inspect the “Last Name” field and use the .fill() function to pass the locator and enter the test data.

How to Handle Nested Frames in Playwright?

Nested frames occur when one iframe is embedded inside another iframe. This allows for more complex page layouts, as frames can be nested to multiple levels.

In modern web pages, <iframe> elements are used instead of the older <frameset> approach. Playwright provides tools like frame locators to interact with both parent and nested frames.

Code Implementation:

import { expect, test } from "@playwright/test";
test("Interact with frames", async ({ page }) => {
    await page.goto("https://letcode.in/frame");
    const allframes = page.frames();
    console.log("No.of frames: " + allframes.length);
    const frame = page.frameLocator("#firstFr")
    await frame.locator("input[name='fname']").fill("Koushik");
    await frame.locator("input[name='lname']").fill("Chatterjee");
    const innerFrame = frame.frameLocator("iframe[src='innerFrame']")
    await innerFrame.locator("input[name='email']").fill("koushik@gmail.com")
    await frame.locator("input[name='fname']").fill("letcode");
    const myFrame = page.frame("firstFr")
    if (myFrame != null) {
        await myFrame.fill("", "")
    }
    await myFrame?.fill("input[name='fname']", "koushik")
    await myFrame?.fill("input[name='lname']", "chatterjee")
    expect(await myFrame?.locator("p.has-text-info").textContent()).toContain("You have entered")
    await page.waitForTimeout(3000);

Code Walkthrough:

  • Navigate to the Page: Use page.goto() to open https://letcode.in/frame in the browser.
  • Get All Frames: Use page.frames() to retrieve all frames on the page and log the count for verification.
  • Use the Frame ID: In VS Code, use the ID from the inspector (e.g., #firstFr) instead of the frame name or URL to identify the parent frame.
  • Fill the First Name Field: Use the frame locator to target the “First Name” field and enter the test data.
  • Fill the Last Name Field: Use the frame locator to target the “Last Name” field and enter the test data.
  • Target the Nested Frame: Use the parent frame locator to access the nested iframe (iframe[src='innerFrame']).
  • Fill the Email Field: Use the nested frame locator to enter the email in the “Email” input field.
  • Repeat Interaction on Parent Frame: Use the parent frame locator to fill the “First Name” field again if needed.
  • Optional Frame Object Approach: You can alternatively use page.frame("firstFr") with optional chaining ?. or conditional checks to interact with fields.
  • Run and Observe: Execute the test in VS Code. Observe the browser or console to verify that the test data is populated correctly.
  • Wait for Observation: Use page.waitForTimeout(3000) to pause and visually confirm the test execution.

Result:

frame result

Now that we’ve explored how to automate single and nested frames in Playwright, the next step is understanding how to handle full window interactions.

Playwright allows you to work within embedded content, but real-world applications often trigger entirely new tabs or pop-up windows.

Let’s move on and see how to handle windows in Playwright effectively.

How to Handle Windows in Playwright?

Handling windows in Playwright is simple. Each browser can have multiple pages, and a Page refers to a single tab or a pop-up window within a browser context. You can use it to navigate URLs and interact with the page content.

Using the expect_page method, you can act on a new window or wait for it to load. Once the new window appears, you can use Playwright’s assertion library to ensure that your user journey is successful.

When handling windows in Playwright, it’s important to understand that your automation may involve not just a single window but also multiple tabs or additional pop-up windows that open during user actions. Playwright treats each of these as a separate page, and your test must be structured to detect and interact with them correctly.

Let’s understand how to handle multiple windows and tabs in Playwright in the sections below.

How to Handle Multiple Windows in Playwright?

When working with web applications, certain actions, such as clicking a link, opening a pop-up, or triggering a new tab, can create additional browser windows.

Understanding how to handle multiple windows in Playwright ensures your tests remain stable and accurately simulate real user behavior.

To understand how this works, let’s consider a test scenario.

Test Scenario:

  • Visit the Website: Go to the LambdaTest Selenium Playground website, which provides examples for window and modal handling.
  • Locate the Multiple Window Popup Section: Scroll down to the section labeled “Open Twitter & Facebook Popup”.
  • Open the Popups: Click the button, and two new tabs (Twitter and Facebook) will open in the browser.

Code Implementation:

const [multiPage] = await Promise.all([
        page.waitForEvent("popup"),
        page.click("#followboth")
    ])
    await multiPage.waitForLoadState();


    const pages = multiPage.context().pages();
    console.log('No.of tabs: ' + pages.length);

Code Walkthrough:

  • Navigate to the Test Page: Open the windows.test.ts file and navigate to the LambdaTest Window Popup Demo URL.
  • Trigger Multiple Window Actions: Use Promise.all to simultaneously wait for the popup event and perform the button click action.
  • Click the Multi-Window Button: Call the click function on the #followboth element to open multiple popup tabs.
  • Capture the Multi-Window Event: Store the resulting popup event in a variable named multiPage (not newWindow) because multiple tabs will open.
  • Retrieve All Open Tabs: Use const pages = multiPage.context().pages(); to collect every active page in the browser context.
  • Log the Total Tab Count: Add console.log("No. of tabs: " + pages.length); to verify how many tabs were opened.
  • Print Each Tab’s URL: Use pages.forEach(tab => console.log(tab.url())); to output the URL of every open tab.

How to Handle Tabs in Playwright?

Handling tabs in Playwright is an essential part of handling windows in Playwright, especially when user actions open new browser tabs alongside pop-ups. Playwright allows you to detect, wait for, and interact with these tabs just like standalone pages, ensuring smooth workflows even when switching between multiple views. When combined with handling frames and windows in Playwright, this gives you full control over complex, multi-surface user journeys.

Test Scenario:

  • Open the Page: Visit the LambdaTest Selenium Playground Window Popup Demo page.
  • Trigger the Single Window Popup: Under the “Single Window Popup” section, click the “Follow On Twitter” link to open a new tab or pop-up.
  • Inspect and Interact: Inspect the link element and interact with it using the available XPath or link text.
  • Capture the New Tab URL: Retrieve the URL of the newly opened tab and log it in the terminal to verify successful navigation.

Code Implementation:

import { test } from "@playwright/test";
test("Handle tabs in Playwright", async ({ page }) => {
  // Navigate to the demo page
  await page.goto("https://www.lambdatest.com/selenium-playground/window-popup-modal-demo");
  console.log("Main page URL: " + page.url());
  // Click the "Follow On Twitter" link and wait for new tab
  const [newWindow] = await Promise.all([    page.waitForEvent("popup"),    page.click("'Follow On Twitter'")  ]);


  // Log the URL of the new tab
  console.log("New tab URL: " + newWindow.url());
});

Code Walkthrough:

  • Create Test File: Create a file named windows.test.ts in VS Code and ensure it is included in your playwright.config.ts.
  • Navigate to the Demo Page: Use page.goto() to open the LambdaTest Window Popup Demo page.
  • Trigger New Tab: Use Promise.all() to perform two actions simultaneously, waiting for the popup event while clicking the element that opens the new tab.
    • Wait for the Popup Event: Use page.waitForEvent("popup") to wait for the new tab to open.
    • Click the Follow On Twitter Link: Trigger the popup by clicking the link using page.click().
  • Capture the New Tab: Store the resulting popup page in a variable named newWindow.
  • Log URLs: Use console.log() to print both the main page URL and the new tab URL for verification.

Test Execution:

Open the terminal, and run the following command given below:

npx playwright test tests/windows.test.ts
local playwright test

Handling frames, iframes, windows, and tabs in Playwright locally can pose several challenges, such as browser inconsistencies, dynamic content like pop-ups or ads, and nested frames can lead to flaky tests.

Limited hardware resources make running multiple windows or tabs simultaneously difficult, and differences between local and production environments can cause unexpected failures. Observing complex test flows and debugging multi-window interactions also becomes cumbersome on a local setup.

Cloud-based platforms, such as LambdaTest, help overcome these challenges by providing multiple browser versions, operating systems, and screen resolutions for testing. They support scalable parallel execution, real-time debugging, and reliable network conditions, ensuring stable interactions with frames and windows.

With cloud testing, teams can efficiently run, monitor, and maintain complex Playwright automation scenarios, making tests faster, more reliable, and easier to manage.

How to Handle Frames and Windows in Playwright Tests at Scale?

LambdaTest is a GenAI-native test execution platform that allows you to perform manual and Playwright testing at scale across 3000+ browsers and OS combinations.

To get started with the LambdaTest platform, follow the steps below:

  • Set Environment Variables: Add your LambdaTest Username and Access Key as environment variables to authenticate your tests on the LambdaTest cloud platform.
  • Define Automation Capabilities: Specify the browser, version, platform, build name, test name, and additional settings such as network logs, video recording, and console logs to configure how your test will run on LambdaTest.
  • const capabilities = {
      browserName: "Chrome",
      browserVersion: "latest",
      "LT:Options": {
        platform: "Windows 10",
        build: "Playwright Test from config",
        name: "Playwright Windows Test",
        user: process.env.LT_USERNAME,
        accessKey: process.env.LT_ACCESS_KEY,
        network: true,
        video: true,
        console: true
      }
    };
    

    Use the LambdaTest Automation Capabilities Generator to create the capabilities block for any browser, OS, or configuration you need.

  • Connect Your Test Script to LambdaTest: Update your Playwright test script to connect using the LambdaTest WebSocket endpoint, ensuring your tests run on the LambdaTest cloud infrastructure.
  •  connectOptions: {
          wsEndpoint: `wss://cdp.lambdatest.com/playwright?capabilities=${encodeURIComponent(
            JSON.stringify(capabilities)
          )}`
        },

Test Execution:

LambdaTest-dashboard

To get started, refer to the documentation on Playwright testing with LambdaTest.

Advanced Techniques for Handling Windows in Playwright

Once you understand the basics of handling frames and windows in Playwright, the next level of complexity usually comes from managing multiple windows, tabs, and multi-page interactions. These advanced techniques help ensure your tests remain stable when dealing with dynamic browser behaviors such as pop-ups, external links, or multiple tabs.

When new windows or tabs open, Playwright may try to proceed before the pages finish loading. To prevent this, use waitForLoadState() after triggering actions like clicking the followboth button. This ensures every page reaches a reliable load state before your test continues.

Playwright supports three load states:

  • domcontentloaded: Triggered when the initial HTML document has been completely loaded and parsed, without waiting for images or stylesheets.
  • load: Fired when the entire page, including all dependent resources such as images, stylesheets, and scripts, has fully loaded.
  • networkidle: Occurs when there are no ongoing network requests for a specific period, indicating that the page is fully stable and idle.

Handling Windows in Playwright Using DOMContentLoaded Load State

When opening new windows or pop-ups in Playwright, it’s important to wait until the page is fully ready before interacting with elements. Using the domcontentloaded load state ensures that the HTML is completely parsed and ready for actions, even if other resources like images or scripts are still loading. This is especially useful when automating multi-window workflows.

To understand the working of the domcontentloaded load state in Playwright for handling windows in Playwright, let's take a test scenario.

Test Scenario:

  • Navigate to LambdaTest’s Selenium Playground Window Popup Demo page.
  • Click the “Open Twitter & Facebook Popup” button to open multiple windows.
  • Wait for each new page to reach the domcontentloaded state before performing any actions.

Code Implementation:

 import { test } from "@playwright/test";


test("Handle windows with domcontentloaded load state", async ({ page }) => {


  // Navigate to the test page
  await page.goto("https://www.lambdatest.com/selenium-playground/window-popup-modal");


  // Trigger pop-ups and wait for new page event
  const [multiPage] = await Promise.all([    page.waitForEvent("popup"),    page.click("#followboth")  ]);


  // Wait until the new pages reach DOMContentLoaded
  await multiPage.waitForLoadState("domcontentloaded");


  // Retrieve all open pages
  const pages = multiPage.context().pages();
  console.log("No. of tabs: " + pages.length);


  // Print URL of each open tab
  pages.forEach(tab => console.log(tab.url()));
});

Code Walkthrough:

  • Navigate to the Test Page: Open the LambdaTest Window Popup Demo page using page.goto().
  • Trigger Multiple Window Actions: Use Promise.all() to simultaneously wait for the popup event and click the #followboth button.
  • Wait for DOMContentLoaded: Call await multiPage.waitForLoadState("domcontentloaded") to ensure the new window’s HTML is fully parsed.
  • Retrieve All Open Tabs: Use const pages = multiPage.context().pages(); to get an array of all open windows or tabs.
  • Log Tab Count: Add console.log("No. of tabs: " + pages.length); to verify that all windows are detected.
  • Print Each Tab URL: Iterate through each page using pages.forEach(tab => console.log(tab.url())); to confirm which URLs are open.

Handling Windows in Playwright Using Load Load State

When automating multiple windows or pop-ups, you may need to ensure that all page resources, including images, scripts, stylesheets, and media, are completely loaded before interacting with the page. Using the load load state in Playwright ensures that the entire page and its associated resources are ready, making it ideal for resource-heavy or visually rich pages.

To understand the working of the load load state in Playwright for handling windows, let’s take a test scenario.

Test Scenario:

  • Navigate to LambdaTest’s Selenium Playground Window Popup Demo page.
  • Click the “Open Twitter & Facebook Popup” button to open multiple windows.
  • Wait for each newly opened page to reach the load state before performing any actions.

Code Implementation:

import { test } from "@playwright/test";

test("Handle windows with load state", async ({ page }) => {

  // Navigate to the test page
  await page.goto("https://www.lambdatest.com/selenium-playground/window-popup-modal");

  // Trigger pop-ups and wait for new page event
  const [multiPage] = await Promise.all([    page.waitForEvent("popup"),    page.click("#followboth")  ]);

  // Wait until the new pages are fully loaded (all resources)
  await multiPage.waitForLoadState("load");

  // Retrieve all open pages
  const pages = multiPage.context().pages();
  console.log("No. of tabs: " + pages.length);

  // Print URL of each open tab
  pages.forEach(tab => console.log(tab.url()));
});

Code Walkthrough:

  • Navigate to the Test Page: Open the LambdaTest Window Popup Demo page using page.goto().
  • Trigger Multiple Window Actions: Use Promise.all() to wait for the popup event while clicking the #followboth button.
  • Wait for Full Page Load: Call await multiPage.waitForLoadState("load") to ensure the HTML and all external resources are fully loaded.
  • Retrieve All Open Tabs: Use const pages = multiPage.context().pages(); to get all active pages or tabs.
  • Log Tab Count: Add console.log("No. of tabs: " + pages.length); to confirm all windows are detected.
  • Print Each Tab URL: Iterate through the pages using pages.forEach(tab => console.log(tab.url())); to check the URLs of all open windows.

Handling Windows in Playwright Using NetworkIdle Load State

When automating dynamic pages or multi-window workflows, some pages continue to load resources in the background such as AJAX calls or API requests. Using the networkidle load state ensures that all active network requests have finished and the page is completely idle before performing any further actions. This is very useful for dynamic web applications.

To understand the working of the networkidle load state in Playwright for handling windows in Playwright, let's take a test scenario.

Test Scenario:

  • Navigate to LambdaTest’s Selenium Playground Window Popup Demo page.
  • Click the “Open Twitter & Facebook Popup” button to open multiple windows.
  • Wait for each new page to reach the networkidle state before interacting with elements.

Code Implementation:

import { test } from "@playwright/test";

test("Handle windows with networkidle load state", async ({ page }) => {

  // Navigate to the test page
  await page.goto("https://www.lambdatest.com/selenium-playground/window-popup-modal");

  // Trigger pop-ups and wait for new page event
  const [multiPage] = await Promise.all([    page.waitForEvent("popup"),    page.click("#followboth")  ]);

  // Wait until the new pages are idle (no network requests for 500ms)
  await multiPage.waitForLoadState("networkidle");

  // Retrieve all open pages
  const pages = multiPage.context().pages();
  console.log("No. of tabs: " + pages.length);

  // Print URL of each open tab
  pages.forEach(tab => console.log(tab.url()));
});

Code Walkthrough:

  • Navigate to the Test Page: Open the LambdaTest Window Popup Demo page using page.goto().
  • Trigger Multiple Window Actions: Use Promise.all() to wait for the popup event while clicking the #followboth button.
  • Wait for Network Idle: Call await multiPage.waitForLoadState("networkidle") to ensure all network requests have finished and the page is fully idle before proceeding.
  • Retrieve All Open Tabs: Use const pages = multiPage.context().pages(); to fetch all currently active windows or tabs.
  • Log Tab Count: Add console.log("No. of tabs: " + pages.length); to verify that all windows were successfully detected.
  • Print Each Tab URL: Iterate through each page using pages.forEach(tab => console.log(tab.url())); to view URLs of all open tabs.

Interacting With Multiple Pages in Playwright

When working with multiple browser windows or tabs in Playwright, it becomes essential to identify which page corresponds to a specific URL and interact only with the required page. Playwright provides flexibility by allowing you to iterate through all open pages and selectively perform actions on the correct window, a key skill in accurate multi-window automation.

To understand how to interact with multiple web pages or frames in Playwright, let's consider the following test scenario.

Test Scenario:

  • Navigate to LambdaTest’s Selenium Playground Window Popup page.
  • In this scenario, three pages open. We will focus only on the 1st and 2nd pages for interaction, excluding the parent frame.

Code Implementation:

import { test } from "@playwright/test";

test("Interact with multiple pages", async ({ page }) => {

  // Navigate to the test page
  await page.goto("https://www.lambdatest.com/selenium-playground/window-popup-modal");

  // Trigger pop-ups and wait for new page events
  const [multiPage] = await Promise.all([
    page.waitForEvent("popup"),
    page.click("#followboth")
  ]);

  // Retrieve all open pages
  const pages = multiPage.context().pages();
  console.log("No. of tabs: " + pages.length);

  // Identify the Facebook page
  let facebookPage;
  for (let index = 0; index < pages.length; index++) {
      const url = pages[index].url();
      if (url === "https://www.facebook.com/lambdatest/") {
          facebookPage = pages[index];
      }
  }

  // Extract text from the Facebook page
  const text = await facebookPage.textContent("//h1");
  console.log(text);
});

Code Walkthrough:

  • Navigate to the Test Page: Open the LambdaTest Window Popup Demo page using page.goto().
  • Trigger Multiple Window Actions: Use Promise.all() to wait for the popup event while clicking the #followboth button.
  • Retrieve All Open Pages: Use const pages = multiPage.context().pages(); to access all open windows or tabs.
  • Identify the Target Page: Iterate through the pages array, compare each page’s URL, and assign the Facebook page to facebookPage when a match is found.
  • Extract Content: Use facebookPage.textContent("//h1") to extract specific text from the identified page.
  • Log Results: Print the retrieved text using console.log(text) to confirm successful interaction with the correct window.

Conclusion

Handling frames, windows, and multiple pages in Playwright is a crucial skill for automating complex web applications. Frames, including nested iframes, allow you to interact with embedded content that exists outside the main page context, and Playwright provides APIs like page.frame(), page.frames(), and frameLocator() to facilitate these interactions safely.

When dealing with new windows or tabs, Playwright treats each as a separate Page object, which can be captured using page.waitForEvent('popup') and interacted with just like any other page, ensuring seamless automation across multiple surfaces. Stability in multi-window and multi-frame workflows is enhanced by waiting for specific load states such as domcontentloaded, load, or networkidle, which guarantees that the page’s content and resources are fully available before any actions are performed.

By combining these techniques, testers can precisely target frames, navigate between tabs, and perform reliable interactions even on dynamic, resource-heavy, or nested page layouts. Overall, Playwright’s capabilities for handling frames and windows make it possible to create resilient and maintainable automation scripts that accurately replicate real user journeys, while cloud-based testing platforms further enhance scalability and consistency across different browsers and operating systems.

Frequently Asked Questions (FAQs)

Difference between page.frame() and page.frameLocator()
page.frame() retrieves a specific frame object from the page, allowing direct frame-level interactions like fill() or click(). page.frameLocator() provides a locator tied to a frame, enabling Playwright's chainable locator methods (locator(), getByRole(), etc.) inside the frame. frameLocator() is safer for dynamic frames that load asynchronously.
Detecting dynamically added frames
Use page.on('frameattached') to detect when a new <iframe> is added to the DOM. This helps interact with frames loaded after the initial page load, such as ads or widgets. Combining it with page.on('framenavigated') allows detecting frame navigation as well.
Handling cross-origin iframes
Direct DOM access in cross-origin iframes is restricted. You can interact using frameLocator() and locator methods, waiting for the frame to load fully with frame.waitForLoadState() or specific elements inside. User interactions like click(), fill(), or press() are recommended over JavaScript evaluation.
Determining the hierarchy of nested frames
Use page.mainFrame() to access the main document and recursively call frame.childFrames() to traverse all nested frames. This allows visualization of the frame tree and helps target the correct frame for interactions.
Handling modals or pop-ups that don’t trigger popup events
Some modals are dynamic elements rather than new windows. Instead of page.waitForEvent('popup'), use locators to interact with the modal, wait for its appearance with locator.waitFor(), check visibility with locator.isVisible(), and perform actions directly on modal elements.
Switching between multiple tabs
Use context.pages() to access all pages in a context and store page objects in variables. For new tabs, use const newTab = await page.waitForEvent('popup') and switch back as needed. page.bringToFront() can activate a tab before interaction.
Making tests resilient with asynchronous frames or windows
Wait for frames or elements to load dynamically using frame.waitForSelector() or frame.waitForLoadState(). Combine with optional chaining or conditional checks to prevent errors when elements or frames are temporarily unavailable, ensuring reliable test execution.
Running assertions inside nested frames
Access the correct frame using frameLocator() or chaining frame.frameLocator() to reach nested iframes. Once inside, use Playwright’s expect() API to assert text, visibility, or input values, enabling validation in complex multi-frame layouts.
Improving multi-window test stability with waitForLoadState()
waitForLoadState() pauses execution until a window or tab reaches a desired state (domcontentloaded, load, or networkidle), ensuring that interactions like clicks or form fills occur only when the page is fully ready, reducing flaky tests.
Debugging frame and window interactions effectively
Use tools like page.pause(), screenshots, video recording, console logs, and Playwright Trace Viewer to inspect frame counts, URLs, element availability, network requests, and user actions. These help identify exactly where and why a frame or window interaction fails.

Did you find this page helpful?

Helpful

NotHelpful

More Related Hubs

ShadowLT Logo

Start your journey with LambdaTest

Get 100 minutes of automation test minutes FREE!!