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

Prepare Page for async migration part #2 #1345

Merged
merged 17 commits into from
May 29, 2024
Merged
Show file tree
Hide file tree
Changes from 12 commits
Commits
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
40 changes: 20 additions & 20 deletions browser/mapping_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -293,22 +293,22 @@ type browserContextAPI interface {

// pageAPI is the interface of a single browser tab.
type pageAPI interface {
BringToFront()
Check(selector string, opts goja.Value)
BringToFront() error
Check(selector string, opts goja.Value) error
Click(selector string, opts goja.Value) error
Close(opts goja.Value) error
Content() string
Content() (string, error)
Context() *common.BrowserContext
Dblclick(selector string, opts goja.Value)
Dblclick(selector string, opts goja.Value) error
DispatchEvent(selector string, typ string, eventInit goja.Value, opts goja.Value)
EmulateMedia(opts goja.Value)
EmulateVisionDeficiency(typ string)
EmulateMedia(opts goja.Value) error
EmulateVisionDeficiency(typ string) error
Evaluate(pageFunc goja.Value, arg ...goja.Value) (any, error)
EvaluateHandle(pageFunc goja.Value, arg ...goja.Value) (common.JSHandleAPI, error)
Fill(selector string, value string, opts goja.Value)
Focus(selector string, opts goja.Value)
Fill(selector string, value string, opts goja.Value) error
Focus(selector string, opts goja.Value) error
Frames() []*common.Frame
GetAttribute(selector string, name string, opts goja.Value) goja.Value
GetAttribute(selector string, name string, opts goja.Value) (any, error)
GetKeyboard() *common.Keyboard
GetMouse() *common.Mouse
GetTouchscreen() *common.Touchscreen
Expand All @@ -317,7 +317,7 @@ type pageAPI interface {
InnerHTML(selector string, opts goja.Value) string
InnerText(selector string, opts goja.Value) string
InputValue(selector string, opts goja.Value) string
IsChecked(selector string, opts goja.Value) bool
IsChecked(selector string, opts goja.Value) (bool, error)
IsClosed() bool
IsDisabled(selector string, opts goja.Value) bool
IsEditable(selector string, opts goja.Value) bool
Expand All @@ -339,14 +339,14 @@ type pageAPI interface {
SetDefaultTimeout(timeout int64)
SetExtraHTTPHeaders(headers map[string]string)
SetInputFiles(selector string, files goja.Value, opts goja.Value)
SetViewportSize(viewportSize goja.Value)
SetViewportSize(viewportSize goja.Value) error
Tap(selector string, opts goja.Value) error
TextContent(selector string, opts goja.Value) string
ThrottleCPU(common.CPUProfile) error
ThrottleNetwork(common.NetworkProfile) error
Title() (string, error)
Type(selector string, text string, opts goja.Value)
Uncheck(selector string, opts goja.Value)
Uncheck(selector string, opts goja.Value) error
URL() (string, error)
ViewportSize() map[string]float64
WaitForFunction(fn, opts goja.Value, args ...goja.Value) (any, error)
Expand All @@ -367,26 +367,26 @@ type consoleMessageAPI interface {

// frameAPI is the interface of a CDP target frame.
type frameAPI interface {
Check(selector string, opts goja.Value)
Check(selector string, opts goja.Value) error
ChildFrames() []*common.Frame
Click(selector string, opts goja.Value) error
Content() string
Dblclick(selector string, opts goja.Value)
Content() (string, error)
Dblclick(selector string, opts goja.Value) error
DispatchEvent(selector string, typ string, eventInit goja.Value, opts goja.Value)
// EvaluateWithContext for internal use only
EvaluateWithContext(ctx context.Context, pageFunc goja.Value, args ...goja.Value) (any, error)
Evaluate(pageFunc goja.Value, args ...goja.Value) (any, error)
EvaluateHandle(pageFunc goja.Value, args ...goja.Value) (common.JSHandleAPI, error)
Fill(selector string, value string, opts goja.Value)
Focus(selector string, opts goja.Value)
Fill(selector string, value string, opts goja.Value) error
Focus(selector string, opts goja.Value) error
FrameElement() (*common.ElementHandle, error)
GetAttribute(selector string, name string, opts goja.Value) goja.Value
GetAttribute(selector string, name string, opts goja.Value) (any, error)
Goto(url string, opts goja.Value) (*common.Response, error)
Hover(selector string, opts goja.Value)
InnerHTML(selector string, opts goja.Value) string
InnerText(selector string, opts goja.Value) string
InputValue(selector string, opts goja.Value) string
IsChecked(selector string, opts goja.Value) bool
IsChecked(selector string, opts goja.Value) (bool, error)
IsDetached() bool
IsDisabled(selector string, opts goja.Value) bool
IsEditable(selector string, opts goja.Value) bool
Expand All @@ -409,7 +409,7 @@ type frameAPI interface {
TextContent(selector string, opts goja.Value) string
Title() string
Type(selector string, text string, opts goja.Value)
Uncheck(selector string, opts goja.Value)
Uncheck(selector string, opts goja.Value) error
URL() string
WaitForFunction(pageFunc, opts goja.Value, args ...goja.Value) (any, error)
WaitForLoadState(state string, opts goja.Value)
Expand Down
80 changes: 49 additions & 31 deletions common/frame.go
Original file line number Diff line number Diff line change
Expand Up @@ -604,17 +604,20 @@ func (f *Frame) click(selector string, opts *FrameClickOptions) error {
}

// Check clicks the first element found that matches selector.
func (f *Frame) Check(selector string, opts goja.Value) {
func (f *Frame) Check(selector string, opts goja.Value) error {
f.log.Debugf("Frame:Check", "fid:%s furl:%q sel:%q", f.ID(), f.URL(), selector)

popts := NewFrameCheckOptions(f.defaultTimeout())
if err := popts.Parse(f.ctx, opts); err != nil {
k6ext.Panic(f.ctx, "parsing new frame check options: %w", err)
return fmt.Errorf("parsing new frame check options: %w", err)
}
if err := f.check(selector, popts); err != nil {
k6ext.Panic(f.ctx, "checking %q: %w", selector, err)
return fmt.Errorf("checking %q: %w", selector, err)
}

applySlowMo(f.ctx)

return nil
}

func (f *Frame) check(selector string, opts *FrameCheckOptions) error {
Expand All @@ -632,17 +635,20 @@ func (f *Frame) check(selector string, opts *FrameCheckOptions) error {
}

// Uncheck the first found element that matches the selector.
func (f *Frame) Uncheck(selector string, opts goja.Value) {
func (f *Frame) Uncheck(selector string, opts goja.Value) error {
f.log.Debugf("Frame:Uncheck", "fid:%s furl:%q sel:%q", f.ID(), f.URL(), selector)

popts := NewFrameUncheckOptions(f.defaultTimeout())
if err := popts.Parse(f.ctx, opts); err != nil {
k6ext.Panic(f.ctx, "parsing frame uncheck options %q: %w", selector, err)
return fmt.Errorf("parsing frame uncheck options %q: %w", selector, err)
}
if err := f.uncheck(selector, popts); err != nil {
k6ext.Panic(f.ctx, "unchecking %q: %w", selector, err)
return fmt.Errorf("unchecking %q: %w", selector, err)
}

applySlowMo(f.ctx)

return nil
}

func (f *Frame) uncheck(selector string, opts *FrameUncheckOptions) error {
Expand All @@ -661,19 +667,19 @@ func (f *Frame) uncheck(selector string, opts *FrameUncheckOptions) error {

// IsChecked returns true if the first element that matches the selector
// is checked. Otherwise, returns false.
func (f *Frame) IsChecked(selector string, opts goja.Value) bool {
func (f *Frame) IsChecked(selector string, opts goja.Value) (bool, error) {
f.log.Debugf("Frame:IsChecked", "fid:%s furl:%q sel:%q", f.ID(), f.URL(), selector)

popts := NewFrameIsCheckedOptions(f.defaultTimeout())
if err := popts.Parse(f.ctx, opts); err != nil {
k6ext.Panic(f.ctx, "parsing is checked options: %w", err)
return false, fmt.Errorf("parsing is checked options: %w", err)
}
checked, err := f.isChecked(selector, popts)
if err != nil {
k6ext.Panic(f.ctx, "checking element is checked %q: %w", selector, err)
return false, fmt.Errorf("checking element is checked %q: %w", selector, err)
}

return checked
return checked, nil
}

func (f *Frame) isChecked(selector string, opts *FrameIsCheckedOptions) (bool, error) {
Expand Down Expand Up @@ -701,7 +707,7 @@ func (f *Frame) isChecked(selector string, opts *FrameIsCheckedOptions) (bool, e
}

// Content returns the HTML content of the frame.
func (f *Frame) Content() string {
func (f *Frame) Content() (string, error) {
f.log.Debugf("Frame:Content", "fid:%s furl:%q", f.ID(), f.URL())

js := `() => {
Expand All @@ -715,31 +721,39 @@ func (f *Frame) Content() string {
return content;
}`

v, _ := f.Evaluate(js)

// TODO: return error
v, err := f.Evaluate(js)
if err != nil {
return "", fmt.Errorf("getting frame content: %w", err)
}
s, ok := v.(string)
if !ok {
return "", fmt.Errorf("getting frame content: expected string, got %T", v)
}

return v.(string) //nolint:forcetypeassert
return s, nil
}

// Dblclick double clicks an element matching provided selector.
func (f *Frame) Dblclick(selector string, opts goja.Value) {
func (f *Frame) Dblclick(selector string, opts goja.Value) error {
f.log.Debugf("Frame:DblClick", "fid:%s furl:%q sel:%q", f.ID(), f.URL(), selector)

popts := NewFrameDblClickOptions(f.defaultTimeout())
if err := popts.Parse(f.ctx, opts); err != nil {
k6ext.Panic(f.ctx, "parsing double click options: %w", err)
return fmt.Errorf("parsing double click options: %w", err)
}
if err := f.dblclick(selector, popts); err != nil {
k6ext.Panic(f.ctx, "double clicking on %q: %w", selector, err)
return fmt.Errorf("double clicking on %q: %w", selector, err)
}

applySlowMo(f.ctx)

return nil
}

// dblclick is like Dblclick but takes parsed options and neither throws
// an error, or applies slow motion.
func (f *Frame) dblclick(selector string, opts *FrameDblclickOptions) error {
dblclick := func(apiCtx context.Context, eh *ElementHandle, p *Position) (any, error) {
dblclick := func(_ context.Context, eh *ElementHandle, p *Position) (any, error) {
return nil, eh.dblclick(p, opts.ToMouseClickOptions())
}
act := f.newPointerAction(
Expand Down Expand Up @@ -858,17 +872,20 @@ func (f *Frame) EvaluateHandle(pageFunc string, args ...any) (handle JSHandleAPI
}

// Fill fills out the first element found that matches the selector.
func (f *Frame) Fill(selector, value string, opts goja.Value) {
func (f *Frame) Fill(selector, value string, opts goja.Value) error {
f.log.Debugf("Frame:Fill", "fid:%s furl:%q sel:%q val:%q", f.ID(), f.URL(), selector, value)

popts := NewFrameFillOptions(f.defaultTimeout())
if err := popts.Parse(f.ctx, opts); err != nil {
k6ext.Panic(f.ctx, "parsing fill options: %w", err)
return fmt.Errorf("parsing fill options: %w", err)
}
if err := f.fill(selector, value, popts); err != nil {
k6ext.Panic(f.ctx, "filling %q with %q: %w", selector, value, err)
return fmt.Errorf("filling %q with %q: %w", selector, value, err)
}

applySlowMo(f.ctx)

return nil
}

func (f *Frame) fill(selector, value string, opts *FrameFillOptions) error {
Expand All @@ -888,17 +905,20 @@ func (f *Frame) fill(selector, value string, opts *FrameFillOptions) error {
}

// Focus focuses on the first element that matches the selector.
func (f *Frame) Focus(selector string, opts goja.Value) {
func (f *Frame) Focus(selector string, opts goja.Value) error {
f.log.Debugf("Frame:Focus", "fid:%s furl:%q sel:%q", f.ID(), f.URL(), selector)

popts := NewFrameBaseOptions(f.defaultTimeout())
if err := popts.Parse(f.ctx, opts); err != nil {
k6ext.Panic(f.ctx, "parsing focus options: %w", err)
return fmt.Errorf("parsing focus options: %w", err)
}
if err := f.focus(selector, popts); err != nil {
k6ext.Panic(f.ctx, "focusing %q: %w", selector, err)
return fmt.Errorf("focusing %q: %w", selector, err)
}

applySlowMo(f.ctx)

return nil
}

func (f *Frame) focus(selector string, opts *FrameBaseOptions) error {
Expand Down Expand Up @@ -928,21 +948,19 @@ func (f *Frame) FrameElement() (*ElementHandle, error) {
}

// GetAttribute of the first element found that matches the selector.
func (f *Frame) GetAttribute(selector, name string, opts goja.Value) any {
func (f *Frame) GetAttribute(selector, name string, opts goja.Value) (any, error) {
Copy link
Collaborator

@ankur22 ankur22 May 28, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

GetAttribute should only returns a string. Having taken a quick look at the call stack it looks like we're using element.getAttribute, maybe a string instead of any?

It should also map to nil if the string is empty... not sure what the best way forward for that would be since an attribute could just be empty :/

Copy link
Member Author

@inancgumus inancgumus May 29, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

How about returning a bool to detect that in the mapping layer?

func (f *Frame) GetAttribute(selector, name string, opts goja.Value) (string, bool, error)

Even so, this should better go to a new issue because it'd change the existing signatures. I've tried to keep the signatures intact other than adding errors.

f.log.Debugf("Frame:GetAttribute", "fid:%s furl:%q sel:%q name:%s", f.ID(), f.URL(), selector, name)

popts := NewFrameBaseOptions(f.defaultTimeout())
if err := popts.Parse(f.ctx, opts); err != nil {
k6ext.Panic(f.ctx, "parse: %w", err)
return nil, fmt.Errorf("parsing get attribute options: %w", err)
}
v, err := f.getAttribute(selector, name, popts)
if err != nil {
k6ext.Panic(f.ctx, "getting attribute %q of %q: %w", name, selector, err)
return nil, fmt.Errorf("getting attribute %q of %q: %w", name, selector, err)
}

applySlowMo(f.ctx)

return v
return v, nil
}

func (f *Frame) getAttribute(selector, name string, opts *FrameBaseOptions) (any, error) {
Expand Down
Loading