PC Slower Than It Used to Be?
A free scan shows the junk files, broken settings and background clutter dragging Windows down - then fixes them in one click.Free scan · Windows 10 & 11Outdated Drivers Are Slowing You Down
One free scan finds every outdated or missing driver and matches the right update for your exact hardware.Free scan · exact hardware matchIn Playwright, a new browser tab is a Page in the same BrowserContext. Start waiting for the popup or page before the click that opens it, await the resulting object, wait for the navigation state you need, and then read its URL with popup.url() (JavaScript/TypeScript) or popup.url (Python).
const popupPromise = page.waitForEvent('popup');
await page.getByText('open new tab').click();
const popup = await popupPromise;
await popup.waitForLoadState('domcontentloaded');
console.log(popup.url());
That ordering prevents the most common race condition: the tab opens before your test starts listening.
Read a popup URL in JavaScript or TypeScript
Use the page-level popup event when an action on the current page opens a child tab or window, such as a link with target="_blank" or a script calling window.open().
- Create the event promise.
- Perform the click or other action that opens the tab.
- Await the returned
Page. - Wait for an appropriate load or URL condition.
- Read
popup.url().
import { test, expect } from '@playwright/test';
test('gets the URL of a new tab', async ({ page }) => {
const popupPromise = page.waitForEvent('popup');
await page.getByRole('link', { name: 'Open new tab' }).click();
const popup = await popupPromise;
await popup.waitForLoadState('domcontentloaded');
const url = popup.url();
console.log(url);
expect(url).toMatch(/^https?:///);
});
The popup event is available once the new page has navigated to its initial URL. If the site redirects or performs another navigation afterward, the initial value may not be the final URL.
Recommended Free Tools
#1 Best Overall
Wait for the final URL after redirects
When the destination is determined by a redirect, wait for the URL pattern you actually need before reading it.
const popupPromise = page.waitForEvent('popup');
await page.getByText('Continue').click();
const popup = await popupPromise;
await popup.waitForURL('**/complete');
const finalUrl = popup.url();
console.log(finalUrl);
You can use a glob, a regular expression, or a predicate with waitForURL. Choose a condition that represents the business outcome rather than sleeping for an arbitrary number of milliseconds.
Read the URL directly from the document
popup.url() is the direct Page API method. If you need the browser’s current location inside the page, you can also evaluate it after navigation:
const href = await popup.evaluate(() => location.href);
Both approaches return a string. The Page method is normally simpler and does not require code execution in the page.
Rank #2
Catch any new tab in the browser context
Use context.waitForEvent('page') when any page created in the context is relevant, including pages opened by popups. This is useful when the opener is uncertain, when several pages can launch tabs, or when the action is not conveniently tied to one page.
const newPagePromise = context.waitForEvent('page');
await page.getByText('Open new tab').click();
const newPage = await newPagePromise;
await newPage.waitForLoadState('domcontentloaded');
const url = newPage.url();
console.log(url);
The context event has a wider scope than the page-level popup event. If two tabs can open at once, associate each action with its own controlled context or add URL assertions after the event so that the test does not accidentally use the wrong page.
Choosing between popup and page
| Situation | Wait for | Why |
|---|---|---|
| The current page action opens one child tab | page.waitForEvent('popup') |
It scopes the event to that opener. |
| Any page in the context may be created | context.waitForEvent('page') |
It observes every new Page in the context, including popups. |
| The URL changes after the initial navigation | Either event, then waitForURL |
Event delivery alone does not guarantee the final redirect has completed. |
Python async equivalent
Python’s async API uses an expect_popup() context manager. Put the click inside the context manager so Playwright begins listening before the action.
import asyncio
from playwright.async_api import async_playwright
async def main():
async with async_playwright() as p:
browser = await p.chromium.launch(headless=True)
context = await browser.new_context()
page = await context.new_page()
await page.goto("https://example.com")
async with page.expect_popup() as popup_info:
await page.get_by_text("Open new tab").click()
popup = await popup_info.value
await popup.wait_for_load_state("domcontentloaded")
url = popup.url
print(url)
await browser.close()
asyncio.run(main())
For a context-wide wait, use context.expect_page() around the action, then read the returned Page’s url property.
Free tools Windows power users keep installed
One-click scans. No signup required.
Rank #3
async with context.expect_page() as page_info:
await page.get_by_text("Open new tab").click()
new_page = await page_info.value
await new_page.wait_for_load_state("domcontentloaded")
print(new_page.url)
Why the URL is empty, wrong, or missed
The listener starts after the click
This is the classic event race. A fast popup can be created and navigated before a later waitForEvent call runs. Always create the promise first (or enter Python’s expectation context first), then trigger the action.
You read the URL too early
A popup can exist while its document is still loading. Wait for domcontentloaded when you need the initial document, or wait for a specific URL with waitForURL when redirects and client-side navigation matter.
You used the wrong event scope
If the tab is opened by a different frame or page, a page-level popup listener on the wrong opener will never resolve. Use the opener’s popup event when known; otherwise listen on the BrowserContext.
The action did not create a new Page
Some links navigate the existing tab, open a same-tab modal, or are blocked by application logic. Verify the element’s behavior and check whether page.url() changed. If the current page navigates, wait with page.waitForURL instead of expecting a popup.
A bot check or permission prompt blocks navigation
Headless runs can encounter authentication, consent, popup-blocking, or bot-detection flows. Capture a trace or screenshot, inspect console and network errors, and confirm the test context has the required cookies, permissions, and headers. Do not replace event synchronization with a long fixed timeout.
Multiple tabs open together
A broad context listener may capture a background tab, analytics page, or another test’s popup. Isolate tests with separate contexts, avoid concurrent actions that create pages, and assert the captured page’s URL before using it.
Reliable patterns for real test suites
Return the Page and URL from a helper
async function openTabAndGetUrl(page, trigger) {
const popupPromise = page.waitForEvent('popup');
await trigger();
const popup = await popupPromise;
await popup.waitForLoadState('domcontentloaded');
return { popup, url: popup.url() };
}
const { popup, url } = await openTabAndGetUrl(
page,
() => page.getByRole('button', { name: 'Preview' }).click()
);
console.log(url);
Returning both values lets later steps interact with the tab without reopening it and keeps URL capture in one synchronized operation.
Use a targeted readiness condition
domcontentloaded is a useful baseline for reading a URL. For an application that changes routes after rendering, combine the popup wait with waitForURL or a web assertion for a page-specific element. Playwright documents networkidle as discouraged for testing; it can make tests slow and still does not prove that the application reached the state you care about.
Close the page when it is no longer needed
Many opened tabs consume memory, connections, and renderer processes. After assertions and extraction, call await popup.close() (or the Python equivalent) unless the page must remain available for subsequent steps.
Use context.pages() for diagnostics, not primary synchronization
context.pages() returns all currently open pages, which is useful for logging and cleanup. It is less reliable for matching one click to one tab because it does not tell you which page was created by a particular action. Event waiting preserves that association.
Timeouts, errors, and fixes
| Symptom | Likely cause | Fix |
|---|---|---|
page.waitForEvent('popup') timed out |
No popup was created, or the listener watched the wrong opener. | Confirm the click opens a tab; use context.waitForEvent('page') if the opener is unknown. |
| The captured URL is the login or tracking URL | Redirects were still running. | Call waitForURL with the final route before reading url(). |
| The test intermittently misses the tab | The wait was registered after the action. | Create the promise or expectation context before clicking. |
| The URL never changes | The site opened a modal or reused the current page. | Inspect the DOM and wait on the existing page instead of expecting a new Page. |
| Tests interfere with one another | Shared context or simultaneous popup creation. | Use an isolated BrowserContext per test and avoid ambiguous concurrent triggers. |
Performance and reliability considerations
- Prefer one event wait per action. It avoids scanning all pages and gives deterministic ownership of the result.
- Use the smallest readiness wait. URL capture usually needs a navigation or URL condition, not full image loading.
- Keep browser reuse deliberate. Reusing a browser process is efficient, while separate contexts provide cookie and page isolation.
- Record failures. On timeout, save a trace, screenshot, current URLs from
context.pages(), and the triggering action so you can distinguish application behavior from test synchronization. - Account for redirects. The URL at popup creation can differ from the URL after authentication, locale selection, or client-side routing.
Or skip the browser setup
If your goal is a clean image or PDF of a URL rather than interactive tab behavior, ScreenshotNeo provides a single HTTP request. It accepts cookie and consent banners like a visitor, then removes more than 60 known consent platforms, newsletter popups, and chat widgets; each cleanup step can be disabled. Bot checks or CAPTCHAs, blank pages, timeouts, failed loads, and cache hits are not billed, and response headers report the page verdict and billing status.
Use the documented API options for full-page captures, lazy-loaded images, CSS-selector elements, device and viewport settings, dark mode, retina scale, PDF output, custom CSS or JavaScript, clicks, waits, blocked resources, headers, cookies, user agents, authorization, timezone, geolocation, transparent backgrounds, resizing, TTL caching, signed image links, asynchronous webhooks, bulk capture, and usage reporting. An MCP server exposes take_screenshot, get_page_info, and capture_pdf to Claude, Cursor, and other MCP clients.
cURL:
curl -G "https://api.screenshotneo.com/v1/shot" -d access_key=YOUR_API_KEY --data-urlencode url=https://stripe.com -o shot.webp
Python:
import requests
r = requests.get("https://api.screenshotneo.com/v1/shot", params={"access_key": "YOUR_API_KEY", "url": "https://stripe.com"}, timeout=90)
open("shot.webp", "wb").write(r.content)
Node.js:
const q = new URLSearchParams({ access_key: 'YOUR_API_KEY', url: 'https://stripe.com' });
const res = await fetch(`https://api.screenshotneo.com/v1/shot?${q}`);
See the full parameter reference in the ScreenshotNeo documentation. The Free plan includes 1,000 shots per month without a card; paid plans start at $5 for 3,000 shots, and every feature is available on every plan. Sign up for the free plan.
Frequently Asked Questions
Does a new tab have a different BrowserContext?
No. A popup is a new Playwright Page in the existing BrowserContext unless your code explicitly creates another context.
Can I get the URL without waiting for the whole page to load?
Yes. Await the popup event, then read its initial URL; use waitForURL when the final URL depends on redirects or later navigation.
When should I use context.pages()?
Use it to inspect or clean up currently open pages. For associating one new tab with one action, an event wait is safer.
Do these 3 things before closing this tab:
1Fix the driver behind crashes, sound loss and screen glitches2Repair Windows errors before they cause bigger problems3Scan for outdated or missing drivers - takes under a minuteQuick Recap
Product prices and availability are accurate as of the date/time indicated and are subject to change. Any price and availability information displayed on Amazon at the time of purchase will apply.




