diff --git a/go.mod b/go.mod index 847db827eae..1b2ce104a68 100644 --- a/go.mod +++ b/go.mod @@ -14,7 +14,7 @@ require ( github.com/go-sourcemap/sourcemap v2.1.4+incompatible github.com/golang/protobuf v1.5.4 github.com/gorilla/websocket v1.5.3 - github.com/grafana/xk6-browser v1.9.1 + github.com/grafana/xk6-browser v1.9.2-0.20241118153201-d741e4540a3a github.com/grafana/xk6-dashboard v0.7.5 github.com/grafana/xk6-output-opentelemetry v0.3.0 github.com/grafana/xk6-output-prometheus-remote v0.5.0 diff --git a/go.sum b/go.sum index 67fbbf5686f..ee5b622ff7d 100644 --- a/go.sum +++ b/go.sum @@ -87,8 +87,8 @@ github.com/gorilla/websocket v1.5.3 h1:saDtZ6Pbx/0u+bgYQ3q96pZgCzfhKXGPqt7kZ72aN github.com/gorilla/websocket v1.5.3/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE= github.com/grafana/sobek v0.0.0-20241024150027-d91f02b05e9b h1:hzfIt1lf19Zx1jIYdeHvuWS266W+jL+7dxbpvH2PZMQ= github.com/grafana/sobek v0.0.0-20241024150027-d91f02b05e9b/go.mod h1:FmcutBFPLiGgroH42I4/HBahv7GxVjODcVWFTw1ISes= -github.com/grafana/xk6-browser v1.9.1 h1:wmISbIbzl7JtmvpkbXKhUg3XtFPepN+fss6UupZyRW0= -github.com/grafana/xk6-browser v1.9.1/go.mod h1:c0wf9sUlUjbhCnwHUFf6tkgkpoh/AXMuVd21XXIhyWY= +github.com/grafana/xk6-browser v1.9.2-0.20241118153201-d741e4540a3a h1:nZXZ/QAXQEv3N66ExeAWEx0TVYof/qjbnm7ug3A7gNk= +github.com/grafana/xk6-browser v1.9.2-0.20241118153201-d741e4540a3a/go.mod h1:Kvysy3onM6dsyh8Vi7Sqnzwolp4tSL11NN1ckNa0emU= github.com/grafana/xk6-dashboard v0.7.5 h1:TcILyffT/Ea/XD7xG1jMA5lwtusOPRbEQsQDHmO30Mk= github.com/grafana/xk6-dashboard v0.7.5/go.mod h1:Y75F8xmgCraKT+pBzFH6me9AyH5PkXD+Bxo1dm6Il/M= github.com/grafana/xk6-output-opentelemetry v0.3.0 h1:dmclGBFtFVRJijqLncpu2dKVIIvx1GS3y6sQGg4Khl8= diff --git a/vendor/github.com/grafana/xk6-browser/browser/browser_context_mapping.go b/vendor/github.com/grafana/xk6-browser/browser/browser_context_mapping.go index c60ce762586..46efd2e3031 100644 --- a/vendor/github.com/grafana/xk6-browser/browser/browser_context_mapping.go +++ b/vendor/github.com/grafana/xk6-browser/browser/browser_context_mapping.go @@ -4,6 +4,7 @@ import ( "errors" "fmt" "reflect" + "time" "github.com/grafana/sobek" @@ -74,25 +75,34 @@ func mapBrowserContext(vu moduleVU, bc *common.BrowserContext) mapping { //nolin return bc.Cookies(urls...) //nolint:wrapcheck }) }, - "grantPermissions": func(permissions []string, opts sobek.Value) *sobek.Promise { + "grantPermissions": func(permissions []string, opts sobek.Value) (*sobek.Promise, error) { + popts, err := exportTo[common.GrantPermissionsOptions](vu.Runtime(), opts) + if err != nil { + return nil, fmt.Errorf("parsing grant permission options: %w", err) + } return k6ext.Promise(vu.Context(), func() (any, error) { - popts := common.NewGrantPermissionsOptions() - popts.Parse(vu.Context(), opts) - - return nil, bc.GrantPermissions(permissions, popts) //nolint:wrapcheck - }) + return nil, bc.GrantPermissions(permissions, popts) + }), nil }, "setDefaultNavigationTimeout": bc.SetDefaultNavigationTimeout, "setDefaultTimeout": bc.SetDefaultTimeout, - "setGeolocation": func(geolocation sobek.Value) *sobek.Promise { + "setGeolocation": func(geolocation sobek.Value) (*sobek.Promise, error) { + gl, err := exportTo[common.Geolocation](vu.Runtime(), geolocation) + if err != nil { + return nil, fmt.Errorf("parsing geo location: %w", err) + } return k6ext.Promise(vu.Context(), func() (any, error) { - return nil, bc.SetGeolocation(geolocation) //nolint:wrapcheck - }) + return nil, bc.SetGeolocation(&gl) + }), nil }, - "setHTTPCredentials": func(httpCredentials sobek.Value) *sobek.Promise { + "setHTTPCredentials": func(httpCredentials sobek.Value) (*sobek.Promise, error) { + creds, err := exportTo[common.Credentials](rt, httpCredentials) + if err != nil { + return nil, fmt.Errorf("parsing HTTP credentials: %w", err) + } return k6ext.Promise(vu.Context(), func() (any, error) { - return nil, bc.SetHTTPCredentials(httpCredentials) //nolint:staticcheck,wrapcheck - }) + return nil, bc.SetHTTPCredentials(creds) //nolint:staticcheck + }), nil }, "setOffline": func(offline bool) *sobek.Promise { return k6ext.Promise(vu.Context(), func() (any, error) { @@ -100,14 +110,12 @@ func mapBrowserContext(vu moduleVU, bc *common.BrowserContext) mapping { //nolin }) }, "waitForEvent": func(event string, optsOrPredicate sobek.Value) (*sobek.Promise, error) { - ctx := vu.Context() - popts := common.NewWaitForEventOptions( - bc.Timeout(), - ) - if err := popts.Parse(ctx, optsOrPredicate); err != nil { - return nil, fmt.Errorf("parsing waitForEvent options: %w", err) + popts, err := parseWaitForEventOptions(vu.Runtime(), optsOrPredicate, bc.Timeout()) + if err != nil { + return nil, fmt.Errorf("parsing wait for event options: %w", err) } + ctx := vu.Context() return k6ext.Promise(ctx, func() (result any, reason error) { var runInTaskQueue func(p *common.Page) (bool, error) if popts.PredicateFn != nil { @@ -177,3 +185,48 @@ func mapBrowserContext(vu moduleVU, bc *common.BrowserContext) mapping { //nolin }, } } + +// waitForEventOptions are the options used by the browserContext.waitForEvent API. +type waitForEventOptions struct { + Timeout time.Duration + PredicateFn sobek.Callable +} + +// parseWaitForEventOptions parses optsOrPredicate into a WaitForEventOptions. +// It returns a WaitForEventOptions with the default timeout if optsOrPredicate is nil, +// or not a callable predicate function. +// It can parse only a callable predicate function or an object which contains a +// callable predicate function and a timeout. +func parseWaitForEventOptions( + rt *sobek.Runtime, optsOrPredicate sobek.Value, defaultTime time.Duration, +) (*waitForEventOptions, error) { + w := &waitForEventOptions{ + Timeout: defaultTime, + } + + if !sobekValueExists(optsOrPredicate) { + return w, nil + } + var isCallable bool + w.PredicateFn, isCallable = sobek.AssertFunction(optsOrPredicate) + if isCallable { + return w, nil + } + + opts := optsOrPredicate.ToObject(rt) + for _, k := range opts.Keys() { + switch k { + case "predicate": + w.PredicateFn, isCallable = sobek.AssertFunction(opts.Get(k)) + if !isCallable { + return nil, errors.New("predicate function is not callable") + } + case "timeout": + w.Timeout = time.Duration(opts.Get(k).ToInteger()) * time.Millisecond + default: + return nil, fmt.Errorf("unknown option: %s", k) + } + } + + return w, nil +} diff --git a/vendor/github.com/grafana/xk6-browser/browser/browser_mapping.go b/vendor/github.com/grafana/xk6-browser/browser/browser_mapping.go index 19569c09cf0..4d28d396e59 100644 --- a/vendor/github.com/grafana/xk6-browser/browser/browser_mapping.go +++ b/vendor/github.com/grafana/xk6-browser/browser/browser_mapping.go @@ -36,8 +36,8 @@ func mapBrowser(vu moduleVU) mapping { //nolint:funlen,cyclop,gocognit return b.IsConnected(), nil }, "newContext": func(opts sobek.Value) (*sobek.Promise, error) { - popts := common.NewBrowserContextOptions() - if err := popts.Parse(vu.Context(), opts); err != nil { + popts, err := parseBrowserContextOptions(vu.Runtime(), opts) + if err != nil { return nil, fmt.Errorf("parsing browser.newContext options: %w", err) } return k6ext.Promise(vu.Context(), func() (any, error) { @@ -71,8 +71,8 @@ func mapBrowser(vu moduleVU) mapping { //nolint:funlen,cyclop,gocognit return b.Version(), nil }, "newPage": func(opts sobek.Value) (*sobek.Promise, error) { - popts := common.NewBrowserContextOptions() - if err := popts.Parse(vu.Context(), opts); err != nil { + popts, err := parseBrowserContextOptions(vu.Runtime(), opts) + if err != nil { return nil, fmt.Errorf("parsing browser.newPage options: %w", err) } return k6ext.Promise(vu.Context(), func() (any, error) { @@ -107,3 +107,12 @@ func initBrowserContext(bctx *common.BrowserContext, testRunID string) error { return nil } + +// parseBrowserContextOptions parses the [common.BrowserContext] options from a Sobek value. +func parseBrowserContextOptions(rt *sobek.Runtime, opts sobek.Value) (*common.BrowserContextOptions, error) { + b := common.DefaultBrowserContextOptions() + if err := mergeWith(rt, b, opts); err != nil { + return nil, err + } + return b, nil +} diff --git a/vendor/github.com/grafana/xk6-browser/browser/frame_mapping.go b/vendor/github.com/grafana/xk6-browser/browser/frame_mapping.go index f300070ce2e..d414c7dcb17 100644 --- a/vendor/github.com/grafana/xk6-browser/browser/frame_mapping.go +++ b/vendor/github.com/grafana/xk6-browser/browser/frame_mapping.go @@ -230,7 +230,7 @@ func mapFrame(vu moduleVU, f *common.Frame) mapping { //nolint:gocognit,cyclop }, "title": func() *sobek.Promise { return k6ext.Promise(vu.Context(), func() (any, error) { - return f.Title(), nil + return f.Title() }) }, "type": func(selector, text string, opts sobek.Value) *sobek.Promise { diff --git a/vendor/github.com/grafana/xk6-browser/browser/helpers.go b/vendor/github.com/grafana/xk6-browser/browser/helpers.go index ba93f28f57f..10b80473eac 100644 --- a/vendor/github.com/grafana/xk6-browser/browser/helpers.go +++ b/vendor/github.com/grafana/xk6-browser/browser/helpers.go @@ -16,6 +16,26 @@ func panicIfFatalError(ctx context.Context, err error) { } } +// mergeWith merges the Sobek value with the existing Go value. +func mergeWith[T any](rt *sobek.Runtime, src T, v sobek.Value) error { + if !sobekValueExists(v) { + return nil + } + return rt.ExportTo(v, &src) //nolint:wrapcheck +} + +// exportTo exports the Sobek value to a Go value. +// It returns the zero value of T if obj does not exist in the Sobek runtime. +// It's caller's responsibility to check for nilness. +func exportTo[T any](rt *sobek.Runtime, obj sobek.Value) (T, error) { + var t T + if !sobekValueExists(obj) { + return t, nil + } + err := rt.ExportTo(obj, &t) + return t, err //nolint:wrapcheck +} + // exportArg exports the value and returns it. // It returns nil if the value is undefined or null. func exportArg(gv sobek.Value) any { diff --git a/vendor/github.com/grafana/xk6-browser/browser/keyboard_mapping.go b/vendor/github.com/grafana/xk6-browser/browser/keyboard_mapping.go index c129d383e26..a634fe40f84 100644 --- a/vendor/github.com/grafana/xk6-browser/browser/keyboard_mapping.go +++ b/vendor/github.com/grafana/xk6-browser/browser/keyboard_mapping.go @@ -23,22 +23,20 @@ func mapKeyboard(vu moduleVU, kb *common.Keyboard) mapping { }, "press": func(key string, opts sobek.Value) *sobek.Promise { return k6ext.Promise(vu.Context(), func() (any, error) { - kbdOpts := common.NewKeyboardOptions() - if err := kbdOpts.Parse(vu.Context(), opts); err != nil { + kbopts, err := exportTo[common.KeyboardOptions](vu.Runtime(), opts) + if err != nil { return nil, fmt.Errorf("parsing keyboard options: %w", err) } - - return nil, kb.Press(key, kbdOpts) //nolint:wrapcheck + return nil, kb.Press(key, kbopts) }) }, "type": func(text string, opts sobek.Value) *sobek.Promise { return k6ext.Promise(vu.Context(), func() (any, error) { - kbdOpts := common.NewKeyboardOptions() - if err := kbdOpts.Parse(vu.Context(), opts); err != nil { + kbopts, err := exportTo[common.KeyboardOptions](vu.Runtime(), opts) + if err != nil { return nil, fmt.Errorf("parsing keyboard options: %w", err) } - - return nil, kb.Type(text, kbdOpts) //nolint:wrapcheck + return nil, kb.Type(text, kbopts) }) }, "insertText": func(text string) *sobek.Promise { diff --git a/vendor/github.com/grafana/xk6-browser/browser/sync_browser_context_mapping.go b/vendor/github.com/grafana/xk6-browser/browser/sync_browser_context_mapping.go index b304a64a0b8..8873c40deeb 100644 --- a/vendor/github.com/grafana/xk6-browser/browser/sync_browser_context_mapping.go +++ b/vendor/github.com/grafana/xk6-browser/browser/sync_browser_context_mapping.go @@ -49,10 +49,11 @@ func syncMapBrowserContext(vu moduleVU, bc *common.BrowserContext) mapping { //n "close": bc.Close, "cookies": bc.Cookies, "grantPermissions": func(permissions []string, opts sobek.Value) error { - pOpts := common.NewGrantPermissionsOptions() - pOpts.Parse(vu.Context(), opts) - - return bc.GrantPermissions(permissions, pOpts) //nolint:wrapcheck + popts, err := exportTo[common.GrantPermissionsOptions](vu.Runtime(), opts) + if err != nil { + return fmt.Errorf("parsing grant permission options: %w", err) + } + return bc.GrantPermissions(permissions, popts) }, "setDefaultNavigationTimeout": bc.SetDefaultNavigationTimeout, "setDefaultTimeout": bc.SetDefaultTimeout, @@ -60,14 +61,12 @@ func syncMapBrowserContext(vu moduleVU, bc *common.BrowserContext) mapping { //n "setHTTPCredentials": bc.SetHTTPCredentials, //nolint:staticcheck "setOffline": bc.SetOffline, "waitForEvent": func(event string, optsOrPredicate sobek.Value) (*sobek.Promise, error) { - ctx := vu.Context() - popts := common.NewWaitForEventOptions( - bc.Timeout(), - ) - if err := popts.Parse(ctx, optsOrPredicate); err != nil { - return nil, fmt.Errorf("parsing waitForEvent options: %w", err) + popts, err := parseWaitForEventOptions(vu.Runtime(), optsOrPredicate, bc.Timeout()) + if err != nil { + return nil, fmt.Errorf("parsing wait for event options: %w", err) } + ctx := vu.Context() return k6ext.Promise(ctx, func() (result any, reason error) { var runInTaskQueue func(p *common.Page) (bool, error) if popts.PredicateFn != nil { diff --git a/vendor/github.com/grafana/xk6-browser/browser/sync_browser_mapping.go b/vendor/github.com/grafana/xk6-browser/browser/sync_browser_mapping.go index 689e610a1bc..d9c8fd1e284 100644 --- a/vendor/github.com/grafana/xk6-browser/browser/sync_browser_mapping.go +++ b/vendor/github.com/grafana/xk6-browser/browser/sync_browser_mapping.go @@ -4,8 +4,6 @@ import ( "fmt" "github.com/grafana/sobek" - - "github.com/grafana/xk6-browser/common" ) // syncMapBrowser is like mapBrowser but returns synchronous functions. @@ -34,8 +32,8 @@ func syncMapBrowser(vu moduleVU) mapping { //nolint:funlen,cyclop return b.IsConnected(), nil }, "newContext": func(opts sobek.Value) (*sobek.Object, error) { - popts := common.NewBrowserContextOptions() - if err := popts.Parse(vu.Context(), opts); err != nil { + popts, err := parseBrowserContextOptions(vu.Runtime(), opts) + if err != nil { return nil, fmt.Errorf("parsing browser.newContext options: %w", err) } @@ -71,9 +69,9 @@ func syncMapBrowser(vu moduleVU) mapping { //nolint:funlen,cyclop return b.Version(), nil }, "newPage": func(opts sobek.Value) (mapping, error) { - popts := common.NewBrowserContextOptions() - if err := popts.Parse(vu.Context(), opts); err != nil { - return nil, fmt.Errorf("parsing browser.newContext options: %w", err) + popts, err := parseBrowserContextOptions(vu.Runtime(), opts) + if err != nil { + return nil, fmt.Errorf("parsing browser.newPage options: %w", err) } b, err := vu.browser() diff --git a/vendor/github.com/grafana/xk6-browser/browser/sync_keyboard_mapping.go b/vendor/github.com/grafana/xk6-browser/browser/sync_keyboard_mapping.go index 84adfc6300b..3d6161de207 100644 --- a/vendor/github.com/grafana/xk6-browser/browser/sync_keyboard_mapping.go +++ b/vendor/github.com/grafana/xk6-browser/browser/sync_keyboard_mapping.go @@ -13,20 +13,18 @@ func syncMapKeyboard(vu moduleVU, kb *common.Keyboard) mapping { "down": kb.Down, "up": kb.Up, "press": func(key string, opts sobek.Value) error { - kbdOpts := common.NewKeyboardOptions() - if err := kbdOpts.Parse(vu.Context(), opts); err != nil { + kbopts, err := exportTo[common.KeyboardOptions](vu.Runtime(), opts) + if err != nil { return fmt.Errorf("parsing keyboard options: %w", err) } - - return kb.Press(key, kbdOpts) //nolint:wrapcheck + return kb.Press(key, kbopts) }, "type": func(text string, opts sobek.Value) error { - kbdOpts := common.NewKeyboardOptions() - if err := kbdOpts.Parse(vu.Context(), opts); err != nil { + kbopts, err := exportTo[common.KeyboardOptions](vu.Runtime(), opts) + if err != nil { return fmt.Errorf("parsing keyboard options: %w", err) } - - return kb.Type(text, kbdOpts) //nolint:wrapcheck + return kb.Type(text, kbopts) }, "insertText": kb.InsertText, } diff --git a/vendor/github.com/grafana/xk6-browser/common/browser.go b/vendor/github.com/grafana/xk6-browser/common/browser.go index 8a1638cafe1..902fc5d450d 100644 --- a/vendor/github.com/grafana/xk6-browser/common/browser.go +++ b/vendor/github.com/grafana/xk6-browser/common/browser.go @@ -157,7 +157,7 @@ func (b *Browser) connect() error { } // We don't need to lock this because `connect()` is called only in NewBrowser - b.defaultContext, err = NewBrowserContext(b.vuCtx, b, "", NewBrowserContextOptions(), b.logger) + b.defaultContext, err = NewBrowserContext(b.vuCtx, b, "", DefaultBrowserContextOptions(), b.logger) if err != nil { return fmt.Errorf("browser connect: %w", err) } @@ -691,6 +691,19 @@ func (b *Browser) fetchVersion() (browserVersion, error) { return browserVersion{}, fmt.Errorf("getting browser version information: %w", err) } + // Adjust the user agent to remove the headless part. + // + // Including Headless might cause issues with some websites that treat headless + // browsers differently. Later on, [BrowserContext] will set the user agent to + // this user agent if not set by the user. This will force [FrameSession] to + // set the user agent to the browser's user agent. + // + // Doing this here provides a consistent user agent across all browser contexts. + // Also, it makes it consistent to query the user agent from the browser. + if b.browserOpts.Headless { + bv.userAgent = strings.ReplaceAll(bv.userAgent, "Headless", "") + } + return bv, nil } diff --git a/vendor/github.com/grafana/xk6-browser/common/browser_context.go b/vendor/github.com/grafana/xk6-browser/common/browser_context.go index d56b7184834..a1e2bccf817 100644 --- a/vendor/github.com/grafana/xk6-browser/common/browser_context.go +++ b/vendor/github.com/grafana/xk6-browser/common/browser_context.go @@ -13,7 +13,6 @@ import ( "github.com/chromedp/cdproto/network" "github.com/chromedp/cdproto/storage" "github.com/chromedp/cdproto/target" - "github.com/grafana/sobek" "github.com/grafana/xk6-browser/common/js" "github.com/grafana/xk6-browser/k6error" @@ -117,7 +116,12 @@ func NewBrowserContext( ) (*BrowserContext, error) { // set the default options if none provided. if opts == nil { - opts = NewBrowserContextOptions() + opts = DefaultBrowserContextOptions() + } + // Always use the [Browser]'s user agent if it's not set by the user. + // Setting this forces [FrameSession] to set Chromium's user agent. + if strings.TrimSpace(opts.UserAgent) == "" { + opts.UserAgent = browser.UserAgent() } b := BrowserContext{ @@ -132,7 +136,7 @@ func NewBrowserContext( } if len(opts.Permissions) > 0 { - err := b.GrantPermissions(opts.Permissions, NewGrantPermissionsOptions()) + err := b.GrantPermissions(opts.Permissions, GrantPermissionsOptions{}) if err != nil { return nil, err } @@ -207,7 +211,7 @@ func (b *BrowserContext) Close() error { } // GrantPermissions enables the specified permissions, all others will be disabled. -func (b *BrowserContext) GrantPermissions(permissions []string, opts *GrantPermissionsOptions) error { +func (b *BrowserContext) GrantPermissions(permissions []string, opts GrantPermissionsOptions) error { b.logger.Debugf("BrowserContext:GrantPermissions", "bctxid:%v", b.id) permsToProtocol := map[string]cdpbrowser.PermissionType{ @@ -293,12 +297,11 @@ func (b *BrowserContext) SetDefaultTimeout(timeout int64) { } // SetGeolocation overrides the geo location of the user. -func (b *BrowserContext) SetGeolocation(geolocation sobek.Value) error { +func (b *BrowserContext) SetGeolocation(g *Geolocation) error { b.logger.Debugf("BrowserContext:SetGeolocation", "bctxid:%v", b.id) - g := NewGeolocation() - if err := g.Parse(b.ctx, geolocation); err != nil { - return fmt.Errorf("parsing geo location: %w", err) + if err := g.Validate(); err != nil { + return fmt.Errorf("validating geo location: %w", err) } b.opts.Geolocation = g @@ -317,17 +320,12 @@ func (b *BrowserContext) SetGeolocation(geolocation sobek.Value) error { // See for details: // - https://github.com/microsoft/playwright/issues/2196#issuecomment-627134837 // - https://github.com/microsoft/playwright/pull/2763 -func (b *BrowserContext) SetHTTPCredentials(httpCredentials sobek.Value) error { +func (b *BrowserContext) SetHTTPCredentials(hc Credentials) error { b.logger.Warnf("setHTTPCredentials", "setHTTPCredentials is deprecated."+ " Create a new BrowserContext with httpCredentials instead.") b.logger.Debugf("BrowserContext:SetHTTPCredentials", "bctxid:%v", b.id) - c := NewCredentials() - if err := c.Parse(b.ctx, httpCredentials); err != nil { - return fmt.Errorf("parsing HTTP credentials: %w", err) - } - - b.opts.HttpCredentials = c + b.opts.HTTPCredentials = hc for _, p := range b.browser.getPages() { if err := p.updateHTTPCredentials(); err != nil { return fmt.Errorf("setting HTTP credentials in target ID %s: %w", p.targetID, err) diff --git a/vendor/github.com/grafana/xk6-browser/common/browser_context_options.go b/vendor/github.com/grafana/xk6-browser/common/browser_context_options.go index 9950357276f..2b0729caab2 100644 --- a/vendor/github.com/grafana/xk6-browser/common/browser_context_options.go +++ b/vendor/github.com/grafana/xk6-browser/common/browser_context_options.go @@ -1,65 +1,6 @@ package common -import ( - "context" - "errors" - "fmt" - "time" - - "github.com/grafana/sobek" - - "github.com/grafana/xk6-browser/k6ext" -) - -// Geolocation represents a geolocation. -type Geolocation struct { - Latitude float64 `js:"latitude"` - Longitude float64 `js:"longitude"` - Accurracy float64 `js:"accurracy"` -} - -// NewGeolocation creates a new instance of Geolocation. -func NewGeolocation() *Geolocation { - return &Geolocation{} -} - -// Parse parses the geolocation options. -func (g *Geolocation) Parse(ctx context.Context, opts sobek.Value) error { //nolint:cyclop - rt := k6ext.Runtime(ctx) - longitude := 0.0 - latitude := 0.0 - accuracy := 0.0 - - if opts != nil && !sobek.IsUndefined(opts) && !sobek.IsNull(opts) { - opts := opts.ToObject(rt) - for _, k := range opts.Keys() { - switch k { - case "accuracy": - accuracy = opts.Get(k).ToFloat() - case "latitude": - latitude = opts.Get(k).ToFloat() - case "longitude": - longitude = opts.Get(k).ToFloat() - } - } - } - - if longitude < -180 || longitude > 180 { - return fmt.Errorf(`invalid longitude "%.2f": precondition -180 <= LONGITUDE <= 180 failed`, longitude) - } - if latitude < -90 || latitude > 90 { - return fmt.Errorf(`invalid latitude "%.2f": precondition -90 <= LATITUDE <= 90 failed`, latitude) - } - if accuracy < 0 { - return fmt.Errorf(`invalid accuracy "%.2f": precondition 0 <= ACCURACY failed`, accuracy) - } - - g.Accurracy = accuracy - g.Latitude = latitude - g.Longitude = longitude - - return nil -} +import "fmt" // BrowserContextOptions stores browser context options. type BrowserContextOptions struct { @@ -71,7 +12,7 @@ type BrowserContextOptions struct { ExtraHTTPHeaders map[string]string `js:"extraHTTPHeaders"` Geolocation *Geolocation `js:"geolocation"` HasTouch bool `js:"hasTouch"` - HttpCredentials *Credentials `js:"httpCredentials"` + HTTPCredentials Credentials `js:"httpCredentials"` IgnoreHTTPSErrors bool `js:"ignoreHTTPSErrors"` IsMobile bool `js:"isMobile"` JavaScriptEnabled bool `js:"javaScriptEnabled"` @@ -79,15 +20,15 @@ type BrowserContextOptions struct { Offline bool `js:"offline"` Permissions []string `js:"permissions"` ReducedMotion ReducedMotion `js:"reducedMotion"` - Screen *Screen `js:"screen"` + Screen Screen `js:"screen"` TimezoneID string `js:"timezoneID"` UserAgent string `js:"userAgent"` VideosPath string `js:"videosPath"` - Viewport *Viewport `js:"viewport"` + Viewport Viewport `js:"viewport"` } -// NewBrowserContextOptions creates a default set of browser context options. -func NewBrowserContextOptions() *BrowserContextOptions { +// DefaultBrowserContextOptions returns the default browser context options. +func DefaultBrowserContextOptions() *BrowserContextOptions { return &BrowserContextOptions{ ColorScheme: ColorSchemeLight, DeviceScaleFactor: 1.0, @@ -96,146 +37,29 @@ func NewBrowserContextOptions() *BrowserContextOptions { Locale: DefaultLocale, Permissions: []string{}, ReducedMotion: ReducedMotionNoPreference, - Screen: &Screen{Width: DefaultScreenWidth, Height: DefaultScreenHeight}, - Viewport: &Viewport{Width: DefaultScreenWidth, Height: DefaultScreenHeight}, - } -} - -// Parse parses the browser context options. -func (b *BrowserContextOptions) Parse(ctx context.Context, opts sobek.Value) error { //nolint:cyclop,funlen,gocognit - rt := k6ext.Runtime(ctx) - if !sobekValueExists(opts) { - return nil - } - o := opts.ToObject(rt) - for _, k := range o.Keys() { - switch k { - case "acceptDownloads": - b.AcceptDownloads = o.Get(k).ToBoolean() - case "downloadsPath": - b.DownloadsPath = o.Get(k).String() - case "bypassCSP": - b.BypassCSP = o.Get(k).ToBoolean() - case "colorScheme": - switch ColorScheme(o.Get(k).String()) { //nolint:exhaustive - case "light": - b.ColorScheme = ColorSchemeLight - case "dark": - b.ColorScheme = ColorSchemeDark - default: - b.ColorScheme = ColorSchemeNoPreference - } - case "deviceScaleFactor": - b.DeviceScaleFactor = o.Get(k).ToFloat() - case "extraHTTPHeaders": - headers := o.Get(k).ToObject(rt) - for _, k := range headers.Keys() { - b.ExtraHTTPHeaders[k] = headers.Get(k).String() - } - case "geolocation": - geolocation := NewGeolocation() - if err := geolocation.Parse(ctx, o.Get(k).ToObject(rt)); err != nil { - return err - } - b.Geolocation = geolocation - case "hasTouch": - b.HasTouch = o.Get(k).ToBoolean() - case "httpCredentials": - credentials := NewCredentials() - if err := credentials.Parse(ctx, o.Get(k).ToObject(rt)); err != nil { - return err - } - b.HttpCredentials = credentials - case "ignoreHTTPSErrors": - b.IgnoreHTTPSErrors = o.Get(k).ToBoolean() - case "isMobile": - b.IsMobile = o.Get(k).ToBoolean() - case "javaScriptEnabled": - b.JavaScriptEnabled = o.Get(k).ToBoolean() - case "locale": - b.Locale = o.Get(k).String() - case "offline": - b.Offline = o.Get(k).ToBoolean() - case "permissions": - if ps, ok := o.Get(k).Export().([]any); ok { - for _, p := range ps { - b.Permissions = append(b.Permissions, fmt.Sprintf("%v", p)) - } - } - case "reducedMotion": - switch ReducedMotion(o.Get(k).String()) { //nolint:exhaustive - case "reduce": - b.ReducedMotion = ReducedMotionReduce - default: - b.ReducedMotion = ReducedMotionNoPreference - } - case "screen": - screen := &Screen{} - if err := screen.Parse(ctx, o.Get(k).ToObject(rt)); err != nil { - return err - } - b.Screen = screen - case "timezoneID": - b.TimezoneID = o.Get(k).String() - case "userAgent": - b.UserAgent = o.Get(k).String() - case "viewport": - viewport := &Viewport{} - if err := viewport.Parse(ctx, o.Get(k).ToObject(rt)); err != nil { - return err - } - b.Viewport = viewport - } + Screen: Screen{Width: DefaultScreenWidth, Height: DefaultScreenHeight}, + Viewport: Viewport{Width: DefaultScreenWidth, Height: DefaultScreenHeight}, } - - return nil } -// WaitForEventOptions are the options used by the browserContext.waitForEvent API. -type WaitForEventOptions struct { - Timeout time.Duration - PredicateFn sobek.Callable -} - -// NewWaitForEventOptions created a new instance of WaitForEventOptions with a -// default timeout. -func NewWaitForEventOptions(defaultTimeout time.Duration) *WaitForEventOptions { - return &WaitForEventOptions{ - Timeout: defaultTimeout, - } +// Geolocation represents a geolocation. +type Geolocation struct { + Latitude float64 `js:"latitude"` + Longitude float64 `js:"longitude"` + Accuracy float64 `js:"accuracy"` } -// Parse will parse the options or a callable predicate function. It can parse -// only a callable predicate function or an object which contains a callable -// predicate function and a timeout. -func (w *WaitForEventOptions) Parse(ctx context.Context, optsOrPredicate sobek.Value) error { - if !sobekValueExists(optsOrPredicate) { - return nil +// Validate validates the [Geolocation]. +func (g *Geolocation) Validate() error { + if g.Longitude < -180 || g.Longitude > 180 { + return fmt.Errorf(`invalid longitude "%.2f": precondition -180 <= LONGITUDE <= 180 failed`, g.Longitude) } - - var ( - isCallable bool - rt = k6ext.Runtime(ctx) - ) - - w.PredicateFn, isCallable = sobek.AssertFunction(optsOrPredicate) - if isCallable { - return nil + if g.Latitude < -90 || g.Latitude > 90 { + return fmt.Errorf(`invalid latitude "%.2f": precondition -90 <= LATITUDE <= 90 failed`, g.Latitude) } - - opts := optsOrPredicate.ToObject(rt) - for _, k := range opts.Keys() { - switch k { - case "predicate": - w.PredicateFn, isCallable = sobek.AssertFunction(opts.Get(k)) - if !isCallable { - return errors.New("predicate function is not callable") - } - case "timeout": //nolint:goconst - w.Timeout = time.Duration(opts.Get(k).ToInteger()) * time.Millisecond - } + if g.Accuracy < 0 { + return fmt.Errorf(`invalid accuracy "%.2f": precondition 0 <= ACCURACY failed`, g.Accuracy) } - return nil } @@ -243,23 +67,3 @@ func (w *WaitForEventOptions) Parse(ctx context.Context, optsOrPredicate sobek.V type GrantPermissionsOptions struct { Origin string } - -// NewGrantPermissionsOptions returns a new GrantPermissionsOptions. -func NewGrantPermissionsOptions() *GrantPermissionsOptions { - return &GrantPermissionsOptions{} -} - -// Parse parses the options from opts if opts exists in the sobek runtime. -func (g *GrantPermissionsOptions) Parse(ctx context.Context, opts sobek.Value) { - rt := k6ext.Runtime(ctx) - - if sobekValueExists(opts) { - opts := opts.ToObject(rt) - for _, k := range opts.Keys() { - if k == "origin" { - g.Origin = opts.Get(k).String() - break - } - } - } -} diff --git a/vendor/github.com/grafana/xk6-browser/common/element_handle.go b/vendor/github.com/grafana/xk6-browser/common/element_handle.go index c78f277c11a..e612920e5bf 100644 --- a/vendor/github.com/grafana/xk6-browser/common/element_handle.go +++ b/vendor/github.com/grafana/xk6-browser/common/element_handle.go @@ -464,7 +464,7 @@ func (h *ElementHandle) scrollRectIntoViewIfNeeded(apiCtx context.Context, rect return nil } -func (h *ElementHandle) press(apiCtx context.Context, key string, opts *KeyboardOptions) error { +func (h *ElementHandle) press(apiCtx context.Context, key string, opts KeyboardOptions) error { err := h.focus(apiCtx, true) if err != nil { return err @@ -611,7 +611,7 @@ func (h *ElementHandle) textContent(apiCtx context.Context) (any, error) { return h.eval(apiCtx, opts, js) } -func (h *ElementHandle) typ(apiCtx context.Context, text string, opts *KeyboardOptions) error { +func (h *ElementHandle) typ(apiCtx context.Context, text string, opts KeyboardOptions) error { err := h.focus(apiCtx, true) if err != nil { return err @@ -1085,7 +1085,7 @@ func (h *ElementHandle) Press(key string, opts sobek.Value) error { } press := func(apiCtx context.Context, handle *ElementHandle) (any, error) { - return nil, handle.press(apiCtx, key, NewKeyboardOptions()) + return nil, handle.press(apiCtx, key, KeyboardOptions{}) } pressAction := h.newAction( []string{}, press, false, popts.NoWaitAfter, popts.Timeout, @@ -1457,7 +1457,7 @@ func (h *ElementHandle) Type(text string, opts sobek.Value) error { } typ := func(apiCtx context.Context, handle *ElementHandle) (any, error) { - return nil, handle.typ(apiCtx, text, NewKeyboardOptions()) + return nil, handle.typ(apiCtx, text, KeyboardOptions{}) } typeAction := h.newAction( []string{}, typ, false, popts.NoWaitAfter, popts.Timeout, diff --git a/vendor/github.com/grafana/xk6-browser/common/frame.go b/vendor/github.com/grafana/xk6-browser/common/frame.go index 51e44855fde..7f35757f62b 100644 --- a/vendor/github.com/grafana/xk6-browser/common/frame.go +++ b/vendor/github.com/grafana/xk6-browser/common/frame.go @@ -523,15 +523,16 @@ func (f *Frame) waitForSelectorRetry( return nil, err } +// waitForSelector will wait for the given selector to reach a defined state in +// opts. +// +// It will auto retry on certain errors until the retryCount is below 0. The +// retry workaround is needed since the underlying DOM can change when the +// wait action is performed during a navigation. func (f *Frame) waitForSelector(selector string, opts *FrameWaitForSelectorOptions) (_ *ElementHandle, rerr error) { f.log.Debugf("Frame:waitForSelector", "fid:%s furl:%q sel:%q", f.ID(), f.URL(), selector) - document, err := f.document() - if err != nil { - return nil, err - } - - handle, err := document.waitForSelector(f.ctx, selector, opts) + handle, err := f.waitFor(selector, opts, 20) if err != nil { return nil, err } @@ -564,12 +565,12 @@ func (f *Frame) waitForSelector(selector string, opts *FrameWaitForSelectorOptio return handle, nil } -func (f *Frame) waitFor(selector string, opts *FrameWaitForSelectorOptions, retryCount int) error { +func (f *Frame) waitFor(selector string, opts *FrameWaitForSelectorOptions, retryCount int) (_ *ElementHandle, rerr error) { f.log.Debugf("Frame:waitFor", "fid:%s furl:%q sel:%q", f.ID(), f.URL(), selector) retryCount-- if retryCount < 0 { - return errors.New("waitFor retry threshold reached") + return nil, errors.New("waitFor retry threshold reached") } document, err := f.document() @@ -577,10 +578,10 @@ func (f *Frame) waitFor(selector string, opts *FrameWaitForSelectorOptions, retr if strings.Contains(err.Error(), "Cannot find context with specified id") { return f.waitFor(selector, opts, retryCount) } - return err + return nil, err } - _, err = document.waitForSelector(f.ctx, selector, opts) + handle, err := document.waitForSelector(f.ctx, selector, opts) if err != nil { if strings.Contains(err.Error(), "Inspected target navigated or closed") { return f.waitFor(selector, opts, retryCount) @@ -593,7 +594,7 @@ func (f *Frame) waitFor(selector string, opts *FrameWaitForSelectorOptions, retr } } - return err + return handle, err } // ChildFrames returns a list of child frames. @@ -1737,15 +1738,20 @@ func (f *Frame) Timeout() time.Duration { } // Title returns the title of the frame. -func (f *Frame) Title() string { +func (f *Frame) Title() (string, error) { f.log.Debugf("Frame:Title", "fid:%s furl:%q", f.ID(), f.URL()) - script := `() => document.title` - - // TODO: return error + js := `() => document.title` + v, err := f.Evaluate(js) + if err != nil { + return "", fmt.Errorf("getting frame title: %w", err) + } + s, ok := v.(string) + if !ok { + return "", fmt.Errorf("getting frame title: expected string, got %T", v) + } - v, _ := f.Evaluate(script) - return v.(string) //nolint:forcetypeassert + return s, nil } // Type text on the first element found matches the selector. diff --git a/vendor/github.com/grafana/xk6-browser/common/frame_manager.go b/vendor/github.com/grafana/xk6-browser/common/frame_manager.go index 617f20f9eb0..0a675b6664a 100644 --- a/vendor/github.com/grafana/xk6-browser/common/frame_manager.go +++ b/vendor/github.com/grafana/xk6-browser/common/frame_manager.go @@ -245,8 +245,20 @@ func (m *FrameManager) frameNavigated(frameID cdp.FrameID, parentFrameID cdp.Fra isMainFrame := parentFrameID == "" frame := m.frames[frameID] - if !(isMainFrame || frame != nil) { - return errors.New("we either navigate top level or have old version of the navigated frame") + if !isMainFrame && frame == nil { + m.logger.Debugf("FrameManager:frameNavigated:nil frame", + "fmid:%d fid:%v pfid:%v docid:%s fname:%s furl:%s initial:%t", + m.ID(), frameID, parentFrameID, documentID, name, url, initial) + + // If the frame is nil at this point, then the cause of this is likely + // due to chrome not sending a frameAttached event ahead of time. This + // isn't a bug in chrome, and seems to be intended behavior. Instead + // of worrying about the nil frame and causing the test to fail when + // the frame is nil, we can instead return early. The frame will + // be initialized when getFrameTree CDP request is made, which will + // call onFrameAttached and onFrameNavigated. + + return nil } m.logger.Debugf("FrameManager:frameNavigated:removeFrames", diff --git a/vendor/github.com/grafana/xk6-browser/common/frame_options.go b/vendor/github.com/grafana/xk6-browser/common/frame_options.go index ccf4df93e0c..f0e4dced802 100644 --- a/vendor/github.com/grafana/xk6-browser/common/frame_options.go +++ b/vendor/github.com/grafana/xk6-browser/common/frame_options.go @@ -459,12 +459,14 @@ func NewFramePressOptions(defaultTimeout time.Duration) *FramePressOptions { } } -func (o *FramePressOptions) ToKeyboardOptions() *KeyboardOptions { - o2 := NewKeyboardOptions() +// ToKeyboardOptions converts FramePressOptions to KeyboardOptions. +func (o *FramePressOptions) ToKeyboardOptions() KeyboardOptions { + var o2 KeyboardOptions o2.Delay = o.Delay return o2 } +// NewFrameSelectOptionOptions creates and returns a new instance of FrameSelectOptionOptions. func NewFrameSelectOptionOptions(defaultTimeout time.Duration) *FrameSelectOptionOptions { return &FrameSelectOptionOptions{ ElementHandleBaseOptions: *NewElementHandleBaseOptions(defaultTimeout), @@ -579,8 +581,9 @@ func NewFrameTypeOptions(defaultTimeout time.Duration) *FrameTypeOptions { } } -func (o *FrameTypeOptions) ToKeyboardOptions() *KeyboardOptions { - o2 := NewKeyboardOptions() +// ToKeyboardOptions converts FrameTypeOptions to KeyboardOptions. +func (o *FrameTypeOptions) ToKeyboardOptions() KeyboardOptions { + var o2 KeyboardOptions o2.Delay = o.Delay return o2 } diff --git a/vendor/github.com/grafana/xk6-browser/common/frame_session.go b/vendor/github.com/grafana/xk6-browser/common/frame_session.go index 7ee63ce7283..fbdd85903d6 100644 --- a/vendor/github.com/grafana/xk6-browser/common/frame_session.go +++ b/vendor/github.com/grafana/xk6-browser/common/frame_session.go @@ -1136,7 +1136,7 @@ func (fs *FrameSession) updateGeolocation(initial bool) error { action := emulation.SetGeolocationOverride(). WithLatitude(geolocation.Latitude). WithLongitude(geolocation.Longitude). - WithAccuracy(geolocation.Accurracy) + WithAccuracy(geolocation.Accuracy) if err := action.Do(cdp.WithExecutor(fs.ctx, fs.session)); err != nil { return fmt.Errorf("%w", err) } @@ -1148,8 +1148,8 @@ func (fs *FrameSession) updateGeolocation(initial bool) error { func (fs *FrameSession) updateHTTPCredentials(initial bool) error { fs.logger.Debugf("NewFrameSession:updateHttpCredentials", "sid:%v tid:%v", fs.session.ID(), fs.targetID) - credentials := fs.page.browserCtx.opts.HttpCredentials - if !initial || credentials != nil { + credentials := fs.page.browserCtx.opts.HTTPCredentials + if !initial || !credentials.IsEmpty() { return fs.networkManager.Authenticate(credentials) } @@ -1232,7 +1232,7 @@ func (fs *FrameSession) updateViewport() error { if fs.hasUIWindow { // add an inset to viewport depending on the operating system. // this won't add an inset if we're running in headless mode. - viewport.calculateInset( + viewport = viewport.recalculateInset( fs.page.browserCtx.browser.browserOpts.Headless, runtime.GOOS, ) diff --git a/vendor/github.com/grafana/xk6-browser/common/keyboard.go b/vendor/github.com/grafana/xk6-browser/common/keyboard.go index 8a26c182a56..255e03001ae 100644 --- a/vendor/github.com/grafana/xk6-browser/common/keyboard.go +++ b/vendor/github.com/grafana/xk6-browser/common/keyboard.go @@ -20,6 +20,11 @@ const ( ModifierKeyShift ) +// KeyboardOptions represents the options for the keyboard. +type KeyboardOptions struct { + Delay int64 `json:"delay"` +} + // Keyboard represents a keyboard input device. // Each Page has a publicly accessible Keyboard. type Keyboard struct { @@ -62,7 +67,7 @@ func (k *Keyboard) Up(key string) error { // Press sends a key press message to a session target. // It delays the action if `Delay` option is specified. // A press message consists of successive key down and up messages. -func (k *Keyboard) Press(key string, kbdOpts *KeyboardOptions) error { +func (k *Keyboard) Press(key string, kbdOpts KeyboardOptions) error { if err := k.comboPress(key, kbdOpts); err != nil { return fmt.Errorf("pressing key: %w", err) } @@ -83,7 +88,7 @@ func (k *Keyboard) InsertText(text string) error { // // It sends an insertText message if a character is not among // valid characters in the keyboard's layout. -func (k *Keyboard) Type(text string, kbdOpts *KeyboardOptions) error { +func (k *Keyboard) Type(text string, kbdOpts KeyboardOptions) error { if err := k.typ(text, kbdOpts); err != nil { return fmt.Errorf("typing text: %w", err) } @@ -245,7 +250,7 @@ func (k *Keyboard) platformSpecificResolution(key string) string { return key } -func (k *Keyboard) comboPress(keys string, opts *KeyboardOptions) error { +func (k *Keyboard) comboPress(keys string, opts KeyboardOptions) error { if opts.Delay > 0 { if err := wait(k.ctx, opts.Delay); err != nil { return err @@ -291,7 +296,7 @@ func split(keys string) []string { return kk } -func (k *Keyboard) press(key string, opts *KeyboardOptions) error { +func (k *Keyboard) press(key string, opts KeyboardOptions) error { if opts.Delay > 0 { if err := wait(k.ctx, opts.Delay); err != nil { return err @@ -303,7 +308,7 @@ func (k *Keyboard) press(key string, opts *KeyboardOptions) error { return k.up(key) } -func (k *Keyboard) typ(text string, opts *KeyboardOptions) error { +func (k *Keyboard) typ(text string, opts KeyboardOptions) error { layout := keyboardlayout.GetKeyboardLayout(k.layoutName) for _, c := range text { if opts.Delay > 0 { diff --git a/vendor/github.com/grafana/xk6-browser/common/keyboard_options.go b/vendor/github.com/grafana/xk6-browser/common/keyboard_options.go deleted file mode 100644 index c10376ac535..00000000000 --- a/vendor/github.com/grafana/xk6-browser/common/keyboard_options.go +++ /dev/null @@ -1,34 +0,0 @@ -package common - -import ( - "context" - - "github.com/grafana/sobek" - - "github.com/grafana/xk6-browser/k6ext" -) - -type KeyboardOptions struct { - Delay int64 `json:"delay"` -} - -func NewKeyboardOptions() *KeyboardOptions { - return &KeyboardOptions{ - Delay: 0, - } -} - -// Parse parses the keyboard options. -func (o *KeyboardOptions) Parse(ctx context.Context, opts sobek.Value) error { - rt := k6ext.Runtime(ctx) - if opts != nil && !sobek.IsUndefined(opts) && !sobek.IsNull(opts) { - opts := opts.ToObject(rt) - for _, k := range opts.Keys() { - switch k { - case "delay": - o.Delay = opts.Get(k).ToInteger() - } - } - } - return nil -} diff --git a/vendor/github.com/grafana/xk6-browser/common/layout.go b/vendor/github.com/grafana/xk6-browser/common/layout.go index afc8ad96476..5c01525898a 100644 --- a/vendor/github.com/grafana/xk6-browser/common/layout.go +++ b/vendor/github.com/grafana/xk6-browser/common/layout.go @@ -80,34 +80,21 @@ type Viewport struct { Height int64 `js:"height"` } -// Parse viewport details from a given sobek viewport value. -func (v *Viewport) Parse(ctx context.Context, viewport sobek.Value) error { - rt := k6ext.Runtime(ctx) - if viewport != nil && !sobek.IsUndefined(viewport) && !sobek.IsNull(viewport) { - viewport := viewport.ToObject(rt) - for _, k := range viewport.Keys() { - switch k { - case "width": - v.Width = viewport.Get(k).ToInteger() - case "height": - v.Height = viewport.Get(k).ToInteger() - } - } - } - - return nil +// IsEmpty returns true if the viewport is empty. +func (v Viewport) IsEmpty() bool { + return v.Width == 0 && v.Height == 0 } func (v Viewport) String() string { return fmt.Sprintf("%dx%d", v.Width, v.Height) } -// calculateInset depending on a given operating system and, -// add the calculated inset width and height to Viewport. -// It won't update the Viewport if headless is true. -func (v *Viewport) calculateInset(headless bool, os string) { +// recalculateInset is used to calculate the inset width and height +// depending on the operating system and add it to the given v, and +// return a new Viewport. It returns the same Viewport if headless is true. +func (v Viewport) recalculateInset(headless bool, os string) Viewport { if headless { - return + return v } // TODO: popup windows have their own insets. var inset Viewport @@ -123,6 +110,9 @@ func (v *Viewport) calculateInset(headless bool, os string) { // on my Mac and w:0 h:79 works best. inset = Viewport{Width: 0, Height: 79} } - v.Width += inset.Width - v.Height += inset.Height + + return Viewport{ + Width: v.Width + inset.Width, + Height: v.Height + inset.Height, + } } diff --git a/vendor/github.com/grafana/xk6-browser/common/locator.go b/vendor/github.com/grafana/xk6-browser/common/locator.go index 0bc69cc3b4a..01e193b2cb3 100644 --- a/vendor/github.com/grafana/xk6-browser/common/locator.go +++ b/vendor/github.com/grafana/xk6-browser/common/locator.go @@ -621,7 +621,8 @@ func (l *Locator) WaitFor(opts sobek.Value) error { func (l *Locator) waitFor(opts *FrameWaitForSelectorOptions) error { opts.Strict = true - return l.frame.waitFor(l.selector, opts, 20) + _, err := l.frame.waitFor(l.selector, opts, 20) + return err } // DefaultTimeout returns the default timeout for the locator. diff --git a/vendor/github.com/grafana/xk6-browser/common/network_manager.go b/vendor/github.com/grafana/xk6-browser/common/network_manager.go index e550f8fc317..d796804a257 100644 --- a/vendor/github.com/grafana/xk6-browser/common/network_manager.go +++ b/vendor/github.com/grafana/xk6-browser/common/network_manager.go @@ -7,6 +7,7 @@ import ( "net" "net/url" "strconv" + "strings" "sync" "time" @@ -25,7 +26,6 @@ import ( "github.com/chromedp/cdproto/emulation" "github.com/chromedp/cdproto/fetch" "github.com/chromedp/cdproto/network" - "github.com/grafana/sobek" ) // Credentials holds HTTP authentication credentials. @@ -34,27 +34,13 @@ type Credentials struct { Password string `js:"password"` } -// NewCredentials return a new Credentials. -func NewCredentials() *Credentials { - return &Credentials{} -} - -// Parse credentials details from a given sobek credentials value. -func (c *Credentials) Parse(ctx context.Context, credentials sobek.Value) error { - rt := k6ext.Runtime(ctx) - if credentials != nil && !sobek.IsUndefined(credentials) && !sobek.IsNull(credentials) { - credentials := credentials.ToObject(rt) - for _, k := range credentials.Keys() { - switch k { - case "username": - c.Username = credentials.Get(k).String() - case "password": - c.Password = credentials.Get(k).String() - } - } +// IsEmpty returns true if the credentials are empty. +func (c Credentials) IsEmpty() bool { + c = Credentials{ + Username: strings.TrimSpace(c.Username), + Password: strings.TrimSpace(c.Password), } - - return nil + return c == (Credentials{}) } type metricInterceptor interface { @@ -70,7 +56,7 @@ type NetworkManager struct { session session parent *NetworkManager frameManager *FrameManager - credentials *Credentials + credentials Credentials resolver k6netext.Resolver vu k6modules.VU customMetrics *k6ext.CustomMetrics @@ -627,7 +613,7 @@ func (m *NetworkManager) onAuthRequired(event *fetch.EventAuthRequired) { case m.attemptedAuth[rid]: delete(m.attemptedAuth, rid) res = fetch.AuthChallengeResponseResponseCancelAuth - case m.credentials != nil: + case !m.credentials.IsEmpty(): // TODO: remove requests from attemptedAuth when: // - request is redirected // - loading finished @@ -732,9 +718,9 @@ func (m *NetworkManager) updateProtocolRequestInterception() error { } // Authenticate sets HTTP authentication credentials to use. -func (m *NetworkManager) Authenticate(credentials *Credentials) error { +func (m *NetworkManager) Authenticate(credentials Credentials) error { m.credentials = credentials - if credentials != nil { + if !credentials.IsEmpty() { m.userReqInterceptionEnabled = true } if err := m.updateProtocolRequestInterception(); err != nil { @@ -744,12 +730,6 @@ func (m *NetworkManager) Authenticate(credentials *Credentials) error { return nil } -// ExtraHTTPHeaders returns the currently set extra HTTP request headers. -func (m *NetworkManager) ExtraHTTPHeaders() sobek.Value { - rt := m.vu.Runtime() - return rt.ToValue(m.extraHTTPHeaders) -} - // SetExtraHTTPHeaders sets extra HTTP request headers to be sent with every request. func (m *NetworkManager) SetExtraHTTPHeaders(headers network.Headers) error { err := network. diff --git a/vendor/github.com/grafana/xk6-browser/common/page.go b/vendor/github.com/grafana/xk6-browser/common/page.go index 9fea1cd292a..d21ac0819d4 100644 --- a/vendor/github.com/grafana/xk6-browser/common/page.go +++ b/vendor/github.com/grafana/xk6-browser/common/page.go @@ -106,29 +106,6 @@ type Screen struct { Height int64 `js:"height"` } -const ( - screenWidth = "width" - screenHeight = "height" -) - -// Parse parses the given screen options. -func (s *Screen) Parse(ctx context.Context, screen sobek.Value) error { - rt := k6ext.Runtime(ctx) - if screen != nil && !sobek.IsUndefined(screen) && !sobek.IsNull(screen) { - screen := screen.ToObject(rt) - for _, k := range screen.Keys() { - switch k { - case screenWidth: - s.Width = screen.Get(k).ToInteger() - case screenHeight: - s.Height = screen.Get(k).ToInteger() - } - } - } - - return nil -} - // ColorScheme represents a browser color scheme. type ColorScheme string @@ -177,12 +154,12 @@ func (c *ColorScheme) UnmarshalJSON(b []byte) error { // EmulatedSize represents the emulated viewport and screen sizes. type EmulatedSize struct { - Viewport *Viewport - Screen *Screen + Viewport Viewport + Screen Screen } // NewEmulatedSize creates and returns a new EmulatedSize. -func NewEmulatedSize(viewport *Viewport, screen *Screen) *EmulatedSize { +func NewEmulatedSize(viewport Viewport, screen Screen) *EmulatedSize { return &EmulatedSize{ Viewport: viewport, Screen: screen, @@ -298,7 +275,7 @@ func NewPage( // We need to init viewport and screen size before initializing the main frame session, // as that's where the emulation is activated. - if bctx.opts.Viewport != nil { + if !bctx.opts.Viewport.IsEmpty() { p.emulatedSize = NewEmulatedSize(bctx.opts.Viewport, bctx.opts.Screen) } @@ -739,11 +716,11 @@ func (p *Page) setViewportSize(viewportSize *Size) error { p.logger.Debugf("Page:setViewportSize", "sid:%v vps:%v", p.sessionID(), viewportSize) - viewport := &Viewport{ + viewport := Viewport{ Width: int64(viewportSize.Width), Height: int64(viewportSize.Height), } - screen := &Screen{ + screen := Screen{ Width: int64(viewportSize.Width), Height: int64(viewportSize.Height), } diff --git a/vendor/modules.txt b/vendor/modules.txt index 0d0914a16c9..cf332cff4a8 100644 --- a/vendor/modules.txt +++ b/vendor/modules.txt @@ -179,7 +179,7 @@ github.com/grafana/sobek/ftoa/internal/fast github.com/grafana/sobek/parser github.com/grafana/sobek/token github.com/grafana/sobek/unistring -# github.com/grafana/xk6-browser v1.9.1 +# github.com/grafana/xk6-browser v1.9.2-0.20241118153201-d741e4540a3a ## explicit; go 1.21 github.com/grafana/xk6-browser/browser github.com/grafana/xk6-browser/chromium