Skip to content
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

Move out the parsing of BrowserContextOptions and child options #1390

Draft
wants to merge 9 commits into
base: main
Choose a base branch
from
Draft
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
23 changes: 17 additions & 6 deletions browser/browser_context_mapping.go
Original file line number Diff line number Diff line change
Expand Up @@ -83,15 +83,26 @@ func mapBrowserContext(vu moduleVU, bc *common.BrowserContext) mapping { //nolin
},
"setDefaultNavigationTimeout": bc.SetDefaultNavigationTimeout,
"setDefaultTimeout": bc.SetDefaultTimeout,
"setGeolocation": func(geolocation sobek.Value) *sobek.Promise {
"setGeolocation": func(geolocation sobek.Value) (*sobek.Promise, error) {
geoloc, err := ParseGeolocation(rt, geolocation)
if err != nil {
return nil, fmt.Errorf("parsing geo location: %w", err)
}
if err := geoloc.Validate(); err != nil {
return nil, fmt.Errorf("validating geo location: %w", err)
}
return k6ext.Promise(vu.Context(), func() (any, error) {
return nil, bc.SetGeolocation(geolocation) //nolint:wrapcheck
})
return nil, bc.SetGeolocation(geoloc) //nolint:wrapcheck
}), nil
},
"setHTTPCredentials": func(httpCredentials sobek.Value) *sobek.Promise {
"setHTTPCredentials": func(httpCredentials sobek.Value) (*sobek.Promise, error) {
creds, err := ParseCredentials(rt, httpCredentials)
if err != nil {
return nil, fmt.Errorf("parsing httpCredentials options: %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,wrapcheck
}), nil
},
"setOffline": func(offline bool) *sobek.Promise {
return k6ext.Promise(vu.Context(), func() (any, error) {
Expand Down
193 changes: 193 additions & 0 deletions browser/browser_context_options.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,193 @@
package browser

import (
"fmt"

"github.com/grafana/sobek"

"github.com/grafana/xk6-browser/common"
)

const (
optionWidth = "width"
optionHeight = "height"
)

// ParseBrowserContextOptions parses the browser context options.
func ParseBrowserContextOptions( //nolint:funlen,gocognit,cyclop
rt *sobek.Runtime,
opts sobek.Value,
) (*common.BrowserContextOptions, error) {
popts := common.NewBrowserContextOptions()

if !sobekValueExists(opts) {
return popts, nil // return the default options
}

o := opts.ToObject(rt)
for _, k := range o.Keys() {
switch k {
case "acceptDownloads":
popts.AcceptDownloads = o.Get(k).ToBoolean()
case "bypassCSP":
popts.BypassCSP = o.Get(k).ToBoolean()
case "colorScheme":
switch common.ColorScheme(o.Get(k).String()) { //nolint:exhaustive
case "light":
popts.ColorScheme = common.ColorSchemeLight
case "dark":
popts.ColorScheme = common.ColorSchemeDark
default:
popts.ColorScheme = common.ColorSchemeNoPreference
}
case "deviceScaleFactor":
popts.DeviceScaleFactor = o.Get(k).ToFloat()
case "extraHTTPHeaders":
headers := o.Get(k).ToObject(rt)
for _, k := range headers.Keys() {
popts.ExtraHTTPHeaders[k] = headers.Get(k).String()
}
case "geolocation":
geoloc, err := ParseGeolocation(rt, o.Get(k).ToObject(rt))
if err != nil {
return nil, fmt.Errorf("parsing geolocation options: %w", err)
}
popts.Geolocation = geoloc
case "hasTouch":
popts.HasTouch = o.Get(k).ToBoolean()
case "httpCredentials":
creds, err := ParseCredentials(rt, o.Get(k).ToObject(rt))
if err != nil {
return nil, fmt.Errorf("parsing httpCredentials options: %w", err)
}
popts.HttpCredentials = creds
case "ignoreHTTPSErrors":
popts.IgnoreHTTPSErrors = o.Get(k).ToBoolean()
case "isMobile":
popts.IsMobile = o.Get(k).ToBoolean()
case "javaScriptEnabled":
popts.JavaScriptEnabled = o.Get(k).ToBoolean()
case "locale":
popts.Locale = o.Get(k).String()
case "offline":
popts.Offline = o.Get(k).ToBoolean()
case "permissions":
if ps, ok := o.Get(k).Export().([]any); ok {
for _, p := range ps {
popts.Permissions = append(popts.Permissions, fmt.Sprintf("%v", p))
}
}
case "reducedMotion":
switch common.ReducedMotion(o.Get(k).String()) { //nolint:exhaustive
case "reduce":
popts.ReducedMotion = common.ReducedMotionReduce
default:
popts.ReducedMotion = common.ReducedMotionNoPreference
}
case "screen":
screen, err := ParseScreen(rt, o.Get(k).ToObject(rt))
if err != nil {
return nil, fmt.Errorf("parsing screen options: %w", err)
}
popts.Screen = screen
case "timezoneID":
popts.TimezoneID = o.Get(k).String()
case "userAgent":
popts.UserAgent = o.Get(k).String()
case "viewport":
vp, err := ParseViewport(rt, o.Get(k).ToObject(rt))
if err != nil {
return nil, fmt.Errorf("parsing viewport options: %w", err)
}
popts.Viewport = vp
}
}

return popts, nil
}

// ParseGeolocation parses the geolocation.
func ParseGeolocation(rt *sobek.Runtime, opts sobek.Value) (*common.Geolocation, error) {
var geoloc common.Geolocation

if !sobekValueExists(opts) {
return &geoloc, nil // return the default options
}

o := opts.ToObject(rt)
for _, k := range o.Keys() {
switch k {
case "accuracy":
geoloc.Accurracy = o.Get(k).ToFloat()
case "latitude":
geoloc.Latitude = o.Get(k).ToFloat()
case "longitude":
geoloc.Longitude = o.Get(k).ToFloat()
}
}

return &geoloc, nil
}

// ParseCredentials parses the credentials.
func ParseCredentials(rt *sobek.Runtime, opts sobek.Value) (*common.Credentials, error) {
var creds common.Credentials

if !sobekValueExists(opts) {
return &creds, nil // return the default options
}

o := opts.ToObject(rt)
for _, k := range o.Keys() {
switch k {
case "username":
creds.Username = o.Get(k).String()
case "password":
creds.Password = o.Get(k).String()
}
}

return &creds, nil
}

// ParseScreen parses the screen options.
func ParseScreen(rt *sobek.Runtime, opts sobek.Value) (*common.Screen, error) {
var screen common.Screen

if !sobekValueExists(opts) {
return &screen, nil // return the default options
}

o := opts.ToObject(rt)
for _, k := range o.Keys() {
switch k {
case optionWidth:
screen.Width = o.Get(k).ToInteger()
case optionHeight:
screen.Height = o.Get(k).ToInteger()
}
}

return &screen, nil
}

// ParseViewport parses the viewport options.
func ParseViewport(rt *sobek.Runtime, opts sobek.Value) (*common.Viewport, error) {
var viewport common.Viewport

if !sobekValueExists(opts) {
return &viewport, nil // return the default options
}

o := opts.ToObject(rt)
for _, k := range o.Keys() {
switch k {
case optionWidth:
viewport.Width = o.Get(k).ToInteger()
case optionHeight:
viewport.Height = o.Get(k).ToInteger()
}
}

return &viewport, nil
}
Original file line number Diff line number Diff line change
@@ -1,22 +1,23 @@
package common
package browser

import (
"testing"

"github.com/grafana/xk6-browser/k6ext/k6test"

"github.com/stretchr/testify/assert"

"github.com/grafana/xk6-browser/k6ext/k6test"
)

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"},
})))
opts, err := ParseBrowserContextOptions(
vu.Runtime(),
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"})
Expand Down
30 changes: 20 additions & 10 deletions browser/browser_mapping.go
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ import (
)

// mapBrowser to the JS module.
func mapBrowser(vu moduleVU) mapping { //nolint:funlen,cyclop
func mapBrowser(vu moduleVU) mapping { //nolint:funlen,cyclop,gocognit
return mapping{
"context": func() (mapping, error) {
b, err := vu.browser()
Expand All @@ -36,23 +36,27 @@ func mapBrowser(vu moduleVU) mapping { //nolint:funlen,cyclop
return b.IsConnected(), nil
},
"newContext": func(opts sobek.Value) (*sobek.Promise, error) {
popts, err := ParseBrowserContextOptions(vu.Runtime(), opts)
if err != nil {
return nil, fmt.Errorf("parsing browser.newContext options: %w", err)
}
if err := popts.Validate(); err != nil {
return nil, fmt.Errorf("validating browser.newContext options: %w", err)
}
return k6ext.Promise(vu.Context(), func() (any, error) {
b, err := vu.browser()
if err != nil {
return nil, err
}
bctx, err := b.NewContext(opts)
bctx, err := b.NewContext(popts)
if err != nil {
return nil, err //nolint:wrapcheck
}

if err := initBrowserContext(bctx, vu.testRunID); err != nil {
return nil, err
}

m := mapBrowserContext(vu, bctx)

return m, nil
return mapBrowserContext(vu, bctx), nil
}), nil
},
"userAgent": func() (string, error) {
Expand All @@ -69,23 +73,29 @@ func mapBrowser(vu moduleVU) mapping { //nolint:funlen,cyclop
}
return b.Version(), nil
},
"newPage": func(opts sobek.Value) *sobek.Promise {
"newPage": func(opts sobek.Value) (*sobek.Promise, error) {
popts, err := ParseBrowserContextOptions(vu.Runtime(), opts)
if err != nil {
return nil, fmt.Errorf("parsing browser.newPage options: %w", err)
}
if err := popts.Validate(); err != nil {
return nil, fmt.Errorf("validating browser.newPage options: %w", err)
}
return k6ext.Promise(vu.Context(), func() (any, error) {
b, err := vu.browser()
if err != nil {
return nil, err
}
page, err := b.NewPage(opts)
page, err := b.NewPage(popts)
if err != nil {
return nil, err //nolint:wrapcheck
}

if err := initBrowserContext(b.Context(), vu.testRunID); err != nil {
return nil, err
}

return mapPage(vu, page), nil
})
}), nil
},
}
}
Expand Down
4 changes: 2 additions & 2 deletions browser/mapping_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -264,8 +264,8 @@ type browserAPI interface {
Context() *common.BrowserContext
CloseContext()
IsConnected() bool
NewContext(opts sobek.Value) (*common.BrowserContext, error)
NewPage(opts sobek.Value) (*common.Page, error)
NewContext(opts *common.BrowserContextOptions) (*common.BrowserContext, error)
NewPage(opts *common.BrowserContextOptions) (*common.Page, error)
On(string) (bool, error)
UserAgent() string
Version() string
Expand Down
10 changes: 8 additions & 2 deletions browser/sync_browser_context_mapping.go
Original file line number Diff line number Diff line change
Expand Up @@ -57,8 +57,14 @@ func syncMapBrowserContext(vu moduleVU, bc *common.BrowserContext) mapping { //n
"setDefaultNavigationTimeout": bc.SetDefaultNavigationTimeout,
"setDefaultTimeout": bc.SetDefaultTimeout,
"setGeolocation": bc.SetGeolocation,
"setHTTPCredentials": bc.SetHTTPCredentials, //nolint:staticcheck
"setOffline": bc.SetOffline,
"setHTTPCredentials": func(httpCredentials sobek.Value) error {
creds, err := ParseCredentials(rt, httpCredentials)
if err != nil {
return fmt.Errorf("parsing httpCredentials options: %w", err)
}
return bc.SetHTTPCredentials(creds) //nolint:wrapcheck,staticcheck
},
"setOffline": bc.SetOffline,
"waitForEvent": func(event string, optsOrPredicate sobek.Value) (*sobek.Promise, error) {
ctx := vu.Context()
popts := common.NewWaitForEventOptions(
Expand Down
Loading
Loading