Skip to content
This repository was archived by the owner on Jan 30, 2025. It is now read-only.

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