Content that lives in another document: a page inside a page, a frame inside a frame, and new tabs and windows your test has to switch to.
Every solution on this page starts with this hook. With baseURL set to https://hiraanachwani.com in your playwright.config.ts, it opens this page before each test.
The box below is a separate page loaded in an iframe. Locators on the main page can't see inside it.
Try this
Type Hi from Playwright into the Message field inside the frame and click Send.
Check the frame shows Sent: Hi from Playwright.
Check that page.getByLabel('Message') on the main page finds nothing.
Locator tip: find the iframe element first, then step into it with contentFrame(). Every locator after that searches inside the frame: page.getByTitle('Simple frame').contentFrame().getByLabel('Message').
Show Playwright solution
test('iframe', async ({ page }) => {
const frame = page.getByTitle('Simple frame').contentFrame();
await frame.getByLabel('Message').fill('Hi from Playwright');
await frame.getByRole('button', { name: 'Send' }).click();
await expect(frame.getByTestId('frame-result')).toHaveText('Sent: Hi from Playwright');
// The main page can't see inside the frame
await expect(page.getByLabel('Message')).toHaveCount(0);
});
Nested iframes
A frame inside a frame. You have to step into each one in turn.
Try this
Check the outer frame's heading is Outer frame.
Click the button in the inner frame and check it shows Inner button clicked.
Locator tip: chain contentFrame() once per level: the outer iframe, then the inner iframe inside it. Store each frame in a variable so the test reads top-down.
Click Open new tab and check the new tab's heading is You're in a new tab.
Check its URL ends with /practice/frames/new-tab.
Close the new tab and check the original page is still usable.
Locator tip: start waiting for the tab before you click: const tabPromise = page.context().waitForEvent('page'). If you wait after the click, the event may already have fired.
Show Playwright solution
test('new tab', async ({ page }) => {
const tabPromise = page.context().waitForEvent('page');
await page.getByRole('link', { name: 'Open new tab' }).click();
const newTab = await tabPromise;
await expect(newTab.getByRole('heading', { level: 1 })).toHaveText("You're in a new tab");
await expect(newTab).toHaveURL(/\/practice\/frames\/new-tab$/);
await newTab.close();
await expect(page.getByRole('link', { name: 'Open new tab' })).toBeVisible();
});
Popup window
A button that opens a small window with window.open(). The main page notices when it closes.
Try this
Open the popup and check its heading is This is a popup window.
Check the main page says Popup open.
Click Close this window in the popup and check the main page says Popup closed.
Locator tip:page.waitForEvent('popup') gives you the window this page opened. It's a full page object, so getByRole and expect work on it as usual.