From a5650b5182eee364b5b926573559b9f31fb19fe1 Mon Sep 17 00:00:00 2001 From: "microsoft-playwright-automation[bot]" <203992400+microsoft-playwright-automation[bot]@users.noreply.github.com> Date: Wed, 17 Jun 2026 11:42:34 +0000 Subject: [PATCH] feat(roll): roll to ToT Playwright (17-06-26) --- dotnet/docs/api/class-browsertype.mdx | 4 + dotnet/docs/api/class-credentials.mdx | 71 +++++++-- dotnet/docs/api/class-webstorage.mdx | 8 +- dotnet/docs/docker.mdx | 2 + dotnet/docs/getting-started-cli.mdx | 1 + dotnet/docs/intro.mdx | 4 +- dotnet/docs/release-notes.mdx | 55 +++++++ java/docs/api/class-browsertype.mdx | 4 + java/docs/api/class-credentials.mdx | 69 +++++++-- java/docs/api/class-webstorage.mdx | 10 +- java/docs/docker.mdx | 2 + java/docs/getting-started-cli.mdx | 1 + java/docs/intro.mdx | 4 +- java/docs/release-notes.mdx | 53 +++++++ nodejs/docs/api/class-browsercontext.mdx | 3 + nodejs/docs/api/class-browsertype.mdx | 4 + nodejs/docs/api/class-credentials.mdx | 26 ++-- nodejs/docs/api/class-frame.mdx | 9 ++ nodejs/docs/api/class-page.mdx | 18 +++ nodejs/docs/api/class-websocket.mdx | 3 + nodejs/docs/api/class-webstorage.mdx | 8 +- nodejs/docs/api/class-worker.mdx | 3 + nodejs/docs/auth.mdx | 5 +- nodejs/docs/ci.mdx | 20 +-- nodejs/docs/docker.mdx | 20 +-- nodejs/docs/getting-started-cli.mdx | 1 + nodejs/docs/intro.mdx | 4 +- nodejs/docs/release-notes.mdx | 66 +++++++++ python/docs/api/class-browsertype.mdx | 4 + python/docs/api/class-credentials.mdx | 168 ++++++++++++++++++++-- python/docs/api/class-webstorage.mdx | 8 +- python/docs/docker.mdx | 2 + python/docs/getting-started-cli.mdx | 1 + python/docs/intro.mdx | 52 ++++++- python/docs/library.mdx | 28 ++++ python/docs/release-notes.mdx | 54 +++++++ src/components/GitHubStarButton/index.tsx | 2 +- 37 files changed, 708 insertions(+), 89 deletions(-) diff --git a/dotnet/docs/api/class-browsertype.mdx b/dotnet/docs/api/class-browsertype.mdx index ba4bbf9cbf..1042c70f38 100644 --- a/dotnet/docs/api/class-browsertype.mdx +++ b/dotnet/docs/api/class-browsertype.mdx @@ -100,6 +100,10 @@ Connecting over the Chrome DevTools Protocol is only supported for Chromium-base This connection is significantly lower fidelity than the Playwright protocol connection via [BrowserType.ConnectAsync()](/api/class-browsertype.mdx#browser-type-connect). If you are experiencing issues or attempting to use advanced functionality, you probably want to use [BrowserType.ConnectAsync()](/api/class-browsertype.mdx#browser-type-connect). ::: +:::warning +Playwright maintains a curated list of arguments for launching the browser. If you launch the browser without Playwright and do not pass the exact same arguments, some of Playwright functionality may be broken upon connecting to the browser. +::: + **Usage** ```csharp diff --git a/dotnet/docs/api/class-credentials.mdx b/dotnet/docs/api/class-credentials.mdx index 61d65855a7..a80ab3288b 100644 --- a/dotnet/docs/api/class-credentials.mdx +++ b/dotnet/docs/api/class-credentials.mdx @@ -13,8 +13,59 @@ There are two common ways to use it: **Usage: seed a known credential** +```csharp +var context = await browser.NewContextAsync(); + +// A passkey your backend already provisioned for a test user. +await context.Credentials.CreateAsync("example.com", new() +{ + Id = knownCredentialId, // base64url + UserHandle = knownUserHandle, // base64url + PrivateKey = knownPrivateKey, // base64url PKCS#8 (DER) + PublicKey = knownPublicKey, // base64url SPKI (DER) +}); +await context.Credentials.InstallAsync(); + +var page = await context.NewPageAsync(); +await page.GotoAsync("https://example.com/login"); +// The page's navigator.credentials.get() is answered with the seeded passkey. +``` + **Usage: capture a passkey, then reuse it** +```csharp +// setup test: let the app register a passkey, then save it. +var context = await browser.NewContextAsync(); +await context.Credentials.InstallAsync(); + +var page = await context.NewPageAsync(); +await page.GotoAsync("https://example.com/register"); +await page.GetByRole(AriaRole.Button, new() { Name = "Create a passkey" }).ClickAsync(); + +// Read back the passkey the page registered — it includes the private key. +var credentials = await context.Credentials.GetAsync(new() { RpId = "example.com" }); +File.WriteAllText("playwright/.auth/passkey.json", JsonSerializer.Serialize(credentials[0])); +``` + +```csharp +// later test: seed the captured passkey so the app starts already enrolled. +var credential = JsonSerializer.Deserialize( + File.ReadAllText("playwright/.auth/passkey.json")); +var context = await browser.NewContextAsync(); +await context.Credentials.CreateAsync(credential.RpId, new() +{ + Id = credential.Id, + UserHandle = credential.UserHandle, + PrivateKey = credential.PrivateKey, + PublicKey = credential.PublicKey, +}); +await context.Credentials.InstallAsync(); + +var page = await context.NewPageAsync(); +await page.GotoAsync("https://example.com/login"); +// navigator.credentials.get() resolves the captured passkey — already signed in. +``` + **Defaults** @@ -28,20 +79,23 @@ There are two common ways to use it: Seeds a virtual WebAuthn credential and returns it. -With only `rpId`, generates a fresh **ECDSA P-256** keypair, credential id and user handle. The seeded credential is discoverable (resident), so the page can resolve it from both username-then-passkey and usernameless passkey flows. The returned object carries the `privateKey` and `publicKey`, so it can be persisted to disk and re-seeded in a later test. +With only [rpId](/api/class-credentials.mdx#credentials-create-option-rp-id), generates a fresh **ECDSA P-256** keypair, credential id and user handle. The seeded credential is discoverable (resident), so the page can resolve it from both username-then-passkey and usernameless passkey flows. The returned object carries the private and public keys, so it can be persisted to disk and re-seeded in a later test. -To **import a known credential**, supply all four of `id`, `userHandle`, `privateKey` and `publicKey` together. +To **import a known credential**, supply all four of [Id](/api/class-credentials.mdx#credentials-create-option-id), [UserHandle](/api/class-credentials.mdx#credentials-create-option-user-handle), [PrivateKey](/api/class-credentials.mdx#credentials-create-option-private-key) and [PublicKey](/api/class-credentials.mdx#credentials-create-option-public-key) together. Call [Credentials.InstallAsync()](/api/class-credentials.mdx#credentials-install) before navigating to a page that uses WebAuthn. **Usage** ```csharp -await Credentials.CreateAsync(options); +await Credentials.CreateAsync(rpId, options); ``` **Arguments** -- `options` `CredentialsCreateOptions?` +- `rpId` [string]# + + Relying party id (typically the site's effective domain). +- `options` `CredentialsCreateOptions?` *(optional)* - `Id` [string]? *(optional)*# Base64url-encoded credential id. Auto-generated if omitted. @@ -51,9 +105,6 @@ await Credentials.CreateAsync(options); - `PublicKey` [string]? *(optional)*# Base64url-encoded SPKI (DER) public key. Auto-generated if omitted. - - `RpId` [string]# - - Relying party id (typically the site's effective domain). - `UserHandle` [string]? *(optional)*# Base64url-encoded user handle. Auto-generated if omitted. @@ -104,9 +155,9 @@ await Credentials.DeleteAsync(id); Added in: v1.61credentials.GetAsync -Returns every credential currently held by the authenticator, optionally filtered by `rpId` or `id`. This includes both credentials seeded with [Credentials.CreateAsync()](/api/class-credentials.mdx#credentials-create) and credentials the page registered itself by calling `navigator.credentials.create()`. +Returns every credential currently held by the authenticator, optionally filtered by [RpId](/api/class-credentials.mdx#credentials-get-option-rp-id) or [Id](/api/class-credentials.mdx#credentials-get-option-id). This includes both credentials seeded with [Credentials.CreateAsync()](/api/class-credentials.mdx#credentials-create) and credentials the page registered itself by calling `navigator.credentials.create()`. -Each returned credential includes its `privateKey` and `publicKey`, so a passkey the app just registered can be saved and re-seeded into a later test with [Credentials.CreateAsync()](/api/class-credentials.mdx#credentials-create) — see the second example in the class overview. +Each returned credential includes its private and public keys, so a passkey the app just registered can be saved and re-seeded into a later test with [Credentials.CreateAsync()](/api/class-credentials.mdx#credentials-create) — see the second example in the class overview. **Usage** @@ -148,7 +199,7 @@ await Credentials.GetAsync(options); Installs the virtual WebAuthn authenticator into the context, overriding `navigator.credentials.create()` and `navigator.credentials.get()` in all current and future pages. Call this before the page first touches `navigator.credentials`. -Required: until `install()` is called, no interception is in place and the page sees the platform's native (or absent) WebAuthn behaviour. Seeding credentials with [Credentials.CreateAsync()](/api/class-credentials.mdx#credentials-create) without `install()` populates the authenticator, but the page will never see those credentials. +Required: until [Credentials.InstallAsync()](/api/class-credentials.mdx#credentials-install) is called, no interception is in place and the page sees the platform's native (or absent) WebAuthn behaviour. Seeding credentials with [Credentials.CreateAsync()](/api/class-credentials.mdx#credentials-create) without installing populates the authenticator, but the page will never see those credentials. **Usage** diff --git a/dotnet/docs/api/class-webstorage.mdx b/dotnet/docs/api/class-webstorage.mdx index 44c234f4b7..e2cf574f19 100644 --- a/dotnet/docs/api/class-webstorage.mdx +++ b/dotnet/docs/api/class-webstorage.mdx @@ -46,7 +46,7 @@ await WebStorage.ClearAsync(); Added in: v1.61webStorage.GetItemAsync -Returns the value for the given `name`, or `null` if the key is not present. +Returns the value for the given [name](/api/class-webstorage.mdx#web-storage-get-item-option-name) if present. **Usage** @@ -68,7 +68,7 @@ await WebStorage.GetItemAsync(name); Added in: v1.61webStorage.ItemsAsync -Returns all items in the storage as `name`/`value` pairs. +Returns all items in the storage as name/value pairs. **Usage** @@ -90,7 +90,7 @@ await WebStorage.ItemsAsync(); Added in: v1.61webStorage.RemoveItemAsync -Removes the item with the given `name`. No-op if the item is absent. +Removes the item with the given [name](/api/class-webstorage.mdx#web-storage-remove-item-option-name). No-op if the item is absent. **Usage** @@ -112,7 +112,7 @@ await WebStorage.RemoveItemAsync(name); Added in: v1.61webStorage.SetItemAsync -Sets the value for the given `name`. Overwrites any existing value for that name. +Sets the value for the given [name](/api/class-webstorage.mdx#web-storage-set-item-option-name). Overwrites any existing value for that name. **Usage** diff --git a/dotnet/docs/docker.mdx b/dotnet/docs/docker.mdx index 8d6f27c123..294027150e 100644 --- a/dotnet/docs/docker.mdx +++ b/dotnet/docs/docker.mdx @@ -135,6 +135,7 @@ We currently publish images with the following tags: - `:v1.60.0` - Playwright v1.60.0 release docker image based on Ubuntu 24.04 LTS (Noble Numbat). - `:v1.60.0-noble` - Playwright v1.60.0 release docker image based on Ubuntu 24.04 LTS (Noble Numbat). - `:v1.60.0-jammy` - Playwright v1.60.0 release docker image based on Ubuntu 22.04 LTS (Jammy Jellyfish). +- `:v1.60.0-resolute` - Playwright v1.60.0 release docker image based on Ubuntu 26.04 LTS (Resolute Raccoon). :::note @@ -144,6 +145,7 @@ It is recommended to always pin your Docker image to a specific version if possi ### Base images We currently publish images based on the following [Ubuntu](https://hub.docker.com/_/ubuntu) versions: +- **Ubuntu 26.04 LTS** (Resolute Raccoon), image tags include `resolute` - **Ubuntu 24.04 LTS** (Noble Numbat), image tags include `noble` - **Ubuntu 22.04 LTS** (Jammy Jellyfish), image tags include `jammy` diff --git a/dotnet/docs/getting-started-cli.mdx b/dotnet/docs/getting-started-cli.mdx index 2ac184da29..893def3363 100644 --- a/dotnet/docs/getting-started-cli.mdx +++ b/dotnet/docs/getting-started-cli.mdx @@ -32,6 +32,7 @@ playwright-cli --help Alternatively, install `@playwright/cli` as a local dependency and use `npx`: ```bash +npm install -D @playwright/cli@latest npx playwright-cli --help ``` diff --git a/dotnet/docs/intro.mdx b/dotnet/docs/intro.mdx index 703841ea58..c9f4bd587f 100644 --- a/dotnet/docs/intro.mdx +++ b/dotnet/docs/intro.mdx @@ -275,8 +275,8 @@ See our doc on [Running and Debugging Tests](./running-tests.mdx) to learn more ## System requirements - Playwright is distributed as a .NET Standard 2.0 library. We recommend .NET 8. - Windows 11+, Windows Server 2019+ or Windows Subsystem for Linux (WSL). -- macOS 14 Sonoma, or later. -- Debian 12, Debian 13, Ubuntu 22.04, Ubuntu 24.04, on x86-64 and arm64 architecture. +- macOS 14 (Sonoma) or later. +- Debian 12 / 13, Ubuntu 22.04 / 24.04 / 26.04 (x86-64 or arm64). ## What's next - [Write tests using web first assertions, page fixtures and locators](./writing-tests.mdx) diff --git a/dotnet/docs/release-notes.mdx b/dotnet/docs/release-notes.mdx index 12c35a72af..f510706f5b 100644 --- a/dotnet/docs/release-notes.mdx +++ b/dotnet/docs/release-notes.mdx @@ -9,6 +9,61 @@ import HTMLCard from '@site/src/components/HTMLCard'; import LiteYouTube from '@site/src/components/LiteYouTube'; +## Version 1.61 + +### 🔑 WebAuthn passkeys + +New [Credentials] virtual authenticator, available via [BrowserContext.Credentials](/api/class-browsercontext.mdx#browser-context-credentials), lets tests register passkeys and answer `navigator.credentials.create()` / `navigator.credentials.get()` ceremonies in the page — no real hardware key required, works in all browsers: + +```csharp +var context = await browser.NewContextAsync(); + +// Seed a passkey your backend provisioned for a test user. +await context.Credentials.CreateAsync("example.com", new() +{ + Id = credentialId, + UserHandle = userHandle, + PrivateKey = privateKey, + PublicKey = publicKey, +}); +await context.Credentials.InstallAsync(); + +var page = await context.NewPageAsync(); +await page.GotoAsync("https://example.com/login"); +// The page's navigator.credentials.get() is answered with the seeded passkey. +``` + +You can also let the app register a passkey once in a setup test, read it back with [Credentials.GetAsync()](/api/class-credentials.mdx#credentials-get), and seed it into later tests — see [Credentials] for details. + +### 🗃️ Web Storage + +New [WebStorage] API, available via [Page.LocalStorage](/api/class-page.mdx#page-local-storage) and [Page.SessionStorage](/api/class-page.mdx#page-session-storage), reads and writes the page's storage for the current origin: + +```csharp +await page.LocalStorage.SetItemAsync("token", "abc"); +var token = await page.LocalStorage.GetItemAsync("token"); +var items = await page.SessionStorage.ItemsAsync(); +``` + +### New APIs +- [ApiResponse.SecurityDetailsAsync()](/api/class-apiresponse.mdx#api-response-security-details) and [ApiResponse.ServerAddrAsync()](/api/class-apiresponse.mdx#api-response-server-addr) mirror the browser-side [Response.SecurityDetailsAsync()](/api/class-response.mdx#response-security-details) and [Response.ServerAddrAsync()](/api/class-response.mdx#response-server-addr). +- New option `ArtifactsDir` in [BrowserType.ConnectOverCDPAsync()](/api/class-browsertype.mdx#browser-type-connect-over-cdp) controls where artifacts such as traces and downloads are stored when attached to an existing browser. +- New option `Cursor` in [Screencast.ShowActionsAsync()](/api/class-screencast.mdx#screencast-show-actions) controls the cursor decoration rendered for pointer actions. +- The `OnFrame` callback in [Screencast.StartAsync()](/api/class-screencast.mdx#screencast-start) now receives a `Timestamp` of when the frame was presented by the browser. + +### 🛠️ Other improvements +- Playwright now supports Ubuntu 26.04. +- HAR and trace recordings now include WebSocket requests. + +### Browser Versions +- Chromium 149.0.7827.55 +- Mozilla Firefox 151.0 +- WebKit 26.5 + +This version was also tested against the following stable channels: +- Google Chrome 149 +- Microsoft Edge 149 + ## Version 1.60 ### 🌐 HAR recording on Tracing diff --git a/java/docs/api/class-browsertype.mdx b/java/docs/api/class-browsertype.mdx index 806c3d9c5b..aa19f174d7 100644 --- a/java/docs/api/class-browsertype.mdx +++ b/java/docs/api/class-browsertype.mdx @@ -99,6 +99,10 @@ Connecting over the Chrome DevTools Protocol is only supported for Chromium-base This connection is significantly lower fidelity than the Playwright protocol connection via [BrowserType.connect()](/api/class-browsertype.mdx#browser-type-connect). If you are experiencing issues or attempting to use advanced functionality, you probably want to use [BrowserType.connect()](/api/class-browsertype.mdx#browser-type-connect). ::: +:::warning +Playwright maintains a curated list of arguments for launching the browser. If you launch the browser without Playwright and do not pass the exact same arguments, some of Playwright functionality may be broken upon connecting to the browser. +::: + **Usage** ```java diff --git a/java/docs/api/class-credentials.mdx b/java/docs/api/class-credentials.mdx index 7d6fd677a5..cf6fd16892 100644 --- a/java/docs/api/class-credentials.mdx +++ b/java/docs/api/class-credentials.mdx @@ -13,8 +13,56 @@ There are two common ways to use it: **Usage: seed a known credential** +```java +BrowserContext context = browser.newContext(); + +// A passkey your backend already provisioned for a test user. +context.credentials().create("example.com", new Credentials.CreateOptions() + .setId(knownCredentialId) // base64url + .setUserHandle(knownUserHandle) // base64url + .setPrivateKey(knownPrivateKey) // base64url PKCS#8 (DER) + .setPublicKey(knownPublicKey)); // base64url SPKI (DER) +context.credentials().install(); + +Page page = context.newPage(); +page.navigate("https://example.com/login"); +// The page's navigator.credentials.get() is answered with the seeded passkey. +``` + **Usage: capture a passkey, then reuse it** +```java +// setup test: let the app register a passkey, then save it. +BrowserContext context = browser.newContext(); +context.credentials().install(); + +Page page = context.newPage(); +page.navigate("https://example.com/register"); +page.getByRole(AriaRole.BUTTON, new Page.GetByRoleOptions().setName("Create a passkey")).click(); + +// Read back the passkey the page registered — it includes the private key. +VirtualCredential credential = context.credentials().get( + new Credentials.GetOptions().setRpId("example.com")).get(0); +Files.writeString(Paths.get("playwright/.auth/passkey.json"), new Gson().toJson(credential)); +``` + +```java +// later test: seed the captured passkey so the app starts already enrolled. +VirtualCredential credential = new Gson().fromJson( + Files.readString(Paths.get("playwright/.auth/passkey.json")), VirtualCredential.class); +BrowserContext context = browser.newContext(); +context.credentials().create(credential.rpId, new Credentials.CreateOptions() + .setId(credential.id) + .setUserHandle(credential.userHandle) + .setPrivateKey(credential.privateKey) + .setPublicKey(credential.publicKey)); +context.credentials().install(); + +Page page = context.newPage(); +page.navigate("https://example.com/login"); +// navigator.credentials.get() resolves the captured passkey — already signed in. +``` + **Defaults** @@ -28,20 +76,24 @@ There are two common ways to use it: Seeds a virtual WebAuthn credential and returns it. -With only `rpId`, generates a fresh **ECDSA P-256** keypair, credential id and user handle. The seeded credential is discoverable (resident), so the page can resolve it from both username-then-passkey and usernameless passkey flows. The returned object carries the `privateKey` and `publicKey`, so it can be persisted to disk and re-seeded in a later test. +With only [rpId](/api/class-credentials.mdx#credentials-create-option-rp-id), generates a fresh **ECDSA P-256** keypair, credential id and user handle. The seeded credential is discoverable (resident), so the page can resolve it from both username-then-passkey and usernameless passkey flows. The returned object carries the private and public keys, so it can be persisted to disk and re-seeded in a later test. -To **import a known credential**, supply all four of `id`, `userHandle`, `privateKey` and `publicKey` together. +To **import a known credential**, supply all four of [setId](/api/class-credentials.mdx#credentials-create-option-id), [setUserHandle](/api/class-credentials.mdx#credentials-create-option-user-handle), [setPrivateKey](/api/class-credentials.mdx#credentials-create-option-private-key) and [setPublicKey](/api/class-credentials.mdx#credentials-create-option-public-key) together. Call [Credentials.install()](/api/class-credentials.mdx#credentials-install) before navigating to a page that uses WebAuthn. **Usage** ```java -Credentials.create(options); +Credentials.create(rpId); +Credentials.create(rpId, options); ``` **Arguments** -- `options` `Credentials.CreateOptions` +- `rpId` [String]# + + Relying party id (typically the site's effective domain). +- `options` `Credentials.CreateOptions` *(optional)* - `setId` [String] *(optional)*# Base64url-encoded credential id. Auto-generated if omitted. @@ -51,9 +103,6 @@ Credentials.create(options); - `setPublicKey` [String] *(optional)*# Base64url-encoded SPKI (DER) public key. Auto-generated if omitted. - - `setRpId` [String]# - - Relying party id (typically the site's effective domain). - `setUserHandle` [String] *(optional)*# Base64url-encoded user handle. Auto-generated if omitted. @@ -104,9 +153,9 @@ Credentials.delete(id); Added in: v1.61credentials.get -Returns every credential currently held by the authenticator, optionally filtered by `rpId` or `id`. This includes both credentials seeded with [Credentials.create()](/api/class-credentials.mdx#credentials-create) and credentials the page registered itself by calling `navigator.credentials.create()`. +Returns every credential currently held by the authenticator, optionally filtered by [setRpId](/api/class-credentials.mdx#credentials-get-option-rp-id) or [setId](/api/class-credentials.mdx#credentials-get-option-id). This includes both credentials seeded with [Credentials.create()](/api/class-credentials.mdx#credentials-create) and credentials the page registered itself by calling `navigator.credentials.create()`. -Each returned credential includes its `privateKey` and `publicKey`, so a passkey the app just registered can be saved and re-seeded into a later test with [Credentials.create()](/api/class-credentials.mdx#credentials-create) — see the second example in the class overview. +Each returned credential includes its private and public keys, so a passkey the app just registered can be saved and re-seeded into a later test with [Credentials.create()](/api/class-credentials.mdx#credentials-create) — see the second example in the class overview. **Usage** @@ -149,7 +198,7 @@ Credentials.get(options); Installs the virtual WebAuthn authenticator into the context, overriding `navigator.credentials.create()` and `navigator.credentials.get()` in all current and future pages. Call this before the page first touches `navigator.credentials`. -Required: until `install()` is called, no interception is in place and the page sees the platform's native (or absent) WebAuthn behaviour. Seeding credentials with [Credentials.create()](/api/class-credentials.mdx#credentials-create) without `install()` populates the authenticator, but the page will never see those credentials. +Required: until [Credentials.install()](/api/class-credentials.mdx#credentials-install) is called, no interception is in place and the page sees the platform's native (or absent) WebAuthn behaviour. Seeding credentials with [Credentials.create()](/api/class-credentials.mdx#credentials-create) without installing populates the authenticator, but the page will never see those credentials. **Usage** diff --git a/java/docs/api/class-webstorage.mdx b/java/docs/api/class-webstorage.mdx index a59649b7f9..ce5f4e7757 100644 --- a/java/docs/api/class-webstorage.mdx +++ b/java/docs/api/class-webstorage.mdx @@ -15,7 +15,7 @@ Instances are accessed through [Page.localStorage()](/api/class-page.mdx#page-lo page.navigate("https://example.com"); page.localStorage().setItem("token", "abc"); String token = page.localStorage().getItem("token"); -List all = page.localStorage().items(); +List all = page.localStorage().items(); page.localStorage().removeItem("token"); page.localStorage().clear(); ``` @@ -46,7 +46,7 @@ WebStorage.clear(); Added in: v1.61webStorage.getItem -Returns the value for the given `name`, or `null` if the key is not present. +Returns the value for the given [name](/api/class-webstorage.mdx#web-storage-get-item-option-name) if present. **Usage** @@ -68,7 +68,7 @@ WebStorage.getItem(name); Added in: v1.61webStorage.items -Returns all items in the storage as `name`/`value` pairs. +Returns all items in the storage as name/value pairs. **Usage** @@ -90,7 +90,7 @@ WebStorage.items(); Added in: v1.61webStorage.removeItem -Removes the item with the given `name`. No-op if the item is absent. +Removes the item with the given [name](/api/class-webstorage.mdx#web-storage-remove-item-option-name). No-op if the item is absent. **Usage** @@ -112,7 +112,7 @@ WebStorage.removeItem(name); Added in: v1.61webStorage.setItem -Sets the value for the given `name`. Overwrites any existing value for that name. +Sets the value for the given [name](/api/class-webstorage.mdx#web-storage-set-item-option-name). Overwrites any existing value for that name. **Usage** diff --git a/java/docs/docker.mdx b/java/docs/docker.mdx index c401cc3b9e..75f2168a09 100644 --- a/java/docs/docker.mdx +++ b/java/docs/docker.mdx @@ -143,6 +143,7 @@ We currently publish images with the following tags: - `:v1.60.0` - Playwright v1.60.0 release docker image based on Ubuntu 24.04 LTS (Noble Numbat). - `:v1.60.0-noble` - Playwright v1.60.0 release docker image based on Ubuntu 24.04 LTS (Noble Numbat). - `:v1.60.0-jammy` - Playwright v1.60.0 release docker image based on Ubuntu 22.04 LTS (Jammy Jellyfish). +- `:v1.60.0-resolute` - Playwright v1.60.0 release docker image based on Ubuntu 26.04 LTS (Resolute Raccoon). :::note @@ -152,6 +153,7 @@ It is recommended to always pin your Docker image to a specific version if possi ### Base images We currently publish images based on the following [Ubuntu](https://hub.docker.com/_/ubuntu) versions: +- **Ubuntu 26.04 LTS** (Resolute Raccoon), image tags include `resolute` - **Ubuntu 24.04 LTS** (Noble Numbat), image tags include `noble` - **Ubuntu 22.04 LTS** (Jammy Jellyfish), image tags include `jammy` diff --git a/java/docs/getting-started-cli.mdx b/java/docs/getting-started-cli.mdx index 36ffe77e57..7813f6fc7a 100644 --- a/java/docs/getting-started-cli.mdx +++ b/java/docs/getting-started-cli.mdx @@ -32,6 +32,7 @@ playwright-cli --help Alternatively, install `@playwright/cli` as a local dependency and use `npx`: ```bash +npm install -D @playwright/cli@latest npx playwright-cli --help ``` diff --git a/java/docs/intro.mdx b/java/docs/intro.mdx index 09055f2e32..cf3766a4c8 100644 --- a/java/docs/intro.mdx +++ b/java/docs/intro.mdx @@ -129,8 +129,8 @@ By default browsers launched with Playwright run headless, meaning no browser UI ## System requirements - Java 8 or higher. - Windows 11+, Windows Server 2019+ or Windows Subsystem for Linux (WSL). -- macOS 14 Sonoma, or later. -- Debian 12, Debian 13, Ubuntu 22.04, Ubuntu 24.04, on x86-64 and arm64 architecture. +- macOS 14 (Sonoma) or later. +- Debian 12 / 13, Ubuntu 22.04 / 24.04 / 26.04 (x86-64 or arm64). ## What's next - [Write tests using web first assertions, page fixtures and locators](./writing-tests.mdx) diff --git a/java/docs/release-notes.mdx b/java/docs/release-notes.mdx index a5a5d40ca1..e5339534eb 100644 --- a/java/docs/release-notes.mdx +++ b/java/docs/release-notes.mdx @@ -9,6 +9,59 @@ import HTMLCard from '@site/src/components/HTMLCard'; import LiteYouTube from '@site/src/components/LiteYouTube'; +## Version 1.61 + +### 🔑 WebAuthn passkeys + +New [Credentials] virtual authenticator, available via [BrowserContext.credentials()](/api/class-browsercontext.mdx#browser-context-credentials), lets tests register passkeys and answer `navigator.credentials.create()` / `navigator.credentials.get()` ceremonies in the page — no real hardware key required, works in all browsers: + +```java +BrowserContext context = browser.newContext(); + +// Seed a passkey your backend provisioned for a test user. +context.credentials().create("example.com", new Credentials.CreateOptions() + .setId(credentialId) + .setUserHandle(userHandle) + .setPrivateKey(privateKey) + .setPublicKey(publicKey)); +context.credentials().install(); + +Page page = context.newPage(); +page.navigate("https://example.com/login"); +// The page's navigator.credentials.get() is answered with the seeded passkey. +``` + +You can also let the app register a passkey once in a setup test, read it back with [Credentials.get()](/api/class-credentials.mdx#credentials-get), and seed it into later tests — see [Credentials] for details. + +### 🗃️ Web Storage + +New [WebStorage] API, available via [Page.localStorage()](/api/class-page.mdx#page-local-storage) and [Page.sessionStorage()](/api/class-page.mdx#page-session-storage), reads and writes the page's storage for the current origin: + +```java +page.localStorage().setItem("token", "abc"); +String token = page.localStorage().getItem("token"); +List items = page.sessionStorage().items(); +``` + +### New APIs +- [APIResponse.securityDetails()](/api/class-apiresponse.mdx#api-response-security-details) and [APIResponse.serverAddr()](/api/class-apiresponse.mdx#api-response-server-addr) mirror the browser-side [Response.securityDetails()](/api/class-response.mdx#response-security-details) and [Response.serverAddr()](/api/class-response.mdx#response-server-addr). +- New option `artifactsDir` in [BrowserType.connectOverCDP()](/api/class-browsertype.mdx#browser-type-connect-over-cdp) controls where artifacts such as traces and downloads are stored when attached to an existing browser. +- New option `cursor` in [Screencast.showActions()](/api/class-screencast.mdx#screencast-show-actions) controls the cursor decoration rendered for pointer actions. +- The `onFrame` callback in [Screencast.start()](/api/class-screencast.mdx#screencast-start) now receives a `timestamp` of when the frame was presented by the browser. + +### 🛠️ Other improvements +- Playwright now supports Ubuntu 26.04. +- HAR and trace recordings now include WebSocket requests. + +### Browser Versions +- Chromium 149.0.7827.55 +- Mozilla Firefox 151.0 +- WebKit 26.5 + +This version was also tested against the following stable channels: +- Google Chrome 149 +- Microsoft Edge 149 + ## Version 1.60 ### 🌐 HAR recording on Tracing diff --git a/nodejs/docs/api/class-browsercontext.mdx b/nodejs/docs/api/class-browsercontext.mdx index 573a2de73c..0a742780cf 100644 --- a/nodejs/docs/api/class-browsercontext.mdx +++ b/nodejs/docs/api/class-browsercontext.mdx @@ -1052,6 +1052,9 @@ const page = await pagePromise; - `predicate` [function] *(optional)*# Receives the event data and resolves to truthy value when the waiting should resolve. + - `signal` [AbortSignal] *(optional)* Added in: v1.61# + + Allows to cancel the waiting using an [`AbortSignal`](https://developer.mozilla.org/en-US/docs/Web/API/AbortSignal). If the signal is aborted, the waiting will be aborted and the operation will throw an error. Note that providing a signal does not disable the default timeout; pass `timeout: 0` to disable the timeout entirely. **Returns** - [Promise]<[Object]># diff --git a/nodejs/docs/api/class-browsertype.mdx b/nodejs/docs/api/class-browsertype.mdx index 226b0d25c8..14fac9f5b5 100644 --- a/nodejs/docs/api/class-browsertype.mdx +++ b/nodejs/docs/api/class-browsertype.mdx @@ -94,6 +94,10 @@ Connecting over the Chrome DevTools Protocol is only supported for Chromium-base This connection is significantly lower fidelity than the Playwright protocol connection via [browserType.connect()](/api/class-browsertype.mdx#browser-type-connect). If you are experiencing issues or attempting to use advanced functionality, you probably want to use [browserType.connect()](/api/class-browsertype.mdx#browser-type-connect). ::: +:::warning +Playwright maintains a curated list of arguments for launching the browser. If you launch the browser without Playwright and do not pass the exact same arguments, some of Playwright functionality may be broken upon connecting to the browser. +::: + **Usage** ```js diff --git a/nodejs/docs/api/class-credentials.mdx b/nodejs/docs/api/class-credentials.mdx index 2f7c88bc46..199a9c0123 100644 --- a/nodejs/docs/api/class-credentials.mdx +++ b/nodejs/docs/api/class-credentials.mdx @@ -17,8 +17,7 @@ There are two common ways to use it: const context = await browser.newContext(); // A passkey your backend already provisioned for a test user. -await context.credentials.create({ - rpId: 'example.com', +await context.credentials.create('example.com', { id: knownCredentialId, // base64url userHandle: knownUserHandle, // base64url privateKey: knownPrivateKey, // base64url PKCS#8 (DER) @@ -51,7 +50,7 @@ fs.writeFileSync('playwright/.auth/passkey.json', JSON.stringify(credential)); // later test: seed the captured passkey so the app starts already enrolled. const credential = JSON.parse(fs.readFileSync('playwright/.auth/passkey.json', 'utf8')); const context = await browser.newContext(); -await context.credentials.create(credential); +await context.credentials.create(credential.rpId, credential); await context.credentials.install(); const page = await context.newPage(); @@ -72,20 +71,24 @@ await page.goto('https://example.com/login'); Seeds a virtual WebAuthn credential and returns it. -With only `rpId`, generates a fresh **ECDSA P-256** keypair, credential id and user handle. The seeded credential is discoverable (resident), so the page can resolve it from both username-then-passkey and usernameless passkey flows. The returned object carries the `privateKey` and `publicKey`, so it can be persisted to disk and re-seeded in a later test. +With only [rpId](/api/class-credentials.mdx#credentials-create-option-rp-id), generates a fresh **ECDSA P-256** keypair, credential id and user handle. The seeded credential is discoverable (resident), so the page can resolve it from both username-then-passkey and usernameless passkey flows. The returned object carries the private and public keys, so it can be persisted to disk and re-seeded in a later test. -To **import a known credential**, supply all four of `id`, `userHandle`, `privateKey` and `publicKey` together. +To **import a known credential**, supply all four of [id](/api/class-credentials.mdx#credentials-create-option-id), [userHandle](/api/class-credentials.mdx#credentials-create-option-user-handle), [privateKey](/api/class-credentials.mdx#credentials-create-option-private-key) and [publicKey](/api/class-credentials.mdx#credentials-create-option-public-key) together. Call [credentials.install()](/api/class-credentials.mdx#credentials-install) before navigating to a page that uses WebAuthn. **Usage** ```js -await credentials.create(options); +await credentials.create(rpId); +await credentials.create(rpId, options); ``` **Arguments** -- `options` [Object] +- `rpId` [string]# + + Relying party id (typically the site's effective domain). +- `options` [Object] *(optional)* - `id` [string] *(optional)*# Base64url-encoded credential id. Auto-generated if omitted. @@ -95,9 +98,6 @@ await credentials.create(options); - `publicKey` [string] *(optional)*# Base64url-encoded SPKI (DER) public key. Auto-generated if omitted. - - `rpId` [string]# - - Relying party id (typically the site's effective domain). - `userHandle` [string] *(optional)*# Base64url-encoded user handle. Auto-generated if omitted. @@ -148,9 +148,9 @@ await credentials.delete(id); Added in: v1.61credentials.get -Returns every credential currently held by the authenticator, optionally filtered by `rpId` or `id`. This includes both credentials seeded with [credentials.create()](/api/class-credentials.mdx#credentials-create) and credentials the page registered itself by calling `navigator.credentials.create()`. +Returns every credential currently held by the authenticator, optionally filtered by [rpId](/api/class-credentials.mdx#credentials-get-option-rp-id) or [id](/api/class-credentials.mdx#credentials-get-option-id). This includes both credentials seeded with [credentials.create()](/api/class-credentials.mdx#credentials-create) and credentials the page registered itself by calling `navigator.credentials.create()`. -Each returned credential includes its `privateKey` and `publicKey`, so a passkey the app just registered can be saved and re-seeded into a later test with [credentials.create()](/api/class-credentials.mdx#credentials-create) — see the second example in the class overview. +Each returned credential includes its private and public keys, so a passkey the app just registered can be saved and re-seeded into a later test with [credentials.create()](/api/class-credentials.mdx#credentials-create) — see the second example in the class overview. **Usage** @@ -193,7 +193,7 @@ await credentials.get(options); Installs the virtual WebAuthn authenticator into the context, overriding `navigator.credentials.create()` and `navigator.credentials.get()` in all current and future pages. Call this before the page first touches `navigator.credentials`. -Required: until `install()` is called, no interception is in place and the page sees the platform's native (or absent) WebAuthn behaviour. Seeding credentials with [credentials.create()](/api/class-credentials.mdx#credentials-create) without `install()` populates the authenticator, but the page will never see those credentials. +Required: until [credentials.install()](/api/class-credentials.mdx#credentials-install) is called, no interception is in place and the page sees the platform's native (or absent) WebAuthn behaviour. Seeding credentials with [credentials.create()](/api/class-credentials.mdx#credentials-create) without installing populates the authenticator, but the page will never see those credentials. **Usage** diff --git a/nodejs/docs/api/class-frame.mdx b/nodejs/docs/api/class-frame.mdx index 1d8093048c..05ad899007 100644 --- a/nodejs/docs/api/class-frame.mdx +++ b/nodejs/docs/api/class-frame.mdx @@ -1033,6 +1033,9 @@ await frame.waitForLoadState(); // Waits for 'load' state by default. * `'domcontentloaded'` - wait for the `DOMContentLoaded` event to be fired. * `'networkidle'` - **DISCOURAGED** wait until there are no network connections for at least `500` ms. Don't use this method for testing, rely on web assertions to assess readiness instead. - `options` [Object] *(optional)* + - `signal` [AbortSignal] *(optional)* Added in: v1.61# + + Allows to cancel the waiting using an [`AbortSignal`](https://developer.mozilla.org/en-US/docs/Web/API/AbortSignal). If the signal is aborted, the waiting will be aborted and the operation will throw an error. Note that providing a signal does not disable the default timeout; pass `timeout: 0` to disable the timeout entirely. - `timeout` [number] *(optional)*# Maximum operation time in milliseconds. Defaults to `0` - no timeout. The default value can be changed via `navigationTimeout` option in the config, or by using the [browserContext.setDefaultNavigationTimeout()](/api/class-browsercontext.mdx#browser-context-set-default-navigation-timeout), [browserContext.setDefaultTimeout()](/api/class-browsercontext.mdx#browser-context-set-default-timeout), [page.setDefaultNavigationTimeout()](/api/class-page.mdx#page-set-default-navigation-timeout) or [page.setDefaultTimeout()](/api/class-page.mdx#page-set-default-timeout) methods. @@ -1060,6 +1063,9 @@ await frame.waitForURL('**/target.html'); A glob pattern, regex pattern, URL pattern, or predicate receiving [URL] to match while waiting for the navigation. Note that if the parameter is a string without wildcard characters, the method will wait for navigation to URL that is exactly equal to the string. - `options` [Object] *(optional)* + - `signal` [AbortSignal] *(optional)* Added in: v1.61# + + Allows to cancel the waiting using an [`AbortSignal`](https://developer.mozilla.org/en-US/docs/Web/API/AbortSignal). If the signal is aborted, the waiting will be aborted and the operation will throw an error. Note that providing a signal does not disable the default timeout; pass `timeout: 0` to disable the timeout entirely. - `timeout` [number] *(optional)*# Maximum operation time in milliseconds. Defaults to `0` - no timeout. The default value can be changed via `navigationTimeout` option in the config, or by using the [browserContext.setDefaultNavigationTimeout()](/api/class-browsercontext.mdx#browser-context-set-default-navigation-timeout), [browserContext.setDefaultTimeout()](/api/class-browsercontext.mdx#browser-context-set-default-timeout), [page.setDefaultNavigationTimeout()](/api/class-page.mdx#page-set-default-navigation-timeout) or [page.setDefaultTimeout()](/api/class-page.mdx#page-set-default-timeout) methods. @@ -2547,6 +2553,9 @@ Usage of the [History API](https://developer.mozilla.org/en-US/docs/Web/API/Hist **Arguments** - `options` [Object] *(optional)* + - `signal` [AbortSignal] *(optional)* Added in: v1.61# + + Allows to cancel the waiting using an [`AbortSignal`](https://developer.mozilla.org/en-US/docs/Web/API/AbortSignal). If the signal is aborted, the waiting will be aborted and the operation will throw an error. Note that providing a signal does not disable the default timeout; pass `timeout: 0` to disable the timeout entirely. - `timeout` [number] *(optional)*# Maximum operation time in milliseconds. Defaults to `0` - no timeout. The default value can be changed via `navigationTimeout` option in the config, or by using the [browserContext.setDefaultNavigationTimeout()](/api/class-browsercontext.mdx#browser-context-set-default-navigation-timeout), [browserContext.setDefaultTimeout()](/api/class-browsercontext.mdx#browser-context-set-default-timeout), [page.setDefaultNavigationTimeout()](/api/class-page.mdx#page-set-default-navigation-timeout) or [page.setDefaultTimeout()](/api/class-page.mdx#page-set-default-timeout) methods. diff --git a/nodejs/docs/api/class-page.mdx b/nodejs/docs/api/class-page.mdx index 9773ca33d2..dcc52ffbcf 100644 --- a/nodejs/docs/api/class-page.mdx +++ b/nodejs/docs/api/class-page.mdx @@ -2289,6 +2289,9 @@ const download = await downloadPromise; - `predicate` [function] *(optional)*# Receives the event data and resolves to truthy value when the waiting should resolve. + - `signal` [AbortSignal] *(optional)* Added in: v1.61# + + Allows to cancel the waiting using an [`AbortSignal`](https://developer.mozilla.org/en-US/docs/Web/API/AbortSignal). If the signal is aborted, the waiting will be aborted and the operation will throw an error. Note that providing a signal does not disable the default timeout; pass `timeout: 0` to disable the timeout entirely. **Returns** - [Promise]<[Object]># @@ -2381,6 +2384,9 @@ console.log(await popup.title()); // Popup is ready to use. * `'domcontentloaded'` - wait for the `DOMContentLoaded` event to be fired. * `'networkidle'` - **DISCOURAGED** wait until there are no network connections for at least `500` ms. Don't use this method for testing, rely on web assertions to assess readiness instead. - `options` [Object] *(optional)* + - `signal` [AbortSignal] *(optional)* Added in: v1.61# + + Allows to cancel the waiting using an [`AbortSignal`](https://developer.mozilla.org/en-US/docs/Web/API/AbortSignal). If the signal is aborted, the waiting will be aborted and the operation will throw an error. Note that providing a signal does not disable the default timeout; pass `timeout: 0` to disable the timeout entirely. - `timeout` [number] *(optional)*# Maximum operation time in milliseconds. Defaults to `0` - no timeout. The default value can be changed via `navigationTimeout` option in the config, or by using the [browserContext.setDefaultNavigationTimeout()](/api/class-browsercontext.mdx#browser-context-set-default-navigation-timeout), [browserContext.setDefaultTimeout()](/api/class-browsercontext.mdx#browser-context-set-default-timeout), [page.setDefaultNavigationTimeout()](/api/class-page.mdx#page-set-default-navigation-timeout) or [page.setDefaultTimeout()](/api/class-page.mdx#page-set-default-timeout) methods. @@ -2417,6 +2423,9 @@ const request = await requestPromise; Request URL string, regex or predicate receiving [Request] object. - `options` [Object] *(optional)* + - `signal` [AbortSignal] *(optional)* Added in: v1.61# + + Allows to cancel the waiting using an [`AbortSignal`](https://developer.mozilla.org/en-US/docs/Web/API/AbortSignal). If the signal is aborted, the waiting will be aborted and the operation will throw an error. Note that providing a signal does not disable the default timeout; pass `timeout: 0` to disable the timeout entirely. - `timeout` [number] *(optional)*# Maximum wait time in milliseconds, defaults to 30 seconds, pass `0` to disable the timeout. The default value can be changed by using the [page.setDefaultTimeout()](/api/class-page.mdx#page-set-default-timeout) method. @@ -2454,6 +2463,9 @@ const response = await responsePromise; Request URL string, regex or predicate receiving [Response] object. When a [baseURL](/api/class-browser.mdx#browser-new-context-option-base-url) via the context options was provided and the passed URL is a path, it gets merged via the [`new URL()`](https://developer.mozilla.org/en-US/docs/Web/API/URL/URL) constructor. - `options` [Object] *(optional)* + - `signal` [AbortSignal] *(optional)* Added in: v1.61# + + Allows to cancel the waiting using an [`AbortSignal`](https://developer.mozilla.org/en-US/docs/Web/API/AbortSignal). If the signal is aborted, the waiting will be aborted and the operation will throw an error. Note that providing a signal does not disable the default timeout; pass `timeout: 0` to disable the timeout entirely. - `timeout` [number] *(optional)*# Maximum wait time in milliseconds, defaults to 30 seconds, pass `0` to disable the timeout. The default value can be changed by using the [browserContext.setDefaultTimeout()](/api/class-browsercontext.mdx#browser-context-set-default-timeout) or [page.setDefaultTimeout()](/api/class-page.mdx#page-set-default-timeout) methods. @@ -2481,6 +2493,9 @@ await page.waitForURL('**/target.html'); A glob pattern, regex pattern, URL pattern, or predicate receiving [URL] to match while waiting for the navigation. Note that if the parameter is a string without wildcard characters, the method will wait for navigation to URL that is exactly equal to the string. - `options` [Object] *(optional)* + - `signal` [AbortSignal] *(optional)* Added in: v1.61# + + Allows to cancel the waiting using an [`AbortSignal`](https://developer.mozilla.org/en-US/docs/Web/API/AbortSignal). If the signal is aborted, the waiting will be aborted and the operation will throw an error. Note that providing a signal does not disable the default timeout; pass `timeout: 0` to disable the timeout entirely. - `timeout` [number] *(optional)*# Maximum operation time in milliseconds. Defaults to `0` - no timeout. The default value can be changed via `navigationTimeout` option in the config, or by using the [browserContext.setDefaultNavigationTimeout()](/api/class-browsercontext.mdx#browser-context-set-default-navigation-timeout), [browserContext.setDefaultTimeout()](/api/class-browsercontext.mdx#browser-context-set-default-timeout), [page.setDefaultNavigationTimeout()](/api/class-page.mdx#page-set-default-navigation-timeout) or [page.setDefaultTimeout()](/api/class-page.mdx#page-set-default-timeout) methods. @@ -4580,6 +4595,9 @@ Usage of the [History API](https://developer.mozilla.org/en-US/docs/Web/API/Hist **Arguments** - `options` [Object] *(optional)* + - `signal` [AbortSignal] *(optional)* Added in: v1.61# + + Allows to cancel the waiting using an [`AbortSignal`](https://developer.mozilla.org/en-US/docs/Web/API/AbortSignal). If the signal is aborted, the waiting will be aborted and the operation will throw an error. Note that providing a signal does not disable the default timeout; pass `timeout: 0` to disable the timeout entirely. - `timeout` [number] *(optional)*# Maximum operation time in milliseconds. Defaults to `0` - no timeout. The default value can be changed via `navigationTimeout` option in the config, or by using the [browserContext.setDefaultNavigationTimeout()](/api/class-browsercontext.mdx#browser-context-set-default-navigation-timeout), [browserContext.setDefaultTimeout()](/api/class-browsercontext.mdx#browser-context-set-default-timeout), [page.setDefaultNavigationTimeout()](/api/class-page.mdx#page-set-default-navigation-timeout) or [page.setDefaultTimeout()](/api/class-page.mdx#page-set-default-timeout) methods. diff --git a/nodejs/docs/api/class-websocket.mdx b/nodejs/docs/api/class-websocket.mdx index 2095977d15..eeabbcc0bf 100644 --- a/nodejs/docs/api/class-websocket.mdx +++ b/nodejs/docs/api/class-websocket.mdx @@ -80,6 +80,9 @@ await webSocket.waitForEvent(event, optionsOrPredicate, options); - `predicate` [function] *(optional)*# Receives the event data and resolves to truthy value when the waiting should resolve. + - `signal` [AbortSignal] *(optional)* Added in: v1.61# + + Allows to cancel the waiting using an [`AbortSignal`](https://developer.mozilla.org/en-US/docs/Web/API/AbortSignal). If the signal is aborted, the waiting will be aborted and the operation will throw an error. Note that providing a signal does not disable the default timeout; pass `timeout: 0` to disable the timeout entirely. **Returns** - [Promise]<[Object]># diff --git a/nodejs/docs/api/class-webstorage.mdx b/nodejs/docs/api/class-webstorage.mdx index e830ff499c..16076e42ba 100644 --- a/nodejs/docs/api/class-webstorage.mdx +++ b/nodejs/docs/api/class-webstorage.mdx @@ -46,7 +46,7 @@ await webStorage.clear(); Added in: v1.61webStorage.getItem -Returns the value for the given `name`, or `null` if the key is not present. +Returns the value for the given [name](/api/class-webstorage.mdx#web-storage-get-item-option-name) if present. **Usage** @@ -68,7 +68,7 @@ await webStorage.getItem(name); Added in: v1.61webStorage.items -Returns all items in the storage as `name`/`value` pairs. +Returns all items in the storage as name/value pairs. **Usage** @@ -90,7 +90,7 @@ await webStorage.items(); Added in: v1.61webStorage.removeItem -Removes the item with the given `name`. No-op if the item is absent. +Removes the item with the given [name](/api/class-webstorage.mdx#web-storage-remove-item-option-name). No-op if the item is absent. **Usage** @@ -112,7 +112,7 @@ await webStorage.removeItem(name); Added in: v1.61webStorage.setItem -Sets the value for the given `name`. Overwrites any existing value for that name. +Sets the value for the given [name](/api/class-webstorage.mdx#web-storage-set-item-option-name). Overwrites any existing value for that name. **Usage** diff --git a/nodejs/docs/api/class-worker.mdx b/nodejs/docs/api/class-worker.mdx index 6c23c0b7c6..f8da961aa1 100644 --- a/nodejs/docs/api/class-worker.mdx +++ b/nodejs/docs/api/class-worker.mdx @@ -132,6 +132,9 @@ const consoleMessage = await consolePromise; - `predicate` [function] *(optional)*# Receives the event data and resolves to truthy value when the waiting should resolve. + - `signal` [AbortSignal] *(optional)* Added in: v1.61# + + Allows to cancel the waiting using an [`AbortSignal`](https://developer.mozilla.org/en-US/docs/Web/API/AbortSignal). If the signal is aborted, the waiting will be aborted and the operation will throw an error. Note that providing a signal does not disable the default timeout; pass `timeout: 0` to disable the timeout entirely. **Returns** - [Promise]<[Object]># diff --git a/nodejs/docs/auth.mdx b/nodejs/docs/auth.mdx index d3389e0579..9341c481f2 100644 --- a/nodejs/docs/auth.mdx +++ b/nodejs/docs/auth.mdx @@ -325,8 +325,7 @@ export * from '@playwright/test'; export const test = baseTest.extend({ context: async ({ context }, use) => { // A passkey your backend provisioned for the test user. - await context.credentials.create({ - rpId: 'example.com', + await context.credentials.create('example.com', { id: process.env.PASSKEY_ID, userHandle: process.env.PASSKEY_USER_HANDLE, privateKey: process.env.PASSKEY_PRIVATE_KEY, @@ -366,7 +365,7 @@ export * from '@playwright/test'; export const test = baseTest.extend({ context: async ({ context }, use) => { const credential = JSON.parse(fs.readFileSync('playwright/.auth/passkey.json', 'utf8')); - await context.credentials.create(credential); + await context.credentials.create(credential.rpId, credential); await context.credentials.install(); await use(context); }, diff --git a/nodejs/docs/ci.mdx b/nodejs/docs/ci.mdx index 3ef2360bc1..6fe0ef9def 100644 --- a/nodejs/docs/ci.mdx +++ b/nodejs/docs/ci.mdx @@ -101,7 +101,7 @@ jobs: name: 'Playwright Tests' runs-on: ubuntu-latest container: - image: mcr.microsoft.com/playwright:v1.60.0-noble + image: mcr.microsoft.com/playwright:v1.61.0-noble options: --user 1001 steps: - uses: actions/checkout@v5 @@ -335,7 +335,7 @@ trigger: pool: vmImage: ubuntu-latest -container: mcr.microsoft.com/playwright:v1.60.0-noble +container: mcr.microsoft.com/playwright:v1.61.0-noble steps: - task: UseNode@1 @@ -359,7 +359,7 @@ Running Playwright on CircleCI is very similar to running on GitHub Actions. In executors: pw-noble-development: docker: - - image: mcr.microsoft.com/playwright:v1.60.0-noble + - image: mcr.microsoft.com/playwright:v1.61.0-noble ``` Note: When using the docker agent definition, you are specifying the resource class of where playwright runs to the 'medium' tier [here](https://circleci.com/docs/configuration-reference?#docker-execution-environment). The default behavior of Playwright is to set the number of workers to the detected core count (2 in the case of the medium tier). Overriding the number of workers to greater than this number will cause unnecessary timeouts and failures. @@ -382,7 +382,7 @@ Jenkins supports Docker agents for pipelines. Use the [Playwright Docker image]( ```groovy pipeline { - agent { docker { image 'mcr.microsoft.com/playwright:v1.60.0-noble' } } + agent { docker { image 'mcr.microsoft.com/playwright:v1.61.0-noble' } } stages { stage('e2e-tests') { steps { @@ -399,7 +399,7 @@ pipeline { Bitbucket Pipelines can use public [Docker images as build environments](https://confluence.atlassian.com/bitbucket/use-docker-images-as-build-environments-792298897.html). To run Playwright tests on Bitbucket, use our public Docker image ([see Dockerfile](./docker.mdx)). ```yml -image: mcr.microsoft.com/playwright:v1.60.0-noble +image: mcr.microsoft.com/playwright:v1.61.0-noble ``` ### GitLab CI @@ -412,7 +412,7 @@ stages: tests: stage: test - image: mcr.microsoft.com/playwright:v1.60.0-noble + image: mcr.microsoft.com/playwright:v1.61.0-noble script: ... ``` @@ -427,7 +427,7 @@ stages: tests: stage: test - image: mcr.microsoft.com/playwright:v1.60.0-noble + image: mcr.microsoft.com/playwright:v1.61.0-noble parallel: 7 script: - npm ci @@ -442,7 +442,7 @@ stages: tests: stage: test - image: mcr.microsoft.com/playwright:v1.60.0-noble + image: mcr.microsoft.com/playwright:v1.61.0-noble parallel: matrix: - PROJECT: ['chromium', 'webkit'] @@ -458,7 +458,7 @@ To run Playwright tests on Google Cloud Build, use our public Docker image ([see ```yml steps: -- name: mcr.microsoft.com/playwright:v1.60.0-noble +- name: mcr.microsoft.com/playwright:v1.61.0-noble script: ... env: @@ -476,7 +476,7 @@ type: docker steps: - name: test - image: mcr.microsoft.com/playwright:v1.60.0-noble + image: mcr.microsoft.com/playwright:v1.61.0-noble commands: - npx playwright test ``` diff --git a/nodejs/docs/docker.mdx b/nodejs/docs/docker.mdx index eeec19ffc5..5da6482d6e 100644 --- a/nodejs/docs/docker.mdx +++ b/nodejs/docs/docker.mdx @@ -22,7 +22,7 @@ This Docker image is intended to be used for testing and development purposes on ### Pull the image ```bash -docker pull mcr.microsoft.com/playwright:v1.60.0-noble +docker pull mcr.microsoft.com/playwright:v1.61.0-noble ``` ### Run the image @@ -34,7 +34,7 @@ By default, the Docker image will use the `root` user to run the browsers. This On trusted websites, you can avoid creating a separate user and use root for it since you trust the code which will run on the browsers. ```bash -docker run -it --rm --ipc=host mcr.microsoft.com/playwright:v1.60.0-noble /bin/bash +docker run -it --rm --ipc=host mcr.microsoft.com/playwright:v1.61.0-noble /bin/bash ``` #### Crawling and scraping @@ -42,7 +42,7 @@ docker run -it --rm --ipc=host mcr.microsoft.com/playwright:v1.60.0-noble /bin/b On untrusted websites, it's recommended to use a separate user for launching the browsers in combination with the seccomp profile. Inside the container or if you are using the Docker image as a base image you have to use `adduser` for it. ```bash -docker run -it --rm --ipc=host --user pwuser --security-opt seccomp=seccomp_profile.json mcr.microsoft.com/playwright:v1.60.0-noble /bin/bash +docker run -it --rm --ipc=host --user pwuser --security-opt seccomp=seccomp_profile.json mcr.microsoft.com/playwright:v1.61.0-noble /bin/bash ``` [`seccomp_profile.json`](https://github.com/microsoft/playwright/blob/main/utils/docker/seccomp_profile.json) is needed to run Chromium with sandbox. This is a [default Docker seccomp profile](https://github.com/docker/engine/blob/d0d99b04cf6e00ed3fc27e81fc3d94e7eda70af3/profiles/seccomp/default.json) with extra user namespace cloning permissions: @@ -82,7 +82,7 @@ You can run Playwright Server in Docker while keeping your tests running on the Start the Playwright Server in Docker: ```bash -docker run -p 3000:3000 --rm --init -it --workdir /home/pwuser --user pwuser mcr.microsoft.com/playwright:v1.60.0-noble /bin/sh -c "npx -y playwright@1.60.0 run-server --port 3000 --host 0.0.0.0" +docker run -p 3000:3000 --rm --init -it --workdir /home/pwuser --user pwuser mcr.microsoft.com/playwright:v1.61.0-noble /bin/sh -c "npx -y playwright@1.61.0 run-server --port 3000 --host 0.0.0.0" ``` #### Connecting to the Server @@ -105,7 +105,7 @@ const browser = await playwright['chromium'].connect('ws://127.0.0.1:3000/'); If you need to access local servers from within the Docker container: ```bash -docker run --add-host=hostmachine:host-gateway -p 3000:3000 --rm --init -it --workdir /home/pwuser --user pwuser mcr.microsoft.com/playwright:v1.60.0-noble /bin/sh -c "npx -y playwright@1.60.0 run-server --port 3000 --host 0.0.0.0" +docker run --add-host=hostmachine:host-gateway -p 3000:3000 --rm --init -it --workdir /home/pwuser --user pwuser mcr.microsoft.com/playwright:v1.61.0-noble /bin/sh -c "npx -y playwright@1.61.0 run-server --port 3000 --host 0.0.0.0" ``` This makes `hostmachine` point to the host's localhost. Your tests should use `hostmachine` instead of `localhost` when accessing local servers. @@ -138,9 +138,10 @@ Once this is enabled you can open the port specified in a new browser tab and yo See [all available image tags]. We currently publish images with the following tags: -- `:v1.60.0` - Playwright v1.60.0 release docker image based on Ubuntu 24.04 LTS (Noble Numbat). -- `:v1.60.0-noble` - Playwright v1.60.0 release docker image based on Ubuntu 24.04 LTS (Noble Numbat). -- `:v1.60.0-jammy` - Playwright v1.60.0 release docker image based on Ubuntu 22.04 LTS (Jammy Jellyfish). +- `:v1.61.0` - Playwright v1.61.0 release docker image based on Ubuntu 24.04 LTS (Noble Numbat). +- `:v1.61.0-noble` - Playwright v1.61.0 release docker image based on Ubuntu 24.04 LTS (Noble Numbat). +- `:v1.61.0-jammy` - Playwright v1.61.0 release docker image based on Ubuntu 22.04 LTS (Jammy Jellyfish). +- `:v1.61.0-resolute` - Playwright v1.61.0 release docker image based on Ubuntu 26.04 LTS (Resolute Raccoon). :::note @@ -150,6 +151,7 @@ It is recommended to always pin your Docker image to a specific version if possi ### Base images We currently publish images based on the following [Ubuntu](https://hub.docker.com/_/ubuntu) versions: +- **Ubuntu 26.04 LTS** (Resolute Raccoon), image tags include `resolute` - **Ubuntu 24.04 LTS** (Noble Numbat), image tags include `noble` - **Ubuntu 22.04 LTS** (Jammy Jellyfish), image tags include `jammy` @@ -164,7 +166,7 @@ To run Playwright inside Docker, you need to have Node.js, [Playwright browsers] ```Dockerfile FROM node:20-bookworm -RUN npx -y playwright@1.60.0 install --with-deps +RUN npx -y playwright@1.61.0 install --with-deps ``` diff --git a/nodejs/docs/getting-started-cli.mdx b/nodejs/docs/getting-started-cli.mdx index fb8c675dc3..96652adad5 100644 --- a/nodejs/docs/getting-started-cli.mdx +++ b/nodejs/docs/getting-started-cli.mdx @@ -32,6 +32,7 @@ playwright-cli --help Alternatively, install `@playwright/cli` as a local dependency and use `npx`: ```bash +npm install -D @playwright/cli@latest npx playwright-cli --help ``` diff --git a/nodejs/docs/intro.mdx b/nodejs/docs/intro.mdx index 999c7772ca..e965f6c455 100644 --- a/nodejs/docs/intro.mdx +++ b/nodejs/docs/intro.mdx @@ -258,10 +258,10 @@ pnpm exec playwright --version ## System requirements -- Node.js: latest 20.x, 22.x or 24.x. +- Node.js: latest 22.x, 24.x or 26.x. - Windows 11+, Windows Server 2019+ or Windows Subsystem for Linux (WSL). - macOS 14 (Sonoma) or later. -- Debian 12 / 13, Ubuntu 22.04 / 24.04 (x86-64 or arm64). +- Debian 12 / 13, Ubuntu 22.04 / 24.04 / 26.04 (x86-64 or arm64). ## What's next - [Write tests using web-first assertions, fixtures and locators](./writing-tests.mdx) diff --git a/nodejs/docs/release-notes.mdx b/nodejs/docs/release-notes.mdx index c0530ca55a..a4136cc1ac 100644 --- a/nodejs/docs/release-notes.mdx +++ b/nodejs/docs/release-notes.mdx @@ -9,6 +9,72 @@ import HTMLCard from '@site/src/components/HTMLCard'; import LiteYouTube from '@site/src/components/LiteYouTube'; +## Version 1.61 + +### 🔑 WebAuthn passkeys + +New [Credentials] virtual authenticator, available via [browserContext.credentials](/api/class-browsercontext.mdx#browser-context-credentials), lets tests register passkeys and answer `navigator.credentials.create()` / `navigator.credentials.get()` ceremonies in the page — no real hardware key required, works in all browsers: + +```js +const context = await browser.newContext(); + +// Seed a passkey your backend provisioned for a test user. +await context.credentials.create('example.com', { + id: credentialId, + userHandle, + privateKey, + publicKey, +}); +await context.credentials.install(); + +const page = await context.newPage(); +await page.goto('https://example.com/login'); +// The page's navigator.credentials.get() is answered with the seeded passkey. +``` + +You can also let the app register a passkey once in a setup test, read it back with [credentials.get()](/api/class-credentials.mdx#credentials-get), and seed it into later tests — see [Credentials] for details. + +### 🗃️ Web Storage + +New [WebStorage] API, available via [page.localStorage](/api/class-page.mdx#page-local-storage) and [page.sessionStorage](/api/class-page.mdx#page-session-storage), reads and writes the page's storage for the current origin: + +```js +await page.localStorage.setItem('token', 'abc'); +const token = await page.localStorage.getItem('token'); +const items = await page.sessionStorage.items(); +``` + +### New APIs + +#### Network +- [apiResponse.securityDetails()](/api/class-apiresponse.mdx#api-response-security-details) and [apiResponse.serverAddr()](/api/class-apiresponse.mdx#api-response-server-addr) mirror the browser-side [response.securityDetails()](/api/class-response.mdx#response-security-details) and [response.serverAddr()](/api/class-response.mdx#response-server-addr). + +#### Browser and Screencast +- New option `artifactsDir` in [browserType.connectOverCDP()](/api/class-browsertype.mdx#browser-type-connect-over-cdp) controls where artifacts such as traces and downloads are stored when attached to an existing browser. +- New option `cursor` in [screencast.showActions()](/api/class-screencast.mdx#screencast-show-actions) controls the cursor decoration rendered for pointer actions. +- The `onFrame` callback in [screencast.start()](/api/class-screencast.mdx#screencast-start) now receives a `timestamp` of when the frame was presented by the browser. + +#### Test runner +- The [testOptions.video](/api/class-testoptions.mdx#test-options-video) option now supports the same set of modes as `trace`: new `'on-all-retries'`, `'retain-on-first-failure'` and `'retain-on-failure-and-retries'` values. See the [video modes table](./test-use-options.mdx#video-modes) for which runs are recorded and kept in each mode. +- Supported `expect.soft.poll(...)`. +- New [fullConfig.argv](/api/class-fullconfig.mdx#full-config-argv) — a snapshot of `process.argv` from the runner process, handy for reading custom arguments passed after the `--` separator. +- New [fullConfig.failOnFlakyTests](/api/class-fullconfig.mdx#full-config-fail-on-flaky-tests) mirrors the config option, so reporters can explain why a flaky run failed. +- [testInfo.errors](/api/class-testinfo.mdx#test-info-errors) now lists each sub-error of an `AggregateError` as a separate entry. +- New `-G` command line shorthand for `--grep-invert`. + +### 🛠️ Other improvements +- Playwright now supports Ubuntu 26.04. +- HAR and trace recordings now include WebSocket requests. + +### Browser Versions +- Chromium 149.0.7827.55 +- Mozilla Firefox 151.0 +- WebKit 26.5 + +This version was also tested against the following stable channels: +- Google Chrome 149 +- Microsoft Edge 149 + ## Version 1.60 ### 🌐 HAR recording on Tracing diff --git a/python/docs/api/class-browsertype.mdx b/python/docs/api/class-browsertype.mdx index c2913858ad..1c61a7f68e 100644 --- a/python/docs/api/class-browsertype.mdx +++ b/python/docs/api/class-browsertype.mdx @@ -130,6 +130,10 @@ Connecting over the Chrome DevTools Protocol is only supported for Chromium-base This connection is significantly lower fidelity than the Playwright protocol connection via [browser_type.connect()](/api/class-browsertype.mdx#browser-type-connect). If you are experiencing issues or attempting to use advanced functionality, you probably want to use [browser_type.connect()](/api/class-browsertype.mdx#browser-type-connect). ::: +:::warning +Playwright maintains a curated list of arguments for launching the browser. If you launch the browser without Playwright and do not pass the exact same arguments, some of Playwright functionality may be broken upon connecting to the browser. +::: + **Usage** + + +```py +context = browser.new_context() + +# A passkey your backend already provisioned for a test user. +context.credentials.create( + "example.com", + id=known_credential_id, # base64url + user_handle=known_user_handle, # base64url + private_key=known_private_key, # base64url PKCS#8 (DER) + public_key=known_public_key, # base64url SPKI (DER) +) +context.credentials.install() + +page = context.new_page() +page.goto("https://example.com/login") +# The page's navigator.credentials.get() is answered with the seeded passkey. +``` + + + + +```py +context = await browser.new_context() + +# A passkey your backend already provisioned for a test user. +await context.credentials.create( + "example.com", + id=known_credential_id, # base64url + user_handle=known_user_handle, # base64url + private_key=known_private_key, # base64url PKCS#8 (DER) + public_key=known_public_key, # base64url SPKI (DER) +) +await context.credentials.install() + +page = await context.new_page() +await page.goto("https://example.com/login") +# The page's navigator.credentials.get() is answered with the seeded passkey. +``` + + + + **Usage: capture a passkey, then reuse it** + + + +```py +# setup test: let the app register a passkey, then save it. +context = browser.new_context() +context.credentials.install() + +page = context.new_page() +page.goto("https://example.com/register") +page.get_by_role("button", name="Create a passkey").click() + +# Read back the passkey the page registered — it includes the private key. +[credential] = context.credentials.get(rp_id="example.com") +with open("playwright/.auth/passkey.json", "w") as f: + json.dump(credential, f) +``` + + + + +```py +# setup test: let the app register a passkey, then save it. +context = await browser.new_context() +await context.credentials.install() + +page = await context.new_page() +await page.goto("https://example.com/register") +await page.get_by_role("button", name="Create a passkey").click() + +# Read back the passkey the page registered — it includes the private key. +[credential] = await context.credentials.get(rp_id="example.com") +with open("playwright/.auth/passkey.json", "w") as f: + json.dump(credential, f) +``` + + + + + + + +```py +# later test: seed the captured passkey so the app starts already enrolled. +with open("playwright/.auth/passkey.json") as f: + credential = json.load(f) +context = browser.new_context() +context.credentials.create( + credential["rpId"], + id=credential["id"], + user_handle=credential["userHandle"], + private_key=credential["privateKey"], + public_key=credential["publicKey"], +) +context.credentials.install() + +page = context.new_page() +page.goto("https://example.com/login") +# navigator.credentials.get() resolves the captured passkey — already signed in. +``` + + + + +```py +# later test: seed the captured passkey so the app starts already enrolled. +with open("playwright/.auth/passkey.json") as f: + credential = json.load(f) +context = await browser.new_context() +await context.credentials.create( + credential["rpId"], + id=credential["id"], + user_handle=credential["userHandle"], + private_key=credential["privateKey"], + public_key=credential["publicKey"], +) +await context.credentials.install() + +page = await context.new_page() +await page.goto("https://example.com/login") +# navigator.credentials.get() resolves the captured passkey — already signed in. +``` + + + + **Defaults** @@ -28,9 +180,9 @@ There are two common ways to use it: Seeds a virtual WebAuthn credential and returns it. -With only `rpId`, generates a fresh **ECDSA P-256** keypair, credential id and user handle. The seeded credential is discoverable (resident), so the page can resolve it from both username-then-passkey and usernameless passkey flows. The returned object carries the `privateKey` and `publicKey`, so it can be persisted to disk and re-seeded in a later test. +With only [rp_id](/api/class-credentials.mdx#credentials-create-option-rp-id), generates a fresh **ECDSA P-256** keypair, credential id and user handle. The seeded credential is discoverable (resident), so the page can resolve it from both username-then-passkey and usernameless passkey flows. The returned object carries the private and public keys, so it can be persisted to disk and re-seeded in a later test. -To **import a known credential**, supply all four of `id`, `userHandle`, `privateKey` and `publicKey` together. +To **import a known credential**, supply all four of [id](/api/class-credentials.mdx#credentials-create-option-id), [user_handle](/api/class-credentials.mdx#credentials-create-option-user-handle), [private_key](/api/class-credentials.mdx#credentials-create-option-private-key) and [public_key](/api/class-credentials.mdx#credentials-create-option-public-key) together. Call [credentials.install()](/api/class-credentials.mdx#credentials-install) before navigating to a page that uses WebAuthn. @@ -42,6 +194,9 @@ credentials.create(rp_id, **kwargs) ``` **Arguments** +- `rp_id` [str]# + + Relying party id (typically the site's effective domain). - `id` [str] *(optional)*# Base64url-encoded credential id. Auto-generated if omitted. @@ -51,9 +206,6 @@ credentials.create(rp_id, **kwargs) - `public_key` [str] *(optional)*# Base64url-encoded SPKI (DER) public key. Auto-generated if omitted. -- `rp_id` [str]# - - Relying party id (typically the site's effective domain). - `user_handle` [str] *(optional)*# Base64url-encoded user handle. Auto-generated if omitted. @@ -104,9 +256,9 @@ credentials.delete(id) Added in: v1.61credentials.get -Returns every credential currently held by the authenticator, optionally filtered by `rpId` or `id`. This includes both credentials seeded with [credentials.create()](/api/class-credentials.mdx#credentials-create) and credentials the page registered itself by calling `navigator.credentials.create()`. +Returns every credential currently held by the authenticator, optionally filtered by [rp_id](/api/class-credentials.mdx#credentials-get-option-rp-id) or [id](/api/class-credentials.mdx#credentials-get-option-id). This includes both credentials seeded with [credentials.create()](/api/class-credentials.mdx#credentials-create) and credentials the page registered itself by calling `navigator.credentials.create()`. -Each returned credential includes its `privateKey` and `publicKey`, so a passkey the app just registered can be saved and re-seeded into a later test with [credentials.create()](/api/class-credentials.mdx#credentials-create) — see the second example in the class overview. +Each returned credential includes its private and public keys, so a passkey the app just registered can be saved and re-seeded into a later test with [credentials.create()](/api/class-credentials.mdx#credentials-create) — see the second example in the class overview. **Usage** @@ -148,7 +300,7 @@ credentials.get(**kwargs) Installs the virtual WebAuthn authenticator into the context, overriding `navigator.credentials.create()` and `navigator.credentials.get()` in all current and future pages. Call this before the page first touches `navigator.credentials`. -Required: until `install()` is called, no interception is in place and the page sees the platform's native (or absent) WebAuthn behaviour. Seeding credentials with [credentials.create()](/api/class-credentials.mdx#credentials-create) without `install()` populates the authenticator, but the page will never see those credentials. +Required: until [credentials.install()](/api/class-credentials.mdx#credentials-install) is called, no interception is in place and the page sees the platform's native (or absent) WebAuthn behaviour. Seeding credentials with [credentials.create()](/api/class-credentials.mdx#credentials-create) without installing populates the authenticator, but the page will never see those credentials. **Usage** diff --git a/python/docs/api/class-webstorage.mdx b/python/docs/api/class-webstorage.mdx index 2d4e44c96b..45dcd615d3 100644 --- a/python/docs/api/class-webstorage.mdx +++ b/python/docs/api/class-webstorage.mdx @@ -71,7 +71,7 @@ web_storage.clear() Added in: v1.61webStorage.get_item -Returns the value for the given `name`, or `null` if the key is not present. +Returns the value for the given [name](/api/class-webstorage.mdx#web-storage-get-item-option-name) if present. **Usage** @@ -93,7 +93,7 @@ web_storage.get_item(name) Added in: v1.61webStorage.items -Returns all items in the storage as `name`/`value` pairs. +Returns all items in the storage as name/value pairs. **Usage** @@ -115,7 +115,7 @@ web_storage.items() Added in: v1.61webStorage.remove_item -Removes the item with the given `name`. No-op if the item is absent. +Removes the item with the given [name](/api/class-webstorage.mdx#web-storage-remove-item-option-name). No-op if the item is absent. **Usage** @@ -137,7 +137,7 @@ web_storage.remove_item(name) Added in: v1.61webStorage.set_item -Sets the value for the given `name`. Overwrites any existing value for that name. +Sets the value for the given [name](/api/class-webstorage.mdx#web-storage-set-item-option-name). Overwrites any existing value for that name. **Usage** diff --git a/python/docs/docker.mdx b/python/docs/docker.mdx index 871f30e29b..3944fe6707 100644 --- a/python/docs/docker.mdx +++ b/python/docs/docker.mdx @@ -158,6 +158,7 @@ We currently publish images with the following tags: - `:v1.60.0` - Playwright v1.60.0 release docker image based on Ubuntu 24.04 LTS (Noble Numbat). - `:v1.60.0-noble` - Playwright v1.60.0 release docker image based on Ubuntu 24.04 LTS (Noble Numbat). - `:v1.60.0-jammy` - Playwright v1.60.0 release docker image based on Ubuntu 22.04 LTS (Jammy Jellyfish). +- `:v1.60.0-resolute` - Playwright v1.60.0 release docker image based on Ubuntu 26.04 LTS (Resolute Raccoon). :::note @@ -167,6 +168,7 @@ It is recommended to always pin your Docker image to a specific version if possi ### Base images We currently publish images based on the following [Ubuntu](https://hub.docker.com/_/ubuntu) versions: +- **Ubuntu 26.04 LTS** (Resolute Raccoon), image tags include `resolute` - **Ubuntu 24.04 LTS** (Noble Numbat), image tags include `noble` - **Ubuntu 22.04 LTS** (Jammy Jellyfish), image tags include `jammy` diff --git a/python/docs/getting-started-cli.mdx b/python/docs/getting-started-cli.mdx index f46575f00e..462b332b0d 100644 --- a/python/docs/getting-started-cli.mdx +++ b/python/docs/getting-started-cli.mdx @@ -32,6 +32,7 @@ playwright-cli --help Alternatively, install `@playwright/cli` as a local dependency and use `npx`: ```bash +npm install -D @playwright/cli@latest npx playwright-cli --help ``` diff --git a/python/docs/intro.mdx b/python/docs/intro.mdx index ad6afa158d..aa231c2b17 100644 --- a/python/docs/intro.mdx +++ b/python/docs/intro.mdx @@ -26,10 +26,34 @@ Get started by installing Playwright and running the example test to see it in a Install the [Pytest plugin](https://pypi.org/project/pytest-playwright/): + + + + ```bash pip install pytest-playwright ``` + + + + +```bash +poetry add pytest-playwright +``` + + + + + +```bash +uv add pytest-playwright +``` + + + + + Install the required browsers: ```bash @@ -72,15 +96,39 @@ pytest To update Playwright to the latest version run the following command: + + + + ```bash pip install pytest-playwright playwright -U ``` + + + + +```bash +poetry update pytest-playwright playwright +``` + + + + + +```bash +uv add --upgrade pytest-playwright playwright +``` + + + + + ## System requirements - Python 3.8 or higher. - Windows 11+, Windows Server 2019+ or Windows Subsystem for Linux (WSL). -- macOS 14 Sonoma, or later. -- Debian 12, Debian 13, Ubuntu 22.04, Ubuntu 24.04, on x86-64 and arm64 architecture. +- macOS 14 (Sonoma) or later. +- Debian 12 / 13, Ubuntu 22.04 / 24.04 / 26.04 (x86-64 or arm64). ## What's next - [Write tests using web first assertions, page fixtures and locators](./writing-tests.mdx) diff --git a/python/docs/library.mdx b/python/docs/library.mdx index 96f088b2e3..0141e22eac 100644 --- a/python/docs/library.mdx +++ b/python/docs/library.mdx @@ -10,12 +10,40 @@ import HTMLCard from '@site/src/components/HTMLCard'; [PyPI version](https://pypi.python.org/pypi/playwright/) + + + + ```bash pip install --upgrade pip pip install playwright playwright install ``` + + + + +```bash +poetry self update +poetry add playwright +playwright install +``` + + + + + +```bash +uv self update +uv add playwright +playwright install +``` + + + + + These commands download the Playwright package and install browser binaries for Chromium, Firefox and WebKit. To modify this behavior see [installation parameters](./browsers.mdx#install-browsers). ## Usage diff --git a/python/docs/release-notes.mdx b/python/docs/release-notes.mdx index 75c04eabbb..5730850772 100644 --- a/python/docs/release-notes.mdx +++ b/python/docs/release-notes.mdx @@ -9,6 +9,60 @@ import HTMLCard from '@site/src/components/HTMLCard'; import LiteYouTube from '@site/src/components/LiteYouTube'; +## Version 1.61 + +### 🔑 WebAuthn passkeys + +New [Credentials] virtual authenticator, available via [browser_context.credentials](/api/class-browsercontext.mdx#browser-context-credentials), lets tests register passkeys and answer `navigator.credentials.create()` / `navigator.credentials.get()` ceremonies in the page — no real hardware key required, works in all browsers: + +```python +context = browser.new_context() + +# Seed a passkey your backend provisioned for a test user. +context.credentials.create("example.com", + id=credential_id, + user_handle=user_handle, + private_key=private_key, + public_key=public_key, +) +context.credentials.install() + +page = context.new_page() +page.goto("https://example.com/login") +# The page's navigator.credentials.get() is answered with the seeded passkey. +``` + +You can also let the app register a passkey once in a setup test, read it back with [credentials.get()](/api/class-credentials.mdx#credentials-get), and seed it into later tests — see [Credentials] for details. + +### 🗃️ Web Storage + +New [WebStorage] API, available via [page.local_storage](/api/class-page.mdx#page-local-storage) and [page.session_storage](/api/class-page.mdx#page-session-storage), reads and writes the page's storage for the current origin: + +```python +page.local_storage.set_item("token", "abc") +token = page.local_storage.get_item("token") +items = page.session_storage.items() +``` + +### New APIs +- [api_response.security_details()](/api/class-apiresponse.mdx#api-response-security-details) and [api_response.server_addr()](/api/class-apiresponse.mdx#api-response-server-addr) mirror the browser-side [response.security_details()](/api/class-response.mdx#response-security-details) and [response.server_addr()](/api/class-response.mdx#response-server-addr). +- New option `artifacts_dir` in [browser_type.connect_over_cdp()](/api/class-browsertype.mdx#browser-type-connect-over-cdp) controls where artifacts such as traces and downloads are stored when attached to an existing browser. +- New option `cursor` in [screencast.show_actions()](/api/class-screencast.mdx#screencast-show-actions) controls the cursor decoration rendered for pointer actions. +- The `on_frame` callback in [screencast.start()](/api/class-screencast.mdx#screencast-start) now receives a `timestamp` of when the frame was presented by the browser. + +### 🛠️ Other improvements +- Playwright now supports Ubuntu 26.04. +- HAR and trace recordings now include WebSocket requests. + +### Browser Versions +- Chromium 149.0.7827.55 +- Mozilla Firefox 151.0 +- WebKit 26.5 + +This version was also tested against the following stable channels: +- Google Chrome 149 +- Microsoft Edge 149 + ## Version 1.60 ### 🌐 HAR recording on Tracing diff --git a/src/components/GitHubStarButton/index.tsx b/src/components/GitHubStarButton/index.tsx index e1816929bb..b3a09666fb 100644 --- a/src/components/GitHubStarButton/index.tsx +++ b/src/components/GitHubStarButton/index.tsx @@ -6,7 +6,7 @@ type StarButtonProps = { repo: string } -const STARS = '90k+'; // NOTE: this line is generated by src/generate.js. Do not change! +const STARS = '91k+'; // NOTE: this line is generated by src/generate.js. Do not change! const StarButton: React.FC = ({owner, repo}) => { if (!repo)