Playwright Python 1.60.0 release with HAR tracing and drop locators
Playwright Python 1.60.0 ships HAR recording as a tracing API, a new drop() method on locators, and soft assertions for pytest. The release was published on May 18, 2026 and pulls the bundled browsers up to Chromium 148, Firefox 150, and WebKit 26.4.
The full release notes and downloads are on the GitHub release page.
Python ergonomics
A few things in this drop will matter the day you upgrade.
All timeout parameters now accept datetime.timedelta. That works for page.goto(), locator.click(), expect(...).to_be_visible(), and everywhere else a timeout is taken. No more passing raw milliseconds and trying to remember the unit.
expect.soft(...) collects multiple failures in a test instead of stopping at the first. You need the up to date pytest integration for it to behave properly.
A new FormData class builds request bodies that the browser would send as multipart/form-data. Pass it to api_request_context.post(form=...) and similar request methods.
Other small but useful bits:
async withnow works on objects returned fromscreencast.show_actions()andscreencast.show_overlays().page.expect_request()andpage.expect_response()accept async predicates.expect_eventandwait_for_eventcarry typed overloads, so the payload type is inferred from the event name.
HAR recording lives on tracing
tracing.start_har() and tracing.stop_har() expose HAR recording as a first class tracing API. Same content, mode, and url_filter options as the older record_har configuration, just available at runtime through the tracing handle:
context.tracing.start_har("trace.har")
page = context.new_page()
page.goto("https://playwright.dev")
context.tracing.stop_har()
Handy when the same test setup that turned on tracing is also the right place to capture the network log, instead of pinning HAR options up at context creation time.
locator.drop() for upload zones
locator.drop() simulates an external drag and drop of files or clipboard style payloads onto an element. Playwright dispatches dragenter, dragover, and drop with a synthetic DataTransfer in the page context. It works across browsers and targets the upload zones that every web app eventually grows.
page.locator("#dropzone").drop(
files={"name": "note.txt", "mime_type": "text/plain", "buffer": b"hello"},
)
page.locator("#dropzone").drop(
data={
"text/plain": "hello world",
"text/uri-list": "https://example.com",
},
)
If you have ever wired a fake drop event by hand inside evaluate(), this replaces that pile.
Aria snapshots, locators, and context events
expect(page).to_match_aria_snapshot() now works on a Page, not only a Locator. It is equivalent to asserting against page.locator("body"). The new boxes option on locator.aria_snapshot() and page.aria_snapshot() appends each element bounding box as [box=x,y,width,height], aimed at feeding the snapshot to AI tooling.
BrowserContext now mirrors lifecycle events from its pages: download, frameattached, framedetached, framenavigated, pageclose, and pageload. The browser itself also fires a context event when a new context is created. That removes a class of hand wired listeners around the existing page event.
Smaller additions:
get_by_role()takes adescriptionoption to match the accessible description.expect(locator).to_have_css()takes apseudooption to read computed styles from::beforeor::after.locator.highlight()takes astyleoption for extra inline CSS, pluspage.hide_highlight()clears all highlights.web_socket_route.protocols()returns the WebSocket subprotocols requested by the page.browser_type.connect_over_cdp()takesno_defaultsso attaching to a real user browser does not stomp on its download, focus, or media settings.web_error.location()mirrorsconsole_message.location().console_message.location()now exposeslineandcolumnproperties;line_numberandcolumn_numberare deprecated.
Trace Viewer also gains a pretty print toggle for JSON and form bodies in the network details panel.
Upgrade notes
One breaking change: the long deprecated handle option on browser_context.expose_binding() and page.expose_binding() is gone. If a test still passes handle=... to either call, it will fail. Strip the argument.
Bundled browser versions:
- Chromium 148.0.7778.96
- Firefox 150.0.2
- WebKit 26.4
Also validated against Google Chrome 147 and Microsoft Edge 147.
Where to get it
- Release: github.com/microsoft/playwright-python releases v1.60.0
- Repo: https://github.com/microsoft/playwright-python
- Tag:
v1.60.0