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

Async Frame #1367

Merged
merged 38 commits into from
Jun 6, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
38 commits
Select commit Hold shift + click to select a range
71d8eef
Update Frame.EvaluateHandle to return err
inancgumus Jun 4, 2024
7feeec5
Update Frame.Query to return err
inancgumus Jun 4, 2024
5ec4083
Update Frame.QueryAll to return err
inancgumus Jun 4, 2024
4d28387
Async Frame.check
inancgumus Jun 4, 2024
a366800
Async Frame.content
inancgumus Jun 4, 2024
8161c72
Async Frame.dblclick
inancgumus Jun 4, 2024
6d3f1e6
Async Frame.dispatchEvent
inancgumus Jun 4, 2024
ed30097
Async Frame.evaluate
inancgumus Jun 4, 2024
249c3e5
Async Frame.evaluateHandle
inancgumus Jun 4, 2024
d68359e
Async Frame.fill
inancgumus Jun 4, 2024
bfdc748
Async Frame.focus
inancgumus Jun 4, 2024
16eb5f2
Async Frame.getAttribute
inancgumus Jun 4, 2024
3b51cc2
Async Frame.hover
inancgumus Jun 4, 2024
49266c0
Async Frame.innerHTML
inancgumus Jun 4, 2024
a1ccb79
Async Frame.innerText
inancgumus Jun 4, 2024
54a1812
Async Frame.inputValue
inancgumus Jun 4, 2024
faab189
Async Frame.isChecked
inancgumus Jun 4, 2024
b377711
Async Frame.isDisabled
inancgumus Jun 4, 2024
70d45c7
Async Frame.isEditable
inancgumus Jun 4, 2024
c9a7458
Async Frame.isEnabled
inancgumus Jun 4, 2024
e2ab7f1
Async Frame.isHidden
inancgumus Jun 4, 2024
806ec95
Async Frame.isVisible
inancgumus Jun 4, 2024
8874a27
Async Frame.press
inancgumus Jun 4, 2024
6cac4d9
Async Frame.selectOption
inancgumus Jun 4, 2024
d303b5c
Async Frame.setContent
inancgumus Jun 4, 2024
297a743
Async Frame.setInputFiles
inancgumus Jun 4, 2024
b012e57
Async Frame.textContent
inancgumus Jun 4, 2024
f2a5adc
Async Frame.title
inancgumus Jun 4, 2024
c5de173
Async Frame.type
inancgumus Jun 4, 2024
846787d
Async Frame.uncheck
inancgumus Jun 4, 2024
31916b2
Async Frame.waitForLoadState
inancgumus Jun 4, 2024
f40e06c
Async Frame.waitForSelector
inancgumus Jun 4, 2024
d6f9ca3
Async Frame.waitForTimeout
inancgumus Jun 4, 2024
4761214
Async Frame.query
inancgumus Jun 4, 2024
1a0a640
Async Frame.queryAll
inancgumus Jun 4, 2024
d328f6a
Remove Frame unnecessary goja object mappings
inancgumus Jun 4, 2024
b1b6e9e
Improve null detection in parseRemoteObjectValue
inancgumus Jun 5, 2024
30073cf
Fix async Frame.frameElement mapping
inancgumus Jun 6, 2024
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
289 changes: 202 additions & 87 deletions browser/frame_mapping.go
Original file line number Diff line number Diff line change
Expand Up @@ -13,18 +13,21 @@ import (
//
//nolint:funlen
func mapFrame(vu moduleVU, f *common.Frame) mapping { //nolint:gocognit,cyclop
rt := vu.Runtime()
maps := mapping{
"check": f.Check,
"childFrames": func() *goja.Object {
"check": func(selector string, opts goja.Value) *goja.Promise {
return k6ext.Promise(vu.Context(), func() (any, error) {
return nil, f.Check(selector, opts) //nolint:wrapcheck
})
},
"childFrames": func() []mapping {
var (
mcfs []mapping
cfs = f.ChildFrames()
)
for _, fr := range cfs {
mcfs = append(mcfs, mapFrame(vu, fr))
}
return rt.ToValue(mcfs).ToObject(rt)
return mcfs
},
"click": func(selector string, opts goja.Value) (*goja.Promise, error) {
popts, err := parseFrameClickOptions(vu.Context(), opts, f.Timeout())
Expand All @@ -37,35 +40,63 @@ func mapFrame(vu moduleVU, f *common.Frame) mapping { //nolint:gocognit,cyclop
return nil, err //nolint:wrapcheck
}), nil
},
"content": f.Content,
"dblclick": f.Dblclick,
"dispatchEvent": func(selector, typ string, eventInit, opts goja.Value) error {
"content": func() *goja.Promise {
return k6ext.Promise(vu.Context(), func() (any, error) {
return f.Content() //nolint:wrapcheck
})
},
"dblclick": func(selector string, opts goja.Value) *goja.Promise {
return k6ext.Promise(vu.Context(), func() (any, error) {
return nil, f.Dblclick(selector, opts) //nolint:wrapcheck
})
},
"dispatchEvent": func(selector, typ string, eventInit, opts goja.Value) (*goja.Promise, error) {
popts := common.NewFrameDispatchEventOptions(f.Timeout())
if err := popts.Parse(vu.Context(), opts); err != nil {
return fmt.Errorf("parsing frame dispatch event options: %w", err)
return nil, fmt.Errorf("parsing frame dispatch event options: %w", err)
}
return f.DispatchEvent(selector, typ, exportArg(eventInit), popts) //nolint:wrapcheck
return k6ext.Promise(vu.Context(), func() (any, error) {
return nil, f.DispatchEvent(selector, typ, exportArg(eventInit), popts) //nolint:wrapcheck
}), nil
},
"evaluate": func(pageFunction goja.Value, gargs ...goja.Value) (any, error) {
return f.Evaluate(pageFunction.String(), exportArgs(gargs)...) //nolint:wrapcheck
"evaluate": func(pageFunction goja.Value, gargs ...goja.Value) *goja.Promise {
return k6ext.Promise(vu.Context(), func() (any, error) {
return f.Evaluate(pageFunction.String(), exportArgs(gargs)...) //nolint:wrapcheck
})
},
"evaluateHandle": func(pageFunction goja.Value, gargs ...goja.Value) (mapping, error) {
jsh, err := f.EvaluateHandle(pageFunction.String(), exportArgs(gargs)...)
if err != nil {
return nil, err //nolint:wrapcheck
}
return mapJSHandle(vu, jsh), nil
"evaluateHandle": func(pageFunction goja.Value, gargs ...goja.Value) *goja.Promise {
return k6ext.Promise(vu.Context(), func() (any, error) {
jsh, err := f.EvaluateHandle(pageFunction.String(), exportArgs(gargs)...)
if err != nil {
return nil, err //nolint:wrapcheck
}
return mapJSHandle(vu, jsh), nil
})
},
"fill": f.Fill,
"focus": f.Focus,
"frameElement": func() (mapping, error) {
fe, err := f.FrameElement()
if err != nil {
return nil, err //nolint:wrapcheck
}
return mapElementHandle(vu, fe), nil
"fill": func(selector, value string, opts goja.Value) *goja.Promise {
return k6ext.Promise(vu.Context(), func() (any, error) {
return nil, f.Fill(selector, value, opts) //nolint:wrapcheck
})
},
"focus": func(selector string, opts goja.Value) *goja.Promise {
return k6ext.Promise(vu.Context(), func() (any, error) {
return nil, f.Focus(selector, opts) //nolint:wrapcheck
})
},
"frameElement": func() *goja.Promise {
return k6ext.Promise(vu.Context(), func() (any, error) {
fe, err := f.FrameElement()
if err != nil {
return nil, err //nolint:wrapcheck
}
return mapElementHandle(vu, fe), nil
})
},
"getAttribute": func(selector, name string, opts goja.Value) *goja.Promise {
return k6ext.Promise(vu.Context(), func() (any, error) {
return f.GetAttribute(selector, name, opts) //nolint:wrapcheck
})
},
"getAttribute": f.GetAttribute,
"goto": func(url string, opts goja.Value) (*goja.Promise, error) {
gopts := common.NewFrameGotoOptions(
f.Referrer(),
Expand All @@ -83,34 +114,87 @@ func mapFrame(vu moduleVU, f *common.Frame) mapping { //nolint:gocognit,cyclop
return mapResponse(vu, resp), nil
}), nil
},
"hover": f.Hover,
"innerHTML": f.InnerHTML,
"innerText": f.InnerText,
"inputValue": f.InputValue,
"isChecked": f.IsChecked,
"hover": func(selector string, opts goja.Value) *goja.Promise {
return k6ext.Promise(vu.Context(), func() (any, error) {
return nil, f.Hover(selector, opts) //nolint:wrapcheck
})
},
"innerHTML": func(selector string, opts goja.Value) *goja.Promise {
return k6ext.Promise(vu.Context(), func() (any, error) {
return f.InnerHTML(selector, opts) //nolint:wrapcheck
})
},
"innerText": func(selector string, opts goja.Value) *goja.Promise {
return k6ext.Promise(vu.Context(), func() (any, error) {
return f.InnerText(selector, opts) //nolint:wrapcheck
})
},
"inputValue": func(selector string, opts goja.Value) *goja.Promise {
return k6ext.Promise(vu.Context(), func() (any, error) {
return f.InputValue(selector, opts) //nolint:wrapcheck
})
},
"isChecked": func(selector string, opts goja.Value) *goja.Promise {
return k6ext.Promise(vu.Context(), func() (any, error) {
return f.IsChecked(selector, opts) //nolint:wrapcheck
})
},
"isDetached": f.IsDetached,
"isDisabled": f.IsDisabled,
"isEditable": f.IsEditable,
"isEnabled": f.IsEnabled,
"isHidden": f.IsHidden,
"isVisible": f.IsVisible,
"locator": func(selector string, opts goja.Value) *goja.Object {
ml := mapLocator(vu, f.Locator(selector, opts))
return rt.ToValue(ml).ToObject(rt)
"isDisabled": func(selector string, opts goja.Value) *goja.Promise {
return k6ext.Promise(vu.Context(), func() (any, error) {
return f.IsDisabled(selector, opts) //nolint:wrapcheck
})
},
"isEditable": func(selector string, opts goja.Value) *goja.Promise {
return k6ext.Promise(vu.Context(), func() (any, error) {
return f.IsEditable(selector, opts) //nolint:wrapcheck
})
},
"isEnabled": func(selector string, opts goja.Value) *goja.Promise {
return k6ext.Promise(vu.Context(), func() (any, error) {
return f.IsEnabled(selector, opts) //nolint:wrapcheck
})
},
"isHidden": func(selector string, opts goja.Value) *goja.Promise {
return k6ext.Promise(vu.Context(), func() (any, error) {
return f.IsHidden(selector, opts) //nolint:wrapcheck
})
},
"isVisible": func(selector string, opts goja.Value) *goja.Promise {
return k6ext.Promise(vu.Context(), func() (any, error) {
return f.IsVisible(selector, opts) //nolint:wrapcheck
})
},
"locator": func(selector string, opts goja.Value) mapping {
return mapLocator(vu, f.Locator(selector, opts))
},
"name": f.Name,
"page": func() *goja.Object {
mp := mapPage(vu, f.Page())
return rt.ToValue(mp).ToObject(rt)
},
"parentFrame": func() *goja.Object {
mf := mapFrame(vu, f.ParentFrame())
return rt.ToValue(mf).ToObject(rt)
},
"press": f.Press,
"selectOption": f.SelectOption,
"setContent": f.SetContent,
"setInputFiles": f.SetInputFiles,
"page": func() mapping {
return mapPage(vu, f.Page())
},
"parentFrame": func() mapping {
return mapFrame(vu, f.ParentFrame())
},
"press": func(selector, key string, opts goja.Value) *goja.Promise {
return k6ext.Promise(vu.Context(), func() (any, error) {
return nil, f.Press(selector, key, opts) //nolint:wrapcheck
})
},
"selectOption": func(selector string, values goja.Value, opts goja.Value) *goja.Promise {
return k6ext.Promise(vu.Context(), func() (any, error) {
return f.SelectOption(selector, values, opts) //nolint:wrapcheck
})
},
"setContent": func(html string, opts goja.Value) *goja.Promise {
return k6ext.Promise(vu.Context(), func() (any, error) {
return nil, f.SetContent(html, opts) //nolint:wrapcheck
})
},
"setInputFiles": func(selector string, files goja.Value, opts goja.Value) *goja.Promise {
return k6ext.Promise(vu.Context(), func() (any, error) {
return nil, f.SetInputFiles(selector, files, opts) //nolint:wrapcheck
})
},
"tap": func(selector string, opts goja.Value) (*goja.Promise, error) {
popts := common.NewFrameTapOptions(f.Timeout())
if err := popts.Parse(vu.Context(), opts); err != nil {
Expand All @@ -120,11 +204,27 @@ func mapFrame(vu moduleVU, f *common.Frame) mapping { //nolint:gocognit,cyclop
return nil, f.Tap(selector, popts) //nolint:wrapcheck
}), nil
},
"textContent": f.TextContent,
"title": f.Title,
"type": f.Type,
"uncheck": f.Uncheck,
"url": f.URL,
"textContent": func(selector string, opts goja.Value) *goja.Promise {
return k6ext.Promise(vu.Context(), func() (any, error) {
return f.TextContent(selector, opts) //nolint:wrapcheck
})
},
"title": func() *goja.Promise {
return k6ext.Promise(vu.Context(), func() (any, error) {
return f.Title(), nil
})
},
"type": func(selector, text string, opts goja.Value) *goja.Promise {
return k6ext.Promise(vu.Context(), func() (any, error) {
return nil, f.Type(selector, text, opts) //nolint:wrapcheck
})
},
"uncheck": func(selector string, opts goja.Value) *goja.Promise {
return k6ext.Promise(vu.Context(), func() (any, error) {
return nil, f.Uncheck(selector, opts) //nolint:wrapcheck
})
},
"url": f.URL,
"waitForFunction": func(pageFunc, opts goja.Value, args ...goja.Value) (*goja.Promise, error) {
js, popts, pargs, err := parseWaitForFunctionArgs(
vu.Context(), f.Timeout(), pageFunc, opts, args...,
Expand All @@ -137,7 +237,11 @@ func mapFrame(vu moduleVU, f *common.Frame) mapping { //nolint:gocognit,cyclop
return f.WaitForFunction(js, popts, pargs...) //nolint:wrapcheck
}), nil
},
"waitForLoadState": f.WaitForLoadState,
"waitForLoadState": func(state string, opts goja.Value) *goja.Promise {
return k6ext.Promise(vu.Context(), func() (any, error) {
return nil, f.WaitForLoadState(state, opts) //nolint:wrapcheck
})
},
"waitForNavigation": func(opts goja.Value) (*goja.Promise, error) {
popts := common.NewFrameWaitForNavigationOptions(f.Timeout())
if err := popts.Parse(vu.Context(), opts); err != nil {
Expand All @@ -152,41 +256,52 @@ func mapFrame(vu moduleVU, f *common.Frame) mapping { //nolint:gocognit,cyclop
return mapResponse(vu, resp), nil
}), nil
},
"waitForSelector": func(selector string, opts goja.Value) (mapping, error) {
eh, err := f.WaitForSelector(selector, opts)
"waitForSelector": func(selector string, opts goja.Value) *goja.Promise {
return k6ext.Promise(vu.Context(), func() (any, error) {
eh, err := f.WaitForSelector(selector, opts)
if err != nil {
return nil, err //nolint:wrapcheck
}
return mapElementHandle(vu, eh), nil
})
},
"waitForTimeout": func(timeout int64) *goja.Promise {
return k6ext.Promise(vu.Context(), func() (any, error) {
f.WaitForTimeout(timeout)
return nil, nil
})
},
}
maps["$"] = func(selector string) *goja.Promise {
return k6ext.Promise(vu.Context(), func() (any, error) {
eh, err := f.Query(selector, common.StrictModeOff)
if err != nil {
return nil, err //nolint:wrapcheck
}
return mapElementHandle(vu, eh), nil
},
"waitForTimeout": f.WaitForTimeout,
}
maps["$"] = func(selector string) (mapping, error) {
eh, err := f.Query(selector, common.StrictModeOff)
if err != nil {
return nil, err //nolint:wrapcheck
}
// ElementHandle can be null when the selector does not match any elements.
// We do not want to map nil elementHandles since the expectation is a
// null result in the test script for this case.
if eh == nil {
return nil, nil //nolint:nilnil
}
ehm := mapElementHandle(vu, eh)
// ElementHandle can be null when the selector does not match any elements.
// We do not want to map nil elementHandles since the expectation is a
// null result in the test script for this case.
if eh == nil {
return nil, nil
}
ehm := mapElementHandle(vu, eh)

return ehm, nil
return ehm, nil
})
}
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 mehs, nil
maps["$$"] = func(selector string) *goja.Promise {
return k6ext.Promise(vu.Context(), func() (any, 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 mehs, nil
})
}

return maps
Expand Down
19 changes: 10 additions & 9 deletions common/frame.go
Original file line number Diff line number Diff line change
Expand Up @@ -861,18 +861,19 @@ func (f *Frame) EvaluateGlobal(ctx context.Context, js string) error {
func (f *Frame) EvaluateHandle(pageFunc string, args ...any) (handle JSHandleAPI, _ error) {
f.log.Debugf("Frame:EvaluateHandle", "fid:%s furl:%q", f.ID(), f.URL())

f.waitForExecutionContext(mainWorld)
evalHandle := func() (JSHandleAPI, error) {
f.executionContextMu.RLock()
defer f.executionContextMu.RUnlock()
inancgumus marked this conversation as resolved.
Show resolved Hide resolved

var err error
f.executionContextMu.RLock()
{
ec := f.executionContexts[mainWorld]
if ec == nil {
k6ext.Panic(f.ctx, "evaluating handle for frame: execution context %q not found", mainWorld)
return nil, fmt.Errorf("evaluating handle for frame: execution context %q not found", mainWorld)
}
handle, err = ec.EvalHandle(f.ctx, pageFunc, args...)
return ec.EvalHandle(f.ctx, pageFunc, args...) //nolint:wrapcheck
}
f.executionContextMu.RUnlock()

f.waitForExecutionContext(mainWorld)
handle, err := evalHandle()
if err != nil {
return nil, fmt.Errorf("evaluating handle for frame: %w", err)
}
Expand Down Expand Up @@ -1411,7 +1412,7 @@ func (f *Frame) Query(selector string, strict bool) (*ElementHandle, error) {

document, err := f.document()
if err != nil {
k6ext.Panic(f.ctx, "getting document: %w", err)
return nil, fmt.Errorf("getting document: %w", err)
}
return document.Query(selector, strict)
}
Expand All @@ -1422,7 +1423,7 @@ func (f *Frame) QueryAll(selector string) ([]*ElementHandle, error) {

document, err := f.document()
if err != nil {
k6ext.Panic(f.ctx, "getting document: %w", err)
return nil, fmt.Errorf("getting document: %w", err)
}
return document.QueryAll(selector)
}
Expand Down
Loading
Loading