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

Fix goja escape from DispatchEvents #1193

Merged
merged 3 commits into from
Jan 26, 2024
Merged
Show file tree
Hide file tree
Changes from all 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
110 changes: 65 additions & 45 deletions browser/mapping.go
Original file line number Diff line number Diff line change
Expand Up @@ -62,29 +62,35 @@ func mapLocator(vu moduleVU, lo *common.Locator) mapping {
return nil, lo.Click(popts) //nolint:wrapcheck
}), nil
},
"dblclick": lo.Dblclick,
"check": lo.Check,
"uncheck": lo.Uncheck,
"isChecked": lo.IsChecked,
"isEditable": lo.IsEditable,
"isEnabled": lo.IsEnabled,
"isDisabled": lo.IsDisabled,
"isVisible": lo.IsVisible,
"isHidden": lo.IsHidden,
"fill": lo.Fill,
"focus": lo.Focus,
"getAttribute": lo.GetAttribute,
"innerHTML": lo.InnerHTML,
"innerText": lo.InnerText,
"textContent": lo.TextContent,
"inputValue": lo.InputValue,
"selectOption": lo.SelectOption,
"press": lo.Press,
"type": lo.Type,
"hover": lo.Hover,
"tap": lo.Tap,
"dispatchEvent": lo.DispatchEvent,
"waitFor": lo.WaitFor,
"dblclick": lo.Dblclick,
"check": lo.Check,
"uncheck": lo.Uncheck,
"isChecked": lo.IsChecked,
"isEditable": lo.IsEditable,
"isEnabled": lo.IsEnabled,
"isDisabled": lo.IsDisabled,
"isVisible": lo.IsVisible,
"isHidden": lo.IsHidden,
"fill": lo.Fill,
"focus": lo.Focus,
"getAttribute": lo.GetAttribute,
"innerHTML": lo.InnerHTML,
"innerText": lo.InnerText,
"textContent": lo.TextContent,
"inputValue": lo.InputValue,
"selectOption": lo.SelectOption,
"press": lo.Press,
"type": lo.Type,
"hover": lo.Hover,
"tap": lo.Tap,
"dispatchEvent": func(typ string, eventInit, opts goja.Value) error {
popts := common.NewFrameDispatchEventOptions(lo.DefaultTimeout())
if err := popts.Parse(vu.Context(), opts); err != nil {
return fmt.Errorf("parsing locator dispatch event options: %w", err)
}
return lo.DispatchEvent(typ, eventInit.Export(), popts) //nolint:wrapcheck
},
"waitFor": lo.WaitFor,
}
}

