Skip to content

chore: roll to Playwright v1.52.0 #540

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 3 commits into from
May 5, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 3 additions & 3 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,17 +5,17 @@
[![PkgGoDev](https://pkg.go.dev/badge/github.com/playwright-community/playwright-go)](https://pkg.go.dev/github.com/playwright-community/playwright-go)
[![License](https://img.shields.io/badge/License-MIT-blue.svg)](http://opensource.org/licenses/MIT)
[![Go Report Card](https://goreportcard.com/badge/github.com/playwright-community/playwright-go)](https://goreportcard.com/report/github.com/playwright-community/playwright-go) ![Build Status](https://github.com/playwright-community/playwright-go/workflows/Go/badge.svg)
[![Join Slack](https://img.shields.io/badge/join-slack-infomational)](https://aka.ms/playwright-slack) [![Coverage Status](https://coveralls.io/repos/github/playwright-community/playwright-go/badge.svg?branch=main)](https://coveralls.io/github/playwright-community/playwright-go?branch=main) <!-- GEN:chromium-version-badge -->[![Chromium version](https://img.shields.io/badge/chromium-134.0.6998.35-blue.svg?logo=google-chrome)](https://www.chromium.org/Home)<!-- GEN:stop --> <!-- GEN:firefox-version-badge -->[![Firefox version](https://img.shields.io/badge/firefox-135.0-blue.svg?logo=mozilla-firefox)](https://www.mozilla.org/en-US/firefox/new/)<!-- GEN:stop --> <!-- GEN:webkit-version-badge -->[![WebKit version](https://img.shields.io/badge/webkit-18.4-blue.svg?logo=safari)](https://webkit.org/)<!-- GEN:stop -->
[![Join Slack](https://img.shields.io/badge/join-slack-infomational)](https://aka.ms/playwright-slack) [![Coverage Status](https://coveralls.io/repos/github/playwright-community/playwright-go/badge.svg?branch=main)](https://coveralls.io/github/playwright-community/playwright-go?branch=main) <!-- GEN:chromium-version-badge -->[![Chromium version](https://img.shields.io/badge/chromium-136.0.7103.25-blue.svg?logo=google-chrome)](https://www.chromium.org/Home)<!-- GEN:stop --> <!-- GEN:firefox-version-badge -->[![Firefox version](https://img.shields.io/badge/firefox-137.0-blue.svg?logo=mozilla-firefox)](https://www.mozilla.org/en-US/firefox/new/)<!-- GEN:stop --> <!-- GEN:webkit-version-badge -->[![WebKit version](https://img.shields.io/badge/webkit-18.4-blue.svg?logo=safari)](https://webkit.org/)<!-- GEN:stop -->

[API reference](https://playwright.dev/docs/api/class-playwright) | [Example recipes](https://github.com/playwright-community/playwright-go/tree/main/examples)

Playwright is a Go library to automate [Chromium](https://www.chromium.org/Home), [Firefox](https://www.mozilla.org/en-US/firefox/new/) and [WebKit](https://webkit.org/) with a single API. Playwright is built to enable cross-browser web automation that is **ever-green**, **capable**, **reliable** and **fast**.

| | Linux | macOS | Windows |
| :--- | :---: | :---: | :---: |
| Chromium <!-- GEN:chromium-version -->134.0.6998.35<!-- GEN:stop --> | ✅ | ✅ | ✅ |
| Chromium <!-- GEN:chromium-version -->136.0.7103.25<!-- GEN:stop --> | ✅ | ✅ | ✅ |
| WebKit <!-- GEN:webkit-version -->18.4<!-- GEN:stop --> | ✅ | ✅ | ✅ |
| Firefox <!-- GEN:firefox-version -->135.0<!-- GEN:stop --> | ✅ | ✅ | ✅ |
| Firefox <!-- GEN:firefox-version -->137.0<!-- GEN:stop --> | ✅ | ✅ | ✅ |

Headless execution is supported for all the browsers on all platforms.

Expand Down
35 changes: 26 additions & 9 deletions generated-interfaces.go
Original file line number Diff line number Diff line change
Expand Up @@ -382,8 +382,8 @@ type BrowserContext interface {
// [this] issue. We recommend disabling Service Workers when
// using request interception by setting “[object Object]” to `block`.
//
// 1. url: A glob pattern, regex pattern or predicate receiving [URL] to match while routing. When a “[object Object]” via the
// context options was provided and the passed URL is a path, it gets merged via the
// 1. url: A glob pattern, regex pattern, or predicate that receives a [URL] to match during routing. If “[object Object]” is
// set in the context options and the provided URL is a string that does not start with `*`, it is resolved using the
// [`new URL()`](https://developer.mozilla.org/en-US/docs/Web/API/URL/URL) constructor.
// 2. handler: handler function to route the request.
//
Expand Down Expand Up @@ -529,11 +529,15 @@ type BrowserType interface {
// Launches browser that uses persistent storage located at “[object Object]” and returns the only context. Closing
// this context will automatically close the browser.
//
// userDataDir: Path to a User Data Directory, which stores browser session data like cookies and local storage. More details for
// userDataDir: Path to a User Data Directory, which stores browser session data like cookies and local storage. Pass an empty
// string to create a temporary directory.
//
// More details for
// [Chromium](https://chromium.googlesource.com/chromium/src/+/master/docs/user_data_dir.md#introduction) and
// [Firefox](https://developer.mozilla.org/en-US/docs/Mozilla/Command_Line_Options#User_Profile). Note that Chromium's
// user data directory is the **parent** directory of the "Profile Path" seen at `chrome://version`. Pass an empty
// string to use a temporary directory instead.
// [Firefox](https://wiki.mozilla.org/Firefox/CommandLineOptions#User_profile). Chromium's user data directory is the
// **parent** directory of the "Profile Path" seen at `chrome://version`.
//
// Note that browsers do not allow launching multiple instances with the same User Data Directory.
LaunchPersistentContext(userDataDir string, options ...BrowserTypeLaunchPersistentContextOptions) (BrowserContext, error)

// Returns browser name. For example: `chromium`, `webkit` or `firefox`.
Expand Down Expand Up @@ -2906,6 +2910,16 @@ type LocatorAssertions interface {
// [visible]: https://playwright.dev/docs/actionability#visible
ToBeVisible(options ...LocatorAssertionsToBeVisibleOptions) error

// Ensures the [Locator] points to an element with given CSS classes. All classes from the asserted value, separated
// by spaces, must be present in the
// [Element.ClassList] in any order.
//
// expected: A string containing expected class names, separated by spaces, or a list of such strings to assert multiple
// elements.
//
// [Element.ClassList]: https://developer.mozilla.org/en-US/docs/Web/API/Element/classList
ToContainClass(expected interface{}, options ...LocatorAssertionsToContainClassOptions) error

// Ensures the [Locator] points to an element that contains the given text. All nested elements will be considered
// when computing the text content of the element. You can use regular expressions for the value as well.
//
Expand Down Expand Up @@ -2948,7 +2962,7 @@ type LocatorAssertions interface {
ToHaveAttribute(name string, value interface{}, options ...LocatorAssertionsToHaveAttributeOptions) error

// Ensures the [Locator] points to an element with given CSS classes. When a string is provided, it must fully match
// the element's `class` attribute. To match individual classes or perform partial matches, use a regular expression:
// the element's `class` attribute. To match individual classes use [LocatorAssertions.ToContainClass].
//
// expected: Expected class or RegExp or a list of those.
ToHaveClass(expected interface{}, options ...LocatorAssertionsToHaveClassOptions) error
Expand Down Expand Up @@ -3796,8 +3810,8 @@ type Page interface {
// using request interception by setting “[object Object]” to `block`.
// **NOTE** [Page.Route] will not intercept the first request of a popup page. Use [BrowserContext.Route] instead.
//
// 1. url: A glob pattern, regex pattern or predicate receiving [URL] to match while routing. When a “[object Object]” via the
// context options was provided and the passed URL is a path, it gets merged via the
// 1. url: A glob pattern, regex pattern, or predicate that receives a [URL] to match during routing. If “[object Object]” is
// set in the context options and the provided URL is a string that does not start with `*`, it is resolved using the
// [`new URL()`](https://developer.mozilla.org/en-US/docs/Web/API/URL/URL) constructor.
// 2. handler: handler function to route the request.
//
Expand Down Expand Up @@ -4376,6 +4390,9 @@ type Route interface {
// over to redirected requests.
// [Route.Continue] will immediately send the request to the network, other matching handlers won't be invoked. Use
// [Route.Fallback] If you want next matching handler in the chain to be invoked.
// **NOTE** The `Cookie` header cannot be overridden using this method. If a value is provided, it will be ignored,
// and the cookie will be loaded from the browser's cookie store. To set custom cookies, use
// [BrowserContext.AddCookies].
Continue(options ...RouteContinueOptions) error

// Continues route's request with optional overrides. The method is similar to [Route.Continue] with the difference
Expand Down
24 changes: 18 additions & 6 deletions generated-structs.go
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,10 @@ type APIRequestNewContextOptions struct {
HttpCredentials *HttpCredentials `json:"httpCredentials"`
// Whether to ignore HTTPS errors when sending network requests. Defaults to `false`.
IgnoreHttpsErrors *bool `json:"ignoreHTTPSErrors"`
// Maximum number of request redirects that will be followed automatically. An error will be thrown if the number is
// exceeded. Defaults to `20`. Pass `0` to not follow redirects. This can be overwritten for each request
// individually.
MaxRedirects *int `json:"maxRedirects"`
// Network proxy settings.
Proxy *Proxy `json:"proxy"`
// Populates context with given storage state. This option can be used to initialize context with logged-in
Expand Down Expand Up @@ -858,7 +862,7 @@ type BrowserTypeLaunchOptions struct {
// “[object Object]” option is `true`.
//
// [Chromium]: https://developers.google.com/web/updates/2017/04/headless-chrome
// [Firefox]: https://developer.mozilla.org/en-US/docs/Mozilla/Firefox/Headless_mode
// [Firefox]: https://hacks.mozilla.org/2017/12/using-headless-mode-in-firefox/
Headless *bool `json:"headless"`
// If `true`, Playwright does not pass its own configurations args and only uses the ones from “[object Object]”.
// Dangerous option; use with care. Defaults to `false`.
Expand Down Expand Up @@ -977,7 +981,7 @@ type BrowserTypeLaunchPersistentContextOptions struct {
// “[object Object]” option is `true`.
//
// [Chromium]: https://developers.google.com/web/updates/2017/04/headless-chrome
// [Firefox]: https://developer.mozilla.org/en-US/docs/Mozilla/Firefox/Headless_mode
// [Firefox]: https://hacks.mozilla.org/2017/12/using-headless-mode-in-firefox/
Headless *bool `json:"headless"`
// Credentials for [HTTP authentication]. If no
// origin is specified, the username and password are sent to any servers upon unauthorized responses.
Expand Down Expand Up @@ -2254,6 +2258,9 @@ type KeyboardTypeOptions struct {
}

type LocatorAriaSnapshotOptions struct {
// Generate symbolic reference for each element. One can use `aria-ref=<ref>` locator immediately after capturing the
// snapshot to perform actions on the element.
Ref *bool `json:"ref"`
// Maximum time in milliseconds. Defaults to `30000` (30 seconds). Pass `0` to disable timeout. The default value can
// be changed by using the [BrowserContext.SetDefaultTimeout] or [Page.SetDefaultTimeout] methods.
Timeout *float64 `json:"timeout"`
Expand Down Expand Up @@ -2413,14 +2420,14 @@ type LocatorElementHandleOptions struct {
}

type LocatorEvaluateOptions struct {
// Maximum time in milliseconds. Defaults to `30000` (30 seconds). Pass `0` to disable timeout. The default value can
// be changed by using the [BrowserContext.SetDefaultTimeout] or [Page.SetDefaultTimeout] methods.
// Maximum time in milliseconds to wait for the locator before evaluating. Note that after locator is resolved,
// evaluation itself is not limited by the timeout. Defaults to `30000` (30 seconds). Pass `0` to disable timeout.
Timeout *float64 `json:"timeout"`
}

type LocatorEvaluateHandleOptions struct {
// Maximum time in milliseconds. Defaults to `30000` (30 seconds). Pass `0` to disable timeout. The default value can
// be changed by using the [BrowserContext.SetDefaultTimeout] or [Page.SetDefaultTimeout] methods.
// Maximum time in milliseconds to wait for the locator before evaluating. Note that after locator is resolved,
// evaluation itself is not limited by the timeout. Defaults to `30000` (30 seconds). Pass `0` to disable timeout.
Timeout *float64 `json:"timeout"`
}

Expand Down Expand Up @@ -2940,6 +2947,11 @@ type LocatorAssertionsToBeVisibleOptions struct {
Visible *bool `json:"visible"`
}

type LocatorAssertionsToContainClassOptions struct {
// Time to retry the assertion for in milliseconds. Defaults to `5000`.
Timeout *float64 `json:"timeout"`
}

type LocatorAssertionsToContainTextOptions struct {
// Whether to perform case-insensitive match. “[object Object]” option takes precedence over the corresponding regular
// expression flag if specified.
Expand Down
92 changes: 85 additions & 7 deletions glob.go
Original file line number Diff line number Diff line change
@@ -1,7 +1,9 @@
package playwright

import (
"net/url"
"regexp"
"strconv"
"strings"
)

Expand Down Expand Up @@ -59,12 +61,6 @@ func globMustToRegex(glob string) *regexp.Regexp {
}
} else {
switch c {
case '?':
tokens = append(tokens, ".")
case '[':
tokens = append(tokens, "[")
case ']':
tokens = append(tokens, "]")
case '{':
inGroup = true
tokens = append(tokens, "(")
Expand All @@ -75,7 +71,7 @@ func globMustToRegex(glob string) *regexp.Regexp {
if inGroup {
tokens = append(tokens, "|")
} else {
tokens = append(tokens, string(c))
tokens = append(tokens, "\\"+string(c))
}
default:
if _, ok := escapedChars[c]; ok {
Expand All @@ -90,3 +86,85 @@ func globMustToRegex(glob string) *regexp.Regexp {
tokens = append(tokens, "$")
return regexp.MustCompile(strings.Join(tokens, ""))
}

func resolveGlobToRegex(baseURL *string, glob string, isWebSocketUrl bool) *regexp.Regexp {
if isWebSocketUrl {
baseURL = toWebSocketBaseURL(baseURL)
}
glob = resolveGlobBase(baseURL, glob)
return globMustToRegex(glob)
}

func resolveGlobBase(baseURL *string, match string) string {
if strings.HasPrefix(match, "*") {
return match
}

tokenMap := make(map[string]string)
mapToken := func(original string, replacement string) string {
if len(original) == 0 {
return ""
}
tokenMap[replacement] = original
return replacement
}
// Escaped `\\?` behaves the same as `?` in our glob patterns.
match = strings.ReplaceAll(match, `\\?`, "?")
// Glob symbols may be escaped in the URL and some of them such as ? affect resolution,
// so we replace them with safe components first.
relativePath := strings.Split(match, "/")
for i, token := range relativePath {
if token == "." || token == ".." || token == "" {
continue
}
// Handle special case of http*://, note that the new schema has to be
// a web schema so that slashes are properly inserted after domain.
if i == 0 && strings.HasSuffix(token, ":") {
relativePath[i] = mapToken(token, "http:")
} else {
questionIndex := strings.Index(token, "?")
if questionIndex == -1 {
relativePath[i] = mapToken(token, "$_"+strconv.Itoa(i)+"_$")
} else {
newPrefix := mapToken(token[:questionIndex], "$_"+strconv.Itoa(i)+"_$")
newSuffix := mapToken(token[questionIndex:], "?$"+strconv.Itoa(i)+"_$")
relativePath[i] = newPrefix + newSuffix
}
}
}
resolved := constructURLBasedOnBaseURL(baseURL, strings.Join(relativePath, "/"))
for token, original := range tokenMap {
resolved = strings.ReplaceAll(resolved, token, original)
}
return resolved
}

func constructURLBasedOnBaseURL(baseURL *string, givenURL string) string {
u, err := url.Parse(givenURL)
if err != nil {
return givenURL
}
if baseURL != nil {
base, err := url.Parse(*baseURL)
if err != nil {
return givenURL
}
u = base.ResolveReference(u)
}
if u.Path == "" { // In Node.js, new URL('http://localhost') returns 'http://localhost/'.
u.Path = "/"
}
return u.String()
}

func toWebSocketBaseURL(baseURL *string) *string {
if baseURL == nil {
return nil
}

// Allow http(s) baseURL to match ws(s) urls.
re := regexp.MustCompile(`(?m)^http(s?://)`)
wsBaseURL := re.ReplaceAllString(*baseURL, "ws$1")

return &wsBaseURL
}
62 changes: 53 additions & 9 deletions glob_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -51,13 +51,6 @@ func Test_globMustToRegex(t *testing.T) {
},
want: true,
},
{
args: args{
glob: "http://localhost:8080/?imple/path.js",
target: "http://localhost:8080/simple/path.js",
},
want: true,
},
{
args: args{
glob: "**/{a,b}.js",
Expand Down Expand Up @@ -137,7 +130,35 @@ func Test_globMustToRegex(t *testing.T) {
},
{
args: args{
glob: "**/three-columns/settings.html?**id=[a-z]**",
glob: "**/api/v[0-9]",
target: "http://example.com/api/v[0-9]",
},
want: true,
},
{
args: args{
glob: "**/api/v[0-9]",
target: "http://example.com/api/version",
},
want: false,
},
{
args: args{
glob: "**/api\\?param",
target: "http://example.com/api?param",
},
want: true,
},
{
args: args{
glob: "**/api\\?param",
target: "http://example.com/api-param",
},
want: false,
},
{
args: args{
glob: "**/three-columns/settings.html\\?**id=settings-**",
target: "http://mydomain:8080/blah/blah/three-columns/settings.html?id=settings-e3c58efe-02e9-44b0-97ac-dd138100cf7c&blah",
},
want: true,
Expand All @@ -155,6 +176,29 @@ func Test_globMustToRegex(t *testing.T) {
require.Equal(t, globMustToRegex("\\").String(), `^\\$`)
require.Equal(t, globMustToRegex("\\\\").String(), `^\\$`)
require.Equal(t, globMustToRegex("\\[").String(), `^\[$`)
require.Equal(t, globMustToRegex("[a-z]").String(), `^[a-z]$`)
require.Equal(t, globMustToRegex("[a-z]").String(), `^\[a-z\]$`)
require.Equal(t, globMustToRegex("$^+.\\*()|\\?\\{\\}\\[\\]").String(), `^\$\^\+\.\*\(\)\|\?\{\}\[\]$`)
}

func TestURLMatches(t *testing.T) {
require.True(t, newURLMatcher("http://playwright.dev", nil).Matches("http://playwright.dev/"))
require.True(t, newURLMatcher("http://playwright.dev?a=b", nil).Matches("http://playwright.dev/?a=b"))
require.True(t, newURLMatcher("h*://playwright.dev", nil).Matches("http://playwright.dev/"))
require.True(t, newURLMatcher("http://*.playwright.dev?x=y", nil).Matches("http://api.playwright.dev/?x=y"))
require.True(t, newURLMatcher("**/foo/**", nil).Matches("http://playwright.dev/foo/bar"))
require.True(t, newURLMatcher("?x=y", String("http://playwright.dev")).Matches("http://playwright.dev/?x=y"))
require.True(t, newURLMatcher("./bar?x=y", String("http://playwright.dev/foo/")).Matches("http://playwright.dev/foo/bar?x=y"))

// This is not supported, we treat ? as a query separator.
require.False(t, globMustToRegex("http://localhost:8080/?imple/path.js").MatchString("http://localhost:8080/Simple/path.js"))
require.False(t, newURLMatcher("http://playwright.?ev", nil).Matches("http://playwright.dev/"))
require.True(t, newURLMatcher("http://playwright.?ev", nil).Matches("http://playwright./?ev"))
require.False(t, newURLMatcher("http://playwright.dev/f??", nil).Matches("http://playwright.dev/foo"))
require.True(t, newURLMatcher("http://playwright.dev/f??", nil).Matches("http://playwright.dev/f??"))
require.True(t, newURLMatcher("http://playwright.dev\\?x=y", nil).Matches("http://playwright.dev/?x=y"))
require.True(t, newURLMatcher("http://playwright.dev/\\?x=y", nil).Matches("http://playwright.dev/?x=y"))
require.True(t, newURLMatcher("?bar", String("http://playwright.dev/foo")).Matches("http://playwright.dev/foo?bar"))
require.True(t, newURLMatcher("\\\\?bar", String("http://playwright.dev/foo")).Matches("http://playwright.dev/foo?bar"))
require.True(t, newURLMatcher("**/foo", String("http://first.host/")).Matches("http://second.host/foo"))
require.True(t, newURLMatcher("*//localhost/", String("http://playwright.dev/")).Matches("http://localhost/"))
}
4 changes: 2 additions & 2 deletions go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,8 @@ go 1.22

require (
github.com/coder/websocket v1.8.12
github.com/deckarep/golang-set/v2 v2.6.0
github.com/go-jose/go-jose/v3 v3.0.3
github.com/deckarep/golang-set/v2 v2.7.0
github.com/go-jose/go-jose/v3 v3.0.4
github.com/go-stack/stack v1.8.1
github.com/h2non/filetype v1.1.3
github.com/mitchellh/go-ps v1.0.0
Expand Down
Loading