From b0c7efcd8ec8b2450a6b017a2f6ef4b98cbad70c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C4=B0nan=C3=A7=20G=C3=BCm=C3=BC=C5=9F?= Date: Thu, 25 Jan 2024 14:16:37 +0300 Subject: [PATCH] Refactor away goja from dispatch event This is a necessary fix to remove passing Goja values to ExecutionContext. --- browser/mapping.go | 110 +++++++++++++++++++++--------------- common/element_handle.go | 10 +++- common/execution_context.go | 1 + common/frame.go | 14 ++--- common/locator.go | 22 ++++---- common/page.go | 5 +- tests/locator_test.go | 9 ++- 7 files changed, 100 insertions(+), 71 deletions(-) diff --git a/browser/mapping.go b/browser/mapping.go index e941d8157..656e03bff 100644 --- a/browser/mapping.go +++ b/browser/mapping.go @@ -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, } } @@ -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 { @@ -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 { @@ -518,10 +532,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, diff --git a/common/element_handle.go b/common/element_handle.go index 4bab34b84..8e51a492f 100644 --- a/common/element_handle.go +++ b/common/element_handle.go @@ -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); @@ -774,7 +774,8 @@ 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) } @@ -782,9 +783,12 @@ func (h *ElementHandle) DispatchEvent(typ string, eventInit goja.Value) { 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) { diff --git a/common/execution_context.go b/common/execution_context.go index 6c8e00c44..3354b0c5e 100644 --- a/common/execution_context.go +++ b/common/execution_context.go @@ -303,6 +303,7 @@ func (e *ExecutionContext) Eval(apiCtx context.Context, js string, args ...any) for _, a := range args { evalArgs = append(evalArgs, a) } + return e.eval(apiCtx, opts, js, evalArgs...) } diff --git a/common/frame.go b/common/frame.go index 07880731c..154d13a31 100644 --- a/common/frame.go +++ b/common/frame.go @@ -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) } diff --git a/common/locator.go b/common/locator.go index f00a33d7b..20255c8d7 100644 --- a/common/locator.go +++ b/common/locator.go @@ -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, @@ -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) } @@ -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() +} diff --git a/common/page.go b/common/page.go index 444db29e2..a33a87ef5 100644 --- a/common/page.go +++ b/common/page.go @@ -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. diff --git a/tests/locator_test.go b/tests/locator_test.go index dc79dee9d..0703b8611 100644 --- a/tests/locator_test.go +++ b/tests/locator_test.go @@ -26,7 +26,6 @@ type jsFrameWaitForSelectorOpts struct { func TestLocator(t *testing.T) { t.Parallel() - t.Skip("TODO: fix goja escape") tests := []struct { name string @@ -91,7 +90,11 @@ 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.FrameDispatchEventOptions{ + FrameBaseOptions: &common.FrameBaseOptions{}, + } + err := p.Locator("#link", nil).DispatchEvent("click", "mouseevent", opts) + require.NoError(t, err) require.True(t, result(), "cannot not dispatch event") }, }, @@ -280,7 +283,7 @@ func TestLocator(t *testing.T) { }, { "DispatchEvent", func(l *common.Locator, tb *testBrowser) { - l.DispatchEvent("click", tb.toGojaValue("mouseevent"), timeout(tb)) + l.DispatchEvent("click", "mouseevent", common.NewFrameDispatchEventOptions(100*time.Millisecond)) }, }, {