diff --git a/browser/module.go b/browser/module.go index 1913878f7..7e16a9c28 100644 --- a/browser/module.go +++ b/browser/module.go @@ -74,6 +74,27 @@ func (mi *ModuleInstance) Exports() k6modules.Exports { type mapping map[string]any +// wildcards is a list of extra mappings for our API (api/). +func wildcards() map[string]string { + return map[string]string{ + "Page.query": "$", + "Page.queryAll": "$$", + "Frame.query": "$", + "Frame.queryAll": "$$", + "ElementHandle.query": "$", + "ElementHandle.queryAll": "$$", + } +} + +// isWildcard returns true if the method is a wildcard method and +// returns the name of the method to be called instead of the original +// method. +func isWildcard(exceptions map[string]string, typ, method string) (string, bool) { + name := typ + "." + method + s, ok := exceptions[name] + return s, ok +} + // mapBrowserToGoja maps the browser API to the JS module. // The motivation of this mapping was to support $ and $$ wildcard // methods. @@ -251,25 +272,23 @@ func mapPage(rt *goja.Runtime, p api.Page) mapping { "waitForSelector": p.WaitForSelector, "waitForTimeout": p.WaitForTimeout, "workers": p.Workers, - "query": func(selector string) *goja.Object { - eh := p.Query(selector) + } + maps["$"] = func(selector string) *goja.Object { + eh := p.Query(selector) + ehm := mapElementHandle(rt, eh) + return rt.ToValue(ehm).ToObject(rt) + } + maps["$$"] = func(selector string) *goja.Object { + var ( + mehs []mapping + ehs = p.QueryAll(selector) + ) + for _, eh := range ehs { ehm := mapElementHandle(rt, eh) - return rt.ToValue(ehm).ToObject(rt) - }, - "queryAll": func(selector string) *goja.Object { - var ( - mehs []mapping - ehs = p.QueryAll(selector) - ) - for _, eh := range ehs { - ehm := mapElementHandle(rt, eh) - mehs = append(mehs, ehm) - } - return rt.ToValue(mehs).ToObject(rt) - }, + mehs = append(mehs, ehm) + } + return rt.ToValue(mehs).ToObject(rt) } - maps["$"] = maps["query"] - maps["$$"] = maps["queryAll"] return maps } @@ -319,22 +338,6 @@ func mapFrame(rt *goja.Runtime, f api.Frame) mapping { "loaderID": f.LoaderID, "locator": f.Locator, "name": f.Name, - "query": func(selector string) *goja.Object { - eh := f.Query(selector) - ehm := mapElementHandle(rt, eh) - return rt.ToValue(ehm).ToObject(rt) - }, - "queryAll": func(selector string) *goja.Object { - var ( - mehs []mapping - ehs = f.QueryAll(selector) - ) - for _, eh := range ehs { - ehm := mapElementHandle(rt, eh) - mehs = append(mehs, ehm) - } - return rt.ToValue(mehs).ToObject(rt) - }, "page": func() *goja.Object { mp := mapPage(rt, f.Page()) return rt.ToValue(mp).ToObject(rt) @@ -363,8 +366,22 @@ func mapFrame(rt *goja.Runtime, f api.Frame) mapping { }, "waitForTimeout": f.WaitForTimeout, } - maps["$"] = maps["query"] - maps["$$"] = maps["queryAll"] + maps["$"] = func(selector string) *goja.Object { + eh := f.Query(selector) + ehm := mapElementHandle(rt, eh) + return rt.ToValue(ehm).ToObject(rt) + } + maps["$$"] = func(selector string) *goja.Object { + var ( + mehs []mapping + ehs = f.QueryAll(selector) + ) + for _, eh := range ehs { + ehm := mapElementHandle(rt, eh) + mehs = append(mehs, ehm) + } + return rt.ToValue(mehs).ToObject(rt) + } return maps } @@ -427,25 +444,23 @@ func mapElementHandle(rt *goja.Runtime, eh api.ElementHandle) mapping { ehm := mapElementHandle(rt, eh) return rt.ToValue(ehm).ToObject(rt) }, - "query": func(selector string) *goja.Object { - eh := eh.Query(selector) + } + maps["$"] = func(selector string) *goja.Object { + eh := eh.Query(selector) + ehm := mapElementHandle(rt, eh) + return rt.ToValue(ehm).ToObject(rt) + } + maps["$$"] = func(selector string) *goja.Object { + var ( + mehs []mapping + ehs = eh.QueryAll(selector) + ) + for _, eh := range ehs { ehm := mapElementHandle(rt, eh) - return rt.ToValue(ehm).ToObject(rt) - }, - "queryAll": func(selector string) *goja.Object { - var ( - mehs []mapping - ehs = eh.QueryAll(selector) - ) - for _, eh := range ehs { - ehm := mapElementHandle(rt, eh) - mehs = append(mehs, ehm) - } - return rt.ToValue(mehs).ToObject(rt) - }, + mehs = append(mehs, ehm) + } + return rt.ToValue(mehs).ToObject(rt) } - maps["$"] = maps["query"] - maps["$$"] = maps["queryAll"] return maps } diff --git a/browser/module_test.go b/browser/module_test.go index 5f26e2ad8..afc89a843 100644 --- a/browser/module_test.go +++ b/browser/module_test.go @@ -114,6 +114,9 @@ func TestModuleMappings(t *testing.T) { }, }, } + + wildcards := wildcards() + for name, tt := range mappers { tt := tt t.Run(name, func(t *testing.T) { @@ -128,6 +131,12 @@ func TestModuleMappings(t *testing.T) { require.NotNil(t, method) m := toFirstLetterLower(method.Name) + // change the method name if it is mapped to a wildcard + // method. these wildcard methods are not exist on our + // API. so we need to use the mapped method instead. + if wm, ok := isWildcard(wildcards, typ.Name(), m); ok { + m = wm + } if _, ok := mapped[m]; !ok { t.Errorf("method %s not found", m) }