diff --git a/browser/browser_context_options_test.go b/browser/browser_context_options_test.go new file mode 100644 index 000000000..af815f4cd --- /dev/null +++ b/browser/browser_context_options_test.go @@ -0,0 +1,42 @@ +package browser + +import ( + "testing" + + "github.com/stretchr/testify/assert" + + "github.com/grafana/xk6-browser/common" + "github.com/grafana/xk6-browser/k6ext/k6test" +) + +func TestBrowserContextOptionsPermissions(t *testing.T) { + vu := k6test.NewVU(t) + + opts, err := parseBrowserContextOptions(vu.Context(), vu.ToSobekValue((struct { + Permissions []any `js:"permissions"` + }{ + Permissions: []any{"camera", "microphone"}, + }))) + assert.NoError(t, err) + assert.Len(t, opts.Permissions, 2) + assert.Equal(t, opts.Permissions, []string{"camera", "microphone"}) +} + +func TestBrowserContextSetGeolocation(t *testing.T) { + vu := k6test.NewVU(t) + + opts, err := parseBrowserContextOptions(vu.Context(), vu.ToSobekValue((struct { + GeoLocation *common.Geolocation `js:"geolocation"` + }{ + GeoLocation: &common.Geolocation{ + Latitude: 1.0, + Longitude: 2.0, + Accuracy: 3.0, + }, + }))) + assert.NoError(t, err) + assert.NotNil(t, opts) + assert.Equal(t, 1.0, opts.Geolocation.Latitude) + assert.Equal(t, 2.0, opts.Geolocation.Longitude) + assert.Equal(t, 3.0, opts.Geolocation.Accuracy) +} diff --git a/browser/browser_mapping.go b/browser/browser_mapping.go index 19569c09c..d31e38d9a 100644 --- a/browser/browser_mapping.go +++ b/browser/browser_mapping.go @@ -1,6 +1,7 @@ package browser import ( + "context" "fmt" "github.com/grafana/sobek" @@ -36,8 +37,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.Context(), opts) + if err != nil { return nil, fmt.Errorf("parsing browser.newContext options: %w", err) } return k6ext.Promise(vu.Context(), func() (any, error) { @@ -71,8 +72,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.Context(), opts) + if err != nil { return nil, fmt.Errorf("parsing browser.newPage options: %w", err) } return k6ext.Promise(vu.Context(), func() (any, error) { @@ -107,3 +108,96 @@ func initBrowserContext(bctx *common.BrowserContext, testRunID string) error { return nil } + +// parseBrowserContextOptions parses the [common.BrowserContext] options from a Sobek value. +func parseBrowserContextOptions(ctx context.Context, opts sobek.Value) (*common.BrowserContextOptions, error) { //nolint:cyclop,funlen,gocognit,lll + if !sobekValueExists(opts) { + return nil, nil //nolint:nilnil + } + + b := common.NewBrowserContextOptions() + + rt := k6ext.Runtime(ctx) + 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 common.ColorScheme(o.Get(k).String()) { //nolint:exhaustive + case "light": + b.ColorScheme = common.ColorSchemeLight + case "dark": + b.ColorScheme = common.ColorSchemeDark + default: + b.ColorScheme = common.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 := common.NewGeolocation() + if err := geolocation.Parse(ctx, o.Get(k).ToObject(rt)); err != nil { + return nil, fmt.Errorf("parsing geolocation options: %w", err) + } + b.Geolocation = geolocation + case "hasTouch": + b.HasTouch = o.Get(k).ToBoolean() + case "httpCredentials": + credentials := common.NewCredentials() + if err := credentials.Parse(ctx, o.Get(k).ToObject(rt)); err != nil { + return nil, fmt.Errorf("parsing HTTP credential options: %w", 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 common.ReducedMotion(o.Get(k).String()) { //nolint:exhaustive + case "reduce": + b.ReducedMotion = common.ReducedMotionReduce + default: + b.ReducedMotion = common.ReducedMotionNoPreference + } + case "screen": + var screen common.Screen + if err := screen.Parse(ctx, o.Get(k).ToObject(rt)); err != nil { + return nil, fmt.Errorf("parsing screen options: %w", err) + } + b.Screen = &screen + case "timezoneID": + b.TimezoneID = o.Get(k).String() + case "userAgent": + b.UserAgent = o.Get(k).String() + case "viewport": + var viewport common.Viewport + if err := viewport.Parse(ctx, o.Get(k).ToObject(rt)); err != nil { + return nil, fmt.Errorf("parsing viewport options: %w", err) + } + b.Viewport = &viewport + } + } + + return b, nil +} diff --git a/browser/sync_browser_mapping.go b/browser/sync_browser_mapping.go index 689e610a1..289aaae6a 100644 --- a/browser/sync_browser_mapping.go +++ b/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.Context(), 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.Context(), opts) + if err != nil { + return nil, fmt.Errorf("parsing browser.newPage options: %w", err) } b, err := vu.browser() diff --git a/common/browser_context_options.go b/common/browser_context_options.go index 34e53cd60..26ba16429 100644 --- a/common/browser_context_options.go +++ b/common/browser_context_options.go @@ -98,96 +98,6 @@ func NewBrowserContextOptions() *BrowserContextOptions { } } -// 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 - } - } - - return nil -} - // WaitForEventOptions are the options used by the browserContext.waitForEvent API. type WaitForEventOptions struct { Timeout time.Duration diff --git a/common/browser_context_options_test.go b/common/browser_context_options_test.go deleted file mode 100644 index afc25aab8..000000000 --- a/common/browser_context_options_test.go +++ /dev/null @@ -1,23 +0,0 @@ -package common - -import ( - "testing" - - "github.com/grafana/xk6-browser/k6ext/k6test" - - "github.com/stretchr/testify/assert" -) - -func TestBrowserContextOptionsPermissions(t *testing.T) { - vu := k6test.NewVU(t) - - var opts BrowserContextOptions - err := opts.Parse(vu.Context(), vu.ToSobekValue((struct { - Permissions []any `js:"permissions"` - }{ - Permissions: []any{"camera", "microphone"}, - }))) - assert.NoError(t, err) - assert.Len(t, opts.Permissions, 2) - assert.Equal(t, opts.Permissions, []string{"camera", "microphone"}) -} diff --git a/common/browser_context_test.go b/common/browser_context_test.go index c31424192..7203b191c 100644 --- a/common/browser_context_test.go +++ b/common/browser_context_test.go @@ -325,23 +325,3 @@ func TestFilterCookies(t *testing.T) { }) } } - -func TestBrowserContextSetGeolocation(t *testing.T) { - vu := k6test.NewVU(t) - - var opts BrowserContextOptions - err := opts.Parse(vu.Context(), vu.ToSobekValue((struct { - GeoLocation *Geolocation `js:"geolocation"` - }{ - GeoLocation: &Geolocation{ - Latitude: 1.0, - Longitude: 2.0, - Accuracy: 3.0, - }, - }))) - assert.NoError(t, err) - assert.NotNil(t, opts) - assert.Equal(t, 1.0, opts.Geolocation.Latitude) - assert.Equal(t, 2.0, opts.Geolocation.Longitude) - assert.Equal(t, 3.0, opts.Geolocation.Accuracy) -}