Expand Down Expand Up @@ -249,21 +255,23 @@ func mapElementHandle(vu moduleVU, eh *common.ElementHandle) mapping {
}
return mapFrame(vu, f), nil
},
"dblclick": eh.Dblclick,
"dispatchEvent": eh.DispatchEvent,
"fill": eh.Fill,
"focus": eh.Focus,
"getAttribute": eh.GetAttribute,
"hover": eh.Hover,
"innerHTML": eh.InnerHTML,
"innerText": eh.InnerText,
"inputValue": eh.InputValue,
"isChecked": eh.IsChecked,
"isDisabled": eh.IsDisabled,
"isEditable": eh.IsEditable,
"isEnabled": eh.IsEnabled,
"isHidden": eh.IsHidden,
"isVisible": eh.IsVisible,
"dblclick": eh.Dblclick,
"dispatchEvent": func(typ string, eventInit goja.Value) error {
return eh.DispatchEvent(typ, eventInit.Export()) //nolint:wrapcheck
},
"fill": eh.Fill,
"focus": eh.Focus,
"getAttribute": eh.GetAttribute,
"hover": eh.Hover,
"innerHTML": eh.InnerHTML,
"innerText": eh.InnerText,
"inputValue": eh.InputValue,
"isChecked": eh.IsChecked,
"isDisabled": eh.IsDisabled,
"isEditable": eh.IsEditable,
"isEnabled": eh.IsEnabled,
"isHidden": eh.IsHidden,
"isVisible": eh.IsVisible,
"ownerFrame": func() (mapping, error) {
f, err := eh.OwnerFrame()
if err != nil {
Expand Down Expand Up @@ -355,9 +363,15 @@ func mapFrame(vu moduleVU, f *common.Frame) mapping {
return nil, err //nolint:wrapcheck
}), nil
},
"content": f.Content,
"dblclick": f.Dblclick,
"dispatchEvent": f.DispatchEvent,
"content": f.Content,
"dblclick": f.Dblclick,
"dispatchEvent": func(selector, typ string, eventInit, opts goja.Value) 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 f.DispatchEvent(selector, typ, eventInit.Export(), popts) //nolint:wrapcheck
},
"evaluate": func(pageFunction goja.Value, gargs ...goja.Value) any {
args := make([]any, 0, len(gargs))
for _, a := range gargs {
Expand Down Expand Up @@ -553,10 +567,16 @@ func mapPage(vu moduleVU, p *common.Page) mapping {

return p.Close(opts) //nolint:wrapcheck
},
"content": p.Content,
"context": p.Context,
"dblclick": p.Dblclick,
"dispatchEvent": p.DispatchEvent,
"content": p.Content,
"context": p.Context,
"dblclick": p.Dblclick,
"dispatchEvent": func(selector, typ string, eventInit, opts goja.Value) error {
popts := common.NewFrameDispatchEventOptions(p.Timeout())
if err := popts.Parse(vu.Context(), opts); err != nil {
return fmt.Errorf("parsing page dispatch event options: %w", err)
}
return p.DispatchEvent(selector, typ, eventInit.Export(), popts) //nolint:wrapcheck
},
"dragAndDrop": p.DragAndDrop,
"emulateMedia": p.EmulateMedia,
"emulateVisionDeficiency": p.EmulateVisionDeficiency,
Expand Down
10 changes: 7 additions & 3 deletions common/element_handle.go
Original file line number Diff line number Diff line change
Expand Up @@ -233,7 +233,7 @@ func (h *ElementHandle) defaultTimeout() time.Duration {
return h.frame.manager.timeoutSettings.timeout()
}

func (h *ElementHandle) dispatchEvent(_ context.Context, typ string, eventInit goja.Value) (any, error) {
func (h *ElementHandle) dispatchEvent(_ context.Context, typ string, eventInit any) (any, error) {
fn := `
(node, injected, type, eventInit) => {
injected.dispatchEvent(node, type, eventInit);
Expand Down Expand Up @@ -774,17 +774,21 @@ func (h *ElementHandle) Dblclick(opts goja.Value) {
applySlowMo(h.ctx)
}

func (h *ElementHandle) DispatchEvent(typ string, eventInit goja.Value) {
// DispatchEvent dispatches a DOM event to the element.
func (h *ElementHandle) DispatchEvent(typ string, eventInit any) error {
fn := func(apiCtx context.Context, handle *ElementHandle) (any, error) {
return handle.dispatchEvent(apiCtx, typ, eventInit)
}
opts := NewElementHandleBaseOptions(h.defaultTimeout())
actFn := h.newAction([]string{}, fn, opts.Force, opts.NoWaitAfter, opts.Timeout)
_, err := call(h.ctx, actFn, opts.Timeout)
if err != nil {
k6ext.Panic(h.ctx, "dispatching element event: %w", err)
return fmt.Errorf("dispatching element event %q: %w", typ, err)
}

applySlowMo(h.ctx)

return nil
}

func (h *ElementHandle) Fill(value string, opts goja.Value) {
Expand Down
14 changes: 6 additions & 8 deletions common/frame.go
Original file line number Diff line number Diff line change
Expand Up @@ -719,22 +719,20 @@ func (f *Frame) dblclick(selector string, opts *FrameDblclickOptions) error {
}

// DispatchEvent dispatches an event for the first element matching the selector.
func (f *Frame) DispatchEvent(selector, typ string, eventInit, opts goja.Value) {
func (f *Frame) DispatchEvent(selector, typ string, eventInit any, opts *FrameDispatchEventOptions) error {
f.log.Debugf("Frame:DispatchEvent", "fid:%s furl:%q sel:%q typ:%q", f.ID(), f.URL(), selector, typ)

popts := NewFrameDispatchEventOptions(f.defaultTimeout())
if err := popts.Parse(f.ctx, opts); err != nil {
k6ext.Panic(f.ctx, "parsing dispatch event options: %w", err)
}
if err := f.dispatchEvent(selector, typ, eventInit, popts); err != nil {
k6ext.Panic(f.ctx, "dispatching event %q to %q: %w", typ, selector, err)
if err := f.dispatchEvent(selector, typ, eventInit, opts); err != nil {
return fmt.Errorf("dispatching frame event %q to %q: %w", typ, selector, err)
}
applySlowMo(f.ctx)

return nil
}

// dispatchEvent is like DispatchEvent but takes parsed options and neither throws
// an error, or applies slow motion.
func (f *Frame) dispatchEvent(selector, typ string, eventInit goja.Value, opts *FrameDispatchEventOptions) error {
func (f *Frame) dispatchEvent(selector, typ string, eventInit any, opts *FrameDispatchEventOptions) error {
dispatchEvent := func(apiCtx context.Context, handle *ElementHandle) (any, error) {
return handle.dispatchEvent(apiCtx, typ, eventInit)
}
Expand Down
22 changes: 12 additions & 10 deletions common/locator.go
Original file line number Diff line number Diff line change
Expand Up @@ -585,7 +585,7 @@ func (l *Locator) tap(opts *FrameTapOptions) error {

// DispatchEvent dispatches an event for the element matching the
// locator's selector with strict mode on.
func (l *Locator) DispatchEvent(typ string, eventInit, opts goja.Value) {
func (l *Locator) DispatchEvent(typ string, eventInit any, opts *FrameDispatchEventOptions) error {
l.log.Debugf(
"Locator:DispatchEvent", "fid:%s furl:%q sel:%q typ:%q eventInit:%+v opts:%+v",
l.frame.ID(), l.frame.URL(), l.selector, typ, eventInit, opts,
Expand All @@ -594,18 +594,14 @@ func (l *Locator) DispatchEvent(typ string, eventInit, opts goja.Value) {
var err error
defer func() { panicOrSlowMo(l.ctx, err) }()

popts := NewFrameDispatchEventOptions(l.frame.defaultTimeout())
if err = popts.Parse(l.ctx, opts); err != nil {
err = fmt.Errorf("parsing dispatch event options: %w", err)
return
}
if err = l.dispatchEvent(typ, eventInit, popts); err != nil {
err = fmt.Errorf("dispatching event %q to %q: %w", typ, l.selector, err)
return
if err = l.dispatchEvent(typ, eventInit, opts); err != nil {
return fmt.Errorf("dispatching locator event %q to %q: %w", typ, l.selector, err)
}

return nil
}

func (l *Locator) dispatchEvent(typ string, eventInit goja.Value, opts *FrameDispatchEventOptions) error {
func (l *Locator) dispatchEvent(typ string, eventInit any, opts *FrameDispatchEventOptions) error {
opts.Strict = true
return l.frame.dispatchEvent(l.selector, typ, eventInit, opts)
}
Expand All @@ -627,3 +623,9 @@ func (l *Locator) waitFor(opts *FrameWaitForSelectorOptions) error {
opts.Strict = true
return l.frame.waitFor(l.selector, opts)
}

// DefaultTimeout returns the default timeout for the locator.
// This is an internal API and should not be used by users.
func (l *Locator) DefaultTimeout() time.Duration {
return l.frame.defaultTimeout()
}
5 changes: 3 additions & 2 deletions common/page.go
Original file line number Diff line number Diff line change
Expand Up @@ -716,10 +716,11 @@ func (p *Page) Dblclick(selector string, opts goja.Value) {
p.MainFrame().Dblclick(selector, opts)
}

func (p *Page) DispatchEvent(selector string, typ string, eventInit goja.Value, opts goja.Value) {
// DispatchEvent dispatches an event on the page to the element that matches the provided selector.
func (p *Page) DispatchEvent(selector string, typ string, eventInit any, opts *FrameDispatchEventOptions) error {
p.logger.Debugf("Page:DispatchEvent", "sid:%v selector:%s", p.sessionID(), selector)

p.MainFrame().DispatchEvent(selector, typ, eventInit, opts)
return p.MainFrame().DispatchEvent(selector, typ, eventInit, opts)
}

// DragAndDrop is not implemented.
Expand Down
8 changes: 4 additions & 4 deletions tests/launch_options_slowmo_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,6 @@ import (
"context"
"testing"

"github.com/dop251/goja"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"

Expand All @@ -13,7 +12,6 @@ import (

func TestBrowserOptionsSlowMo(t *testing.T) {
t.Parallel()
t.Skip("TODO: fix goja escape")

if testing.Short() {
t.Skip()
Expand Down Expand Up @@ -47,7 +45,8 @@ func TestBrowserOptionsSlowMo(t *testing.T) {
t.Parallel()
tb := newTestBrowser(t, withFileServer())
testPageSlowMoImpl(t, tb, func(_ *testBrowser, p *common.Page) {
p.DispatchEvent("button", "click", goja.Null(), nil)
err := p.DispatchEvent("button", "click", nil, common.NewFrameDispatchEventOptions(p.Timeout()))
require.NoError(t, err)
})
})
t.Run("emulateMedia", func(t *testing.T) {
Expand Down Expand Up @@ -195,7 +194,8 @@ func TestBrowserOptionsSlowMo(t *testing.T) {
t.Parallel()
tb := newTestBrowser(t, withFileServer())
testFrameSlowMoImpl(t, tb, func(_ *testBrowser, f *common.Frame) {
f.DispatchEvent("button", "click", goja.Null(), nil)
err := f.DispatchEvent("button", "click", nil, common.NewFrameDispatchEventOptions(f.Timeout()))
require.NoError(t, err)
})
})
t.Run("evaluate", func(t *testing.T) {
Expand Down
11 changes: 8 additions & 3 deletions tests/locator_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,6 @@ type jsFrameWaitForSelectorOpts struct {

func TestLocator(t *testing.T) {
t.Parallel()
t.Skip("TODO: fix goja escape")

tests := []struct {
name string
Expand Down Expand Up @@ -91,7 +90,9 @@ func TestLocator(t *testing.T) {
return asBool(t, v)
}
require.False(t, result(), "should not be clicked first")
p.Locator("#link", nil).DispatchEvent("click", tb.toGojaValue("mouseevent"), nil)
opts := common.NewFrameDispatchEventOptions(0) // no timeout
err := p.Locator("#link", nil).DispatchEvent("click", "mouseevent", opts)
require.NoError(t, err)
require.True(t, result(), "cannot not dispatch event")
},
},
Expand Down Expand Up @@ -280,7 +281,11 @@ func TestLocator(t *testing.T) {
},
{
"DispatchEvent", func(l *common.Locator, tb *testBrowser) {
l.DispatchEvent("click", tb.toGojaValue("mouseevent"), timeout(tb))
err := l.DispatchEvent("click", "mouseevent", common.NewFrameDispatchEventOptions(100*time.Millisecond))
if err != nil {
// TODO: remove panic and update tests when all locator methods return error.
panic(err)
}
},
},
{
Expand Down
Loading