diff --git a/api/element_handle.go b/api/element_handle.go index b9e563426..43c9552fd 100644 --- a/api/element_handle.go +++ b/api/element_handle.go @@ -27,8 +27,8 @@ type ElementHandle interface { IsVisible() bool OwnerFrame() Frame Press(key string, opts goja.Value) - Query(selector string) ElementHandle - QueryAll(selector string) []ElementHandle + Query(selector string) (ElementHandle, error) + QueryAll(selector string) ([]ElementHandle, error) Screenshot(opts goja.Value) goja.ArrayBuffer ScrollIntoViewIfNeeded(opts goja.Value) SelectOption(values goja.Value, opts goja.Value) []string diff --git a/api/frame.go b/api/frame.go index 5f474a7fc..1b02e70f8 100644 --- a/api/frame.go +++ b/api/frame.go @@ -35,8 +35,8 @@ type Frame interface { // Locator creates and returns a new locator for this frame. Locator(selector string, opts goja.Value) Locator Name() string - Query(selector string) ElementHandle - QueryAll(selector string) []ElementHandle + Query(selector string) (ElementHandle, error) + QueryAll(selector string) ([]ElementHandle, error) Page() Page ParentFrame() Frame Press(selector string, key string, opts goja.Value) diff --git a/api/page.go b/api/page.go index af056ddda..38589ceb5 100644 --- a/api/page.go +++ b/api/page.go @@ -51,8 +51,8 @@ type Page interface { Pause() Pdf(opts goja.Value) []byte Press(selector string, key string, opts goja.Value) - Query(selector string) ElementHandle - QueryAll(selector string) []ElementHandle + Query(selector string) (ElementHandle, error) + QueryAll(selector string) ([]ElementHandle, error) Reload(opts goja.Value) Response Route(url goja.Value, handler goja.Callable) Screenshot(opts goja.Value) goja.ArrayBuffer diff --git a/browser/mapping.go b/browser/mapping.go index 2f54e4db0..29693b9c5 100644 --- a/browser/mapping.go +++ b/browser/mapping.go @@ -245,21 +245,25 @@ func mapElementHandle(vu moduleVU, eh api.ElementHandle) mapping { return rt.ToValue(ehm).ToObject(rt) }, } - maps["$"] = func(selector string) *goja.Object { - eh := eh.Query(selector) + maps["$"] = func(selector string) (mapping, error) { + eh, err := eh.Query(selector) + if err != nil { + return nil, err //nolint:wrapcheck + } ehm := mapElementHandle(vu, eh) - return rt.ToValue(ehm).ToObject(rt) + return ehm, nil } - maps["$$"] = func(selector string) *goja.Object { - var ( - mehs []mapping - ehs = eh.QueryAll(selector) - ) + maps["$$"] = func(selector string) ([]mapping, error) { + ehs, err := eh.QueryAll(selector) + if err != nil { + return nil, err //nolint:wrapcheck + } + var mehs []mapping for _, eh := range ehs { ehm := mapElementHandle(vu, eh) mehs = append(mehs, ehm) } - return rt.ToValue(mehs).ToObject(rt) + return mehs, nil } jsHandleMap := mapJSHandle(vu, eh) @@ -377,21 +381,25 @@ func mapFrame(vu moduleVU, f api.Frame) mapping { }, "waitForTimeout": f.WaitForTimeout, } - maps["$"] = func(selector string) *goja.Object { - eh := f.Query(selector) + maps["$"] = func(selector string) (mapping, error) { + eh, err := f.Query(selector) + if err != nil { + return nil, err //nolint:wrapcheck + } ehm := mapElementHandle(vu, eh) - return rt.ToValue(ehm).ToObject(rt) + return ehm, nil } - maps["$$"] = func(selector string) *goja.Object { - var ( - mehs []mapping - ehs = f.QueryAll(selector) - ) + maps["$$"] = func(selector string) ([]mapping, error) { + ehs, err := f.QueryAll(selector) + if err != nil { + return nil, err //nolint:wrapcheck + } + var mehs []mapping for _, eh := range ehs { ehm := mapElementHandle(vu, eh) mehs = append(mehs, ehm) } - return rt.ToValue(mehs).ToObject(rt) + return mehs, nil } return maps @@ -540,21 +548,25 @@ func mapPage(vu moduleVU, p api.Page) mapping { return rt.ToValue(mws).ToObject(rt) }, } - maps["$"] = func(selector string) *goja.Object { - eh := p.Query(selector) + maps["$"] = func(selector string) (mapping, error) { + eh, err := p.Query(selector) + if err != nil { + return nil, err //nolint:wrapcheck + } ehm := mapElementHandle(vu, eh) - return rt.ToValue(ehm).ToObject(rt) + return ehm, nil } - maps["$$"] = func(selector string) *goja.Object { - var ( - mehs []mapping - ehs = p.QueryAll(selector) - ) + maps["$$"] = func(selector string) ([]mapping, error) { + ehs, err := p.QueryAll(selector) + if err != nil { + return nil, err //nolint:wrapcheck + } + var mehs []mapping for _, eh := range ehs { ehm := mapElementHandle(vu, eh) mehs = append(mehs, ehm) } - return rt.ToValue(mehs).ToObject(rt) + return mehs, nil } return maps diff --git a/common/element_handle.go b/common/element_handle.go index 49bf5a501..9b8635a35 100644 --- a/common/element_handle.go +++ b/common/element_handle.go @@ -1023,7 +1023,7 @@ func (h *ElementHandle) Press(key string, opts goja.Value) { // Query runs "element.querySelector" within the page. If no element matches the selector, // the return value resolves to "null". -func (h *ElementHandle) Query(selector string) api.ElementHandle { +func (h *ElementHandle) Query(selector string) (api.ElementHandle, error) { parsedSelector, err := NewSelector(selector) if err != nil { k6ext.Panic(h.ctx, "parsing selector %q: %w", selector, err) @@ -1039,35 +1039,35 @@ func (h *ElementHandle) Query(selector string) api.ElementHandle { } result, err := h.evalWithScript(h.ctx, opts, fn, parsedSelector) if err != nil { - k6ext.Panic(h.ctx, "querying selector %q: %w", selector, err) + return nil, fmt.Errorf("querying selector %q: %w", selector, err) } if result == nil { - return nil + return nil, fmt.Errorf("querying selector %q", selector) } - - var ( - handle = result.(api.JSHandle) - element = handle.AsElement() - ) - applySlowMo(h.ctx) - if element != nil { - return element + handle, ok := result.(api.JSHandle) + if !ok { + return nil, fmt.Errorf("querying selector %q, wrong type %T", selector, result) } - handle.Dispose() - return nil + element := handle.AsElement() + if element == nil { + handle.Dispose() + return nil, fmt.Errorf("querying selector %q", selector) + } + + return element, nil } // QueryAll queries element subtree for matching elements. // If no element matches the selector, the return value resolves to "null". -func (h *ElementHandle) QueryAll(selector string) []api.ElementHandle { +func (h *ElementHandle) QueryAll(selector string) ([]api.ElementHandle, error) { defer applySlowMo(h.ctx) handles, err := h.queryAll(selector, h.evalWithScript) if err != nil { - k6ext.Panic(h.ctx, "querying all selector %q: %w", selector, err) + return nil, fmt.Errorf("querying all selector %q: %w", selector, err) } - return handles + return handles, nil } func (h *ElementHandle) queryAll(selector string, eval evalFunc) ([]api.ElementHandle, error) { diff --git a/common/frame.go b/common/frame.go index 54f30aeec..e556e17ec 100644 --- a/common/frame.go +++ b/common/frame.go @@ -1304,32 +1304,25 @@ func (f *Frame) Name() string { // Query runs a selector query against the document tree, returning the first matching element or // "null" if no match is found. -func (f *Frame) Query(selector string) api.ElementHandle { +func (f *Frame) Query(selector string) (api.ElementHandle, error) { f.log.Debugf("Frame:Query", "fid:%s furl:%q sel:%q", f.ID(), f.URL(), selector) document, err := f.document() if err != nil { k6ext.Panic(f.ctx, "getting document: %w", err) } - value := document.Query(selector) - if value != nil { - return value - } - return nil + return document.Query(selector) } -func (f *Frame) QueryAll(selector string) []api.ElementHandle { +// QueryAll runs a selector query against the document tree, returning all matching elements. +func (f *Frame) QueryAll(selector string) ([]api.ElementHandle, error) { f.log.Debugf("Frame:QueryAll", "fid:%s furl:%q sel:%q", f.ID(), f.URL(), selector) document, err := f.document() if err != nil { k6ext.Panic(f.ctx, "getting document: %w", err) } - value := document.QueryAll(selector) - if value != nil { - return value - } - return nil + return document.QueryAll(selector) } // Page returns page that owns frame. diff --git a/common/page.go b/common/page.go index 41100971e..74327dbdc 100644 --- a/common/page.go +++ b/common/page.go @@ -675,13 +675,15 @@ func (p *Page) Press(selector string, key string, opts goja.Value) { p.MainFrame().Press(selector, key, opts) } -func (p *Page) Query(selector string) api.ElementHandle { +// Query returns the first element matching the specified selector. +func (p *Page) Query(selector string) (api.ElementHandle, error) { p.logger.Debugf("Page:Query", "sid:%v selector:%s", p.sessionID(), selector) return p.frameManager.MainFrame().Query(selector) } -func (p *Page) QueryAll(selector string) []api.ElementHandle { +// QueryAll returns all elements matching the specified selector. +func (p *Page) QueryAll(selector string) ([]api.ElementHandle, error) { p.logger.Debugf("Page:QueryAll", "sid:%v selector:%s", p.sessionID(), selector) return p.frameManager.MainFrame().QueryAll(selector) diff --git a/tests/element_handle_test.go b/tests/element_handle_test.go index 40fc7cebd..7e12a1355 100644 --- a/tests/element_handle_test.go +++ b/tests/element_handle_test.go @@ -58,8 +58,8 @@ func TestElementHandleBoundingBoxInvisibleElement(t *testing.T) { p := newTestBrowser(t).NewPage(nil) p.SetContent(`
`, nil) - element := p.Query("div") - + element, err := p.Query("div") + require.NoError(t, err) require.Nil(t, element.BoundingBox()) } @@ -72,7 +72,10 @@ func TestElementHandleBoundingBoxSVG(t *testing.T) {