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

Split mappings to prepare for async migration #1298

Merged
merged 14 commits into from
May 3, 2024
132 changes: 132 additions & 0 deletions browser/browser_context_mapping.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,132 @@
package browser

import (
"fmt"
"reflect"

"github.com/dop251/goja"

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

// mapBrowserContext to the JS module.
func mapBrowserContext(vu moduleVU, bc *common.BrowserContext) mapping { //nolint:funlen,gocognit,cyclop
rt := vu.Runtime()
return mapping{
"addCookies": bc.AddCookies,
"addInitScript": func(script goja.Value) error {
if !gojaValueExists(script) {
return nil
}

source := ""
switch script.ExportType() {
case reflect.TypeOf(string("")):
source = script.String()
case reflect.TypeOf(goja.Object{}):
opts := script.ToObject(rt)
for _, k := range opts.Keys() {
if k == "content" {
source = opts.Get(k).String()
}
}
default:
_, isCallable := goja.AssertFunction(script)
if !isCallable {
source = fmt.Sprintf("(%s);", script.ToString().String())
} else {
source = fmt.Sprintf("(%s)(...args);", script.ToString().String())
}
}

return bc.AddInitScript(source) //nolint:wrapcheck
},
"browser": bc.Browser,
"clearCookies": bc.ClearCookies,
"clearPermissions": bc.ClearPermissions,
"close": bc.Close,
"cookies": bc.Cookies,
"grantPermissions": func(permissions []string, opts goja.Value) error {
pOpts := common.NewGrantPermissionsOptions()
pOpts.Parse(vu.Context(), opts)

return bc.GrantPermissions(permissions, pOpts) //nolint:wrapcheck
},
"setDefaultNavigationTimeout": bc.SetDefaultNavigationTimeout,
"setDefaultTimeout": bc.SetDefaultTimeout,
"setGeolocation": bc.SetGeolocation,
"setHTTPCredentials": bc.SetHTTPCredentials, //nolint:staticcheck
"setOffline": bc.SetOffline,
"waitForEvent": func(event string, optsOrPredicate goja.Value) (*goja.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)
}

return k6ext.Promise(ctx, func() (result any, reason error) {
var runInTaskQueue func(p *common.Page) (bool, error)
if popts.PredicateFn != nil {
runInTaskQueue = func(p *common.Page) (bool, error) {
tq := vu.taskQueueRegistry.get(p.TargetID())

var rtn bool
var err error
// The function on the taskqueue runs in its own goroutine
// so we need to use a channel to wait for it to complete
// before returning the result to the caller.
c := make(chan bool)
tq.Queue(func() error {
var resp goja.Value
resp, err = popts.PredicateFn(vu.Runtime().ToValue(p))
rtn = resp.ToBoolean()
close(c)
return nil
})
<-c

return rtn, err //nolint:wrapcheck
}
}

resp, err := bc.WaitForEvent(event, runInTaskQueue, popts.Timeout)
panicIfFatalError(ctx, err)
if err != nil {
return nil, err //nolint:wrapcheck
}
p, ok := resp.(*common.Page)
if !ok {
panicIfFatalError(ctx, fmt.Errorf("response object is not a page: %w", k6error.ErrFatal))
}

return mapPage(vu, p), nil
}), nil
},
"pages": func() *goja.Object {
var (
mpages []mapping
pages = bc.Pages()
)
for _, page := range pages {
if page == nil {
continue
}
m := mapPage(vu, page)
mpages = append(mpages, m)
}

return rt.ToValue(mpages).ToObject(rt)
},
"newPage": func() (mapping, error) {
page, err := bc.NewPage()
if err != nil {
return nil, err //nolint:wrapcheck
}
return mapPage(vu, page), nil
},
}
}
99 changes: 99 additions & 0 deletions browser/browser_mapping.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,99 @@
package browser

import (
"fmt"

"github.com/dop251/goja"

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

// mapBrowser to the JS module.
func mapBrowser(vu moduleVU) mapping { //nolint:funlen,cyclop
rt := vu.Runtime()
return mapping{
"context": func() (*common.BrowserContext, error) {
b, err := vu.browser()
if err != nil {
return nil, err
}
return b.Context(), nil
},
"closeContext": func() error {
b, err := vu.browser()
if err != nil {
return err
}
return b.CloseContext() //nolint:wrapcheck
},
"isConnected": func() (bool, error) {
b, err := vu.browser()
if err != nil {
return false, err
}
return b.IsConnected(), nil
},
"newContext": func(opts goja.Value) (*goja.Object, error) {
b, err := vu.browser()
if err != nil {
return nil, err
}
bctx, err := b.NewContext(opts)
if err != nil {
return nil, err //nolint:wrapcheck
}

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

m := mapBrowserContext(vu, bctx)

return rt.ToValue(m).ToObject(rt), nil
},
"userAgent": func() (string, error) {
b, err := vu.browser()
if err != nil {
return "", err
}
return b.UserAgent(), nil
},
"version": func() (string, error) {
b, err := vu.browser()
if err != nil {
return "", err
}
return b.Version(), nil
},
"newPage": func(opts goja.Value) (mapping, error) {
b, err := vu.browser()
if err != nil {
return nil, err
}
page, err := b.NewPage(opts)
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
},
}
}

func initBrowserContext(bctx *common.BrowserContext, testRunID string) error {
// Setting a k6 object which will contain k6 specific metadata
// on the current test run. This allows external applications
// (such as Grafana Faro) to identify that the session is a k6
// automated one and not one driven by a real person.
if err := bctx.AddInitScript(
fmt.Sprintf(`window.k6 = { testRunId: %q }`, testRunID),
); err != nil {
return fmt.Errorf("adding k6 object to new browser context: %w", err)
}

return nil
}
38 changes: 38 additions & 0 deletions browser/console_message_mapping.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
package browser

import (
"github.com/dop251/goja"

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

// mapConsoleMessage to the JS module.
func mapConsoleMessage(vu moduleVU, cm *common.ConsoleMessage) mapping {
rt := vu.Runtime()
return mapping{
"args": func() *goja.Object {
var (
margs []mapping
args = cm.Args
)
for _, arg := range args {
a := mapJSHandle(vu, arg)
margs = append(margs, a)
}

return rt.ToValue(margs).ToObject(rt)
},
// page(), text() and type() are defined as
// functions in order to match Playwright's API
"page": func() *goja.Object {
mp := mapPage(vu, cm.Page)
return rt.ToValue(mp).ToObject(rt)
},
"text": func() *goja.Object {
return rt.ToValue(cm.Text).ToObject(rt)
},
"type": func() *goja.Object {
return rt.ToValue(cm.Type).ToObject(rt)
},
}
}
Loading
Loading