diff --git a/common/mouse.go b/common/mouse.go index afab7aba7..450cc850a 100644 --- a/common/mouse.go +++ b/common/mouse.go @@ -2,13 +2,14 @@ package common import ( "context" + "fmt" "time" - "github.com/grafana/xk6-browser/k6ext" - "github.com/chromedp/cdproto/cdp" "github.com/chromedp/cdproto/input" "github.com/dop251/goja" + + "github.com/grafana/xk6-browser/k6ext" ) // Mouse represents a mouse input device. @@ -41,7 +42,7 @@ func (m *Mouse) click(x float64, y float64, opts *MouseClickOptions) error { return err } for i := 0; i < int(mouseDownUpOpts.ClickCount); i++ { - if err := m.down(x, y, mouseDownUpOpts); err != nil { + if err := m.down(mouseDownUpOpts); err != nil { return err } if opts.Delay != 0 { @@ -52,28 +53,29 @@ func (m *Mouse) click(x float64, y float64, opts *MouseClickOptions) error { case <-t.C: } } - if err := m.up(x, y, mouseDownUpOpts); err != nil { + if err := m.up(mouseDownUpOpts); err != nil { return err } } + return nil } -func (m *Mouse) down(x float64, y float64, opts *MouseDownUpOptions) error { +func (m *Mouse) down(opts *MouseDownUpOptions) error { m.button = input.MouseButton(opts.Button) action := input.DispatchMouseEvent(input.MousePressed, m.x, m.y). WithButton(input.MouseButton(opts.Button)). WithModifiers(input.Modifier(m.keyboard.modifiers)). WithClickCount(opts.ClickCount) if err := action.Do(cdp.WithExecutor(m.ctx, m.session)); err != nil { - return err + return fmt.Errorf("mouse down: %w", err) } return nil } func (m *Mouse) move(x float64, y float64, opts *MouseMoveOptions) error { - var fromX float64 = m.x - var fromY float64 = m.y + fromX := m.x + fromY := m.y m.x = x m.y = y for i := int64(1); i <= opts.Steps; i++ { @@ -83,21 +85,23 @@ func (m *Mouse) move(x float64, y float64, opts *MouseMoveOptions) error { WithButton(m.button). WithModifiers(input.Modifier(m.keyboard.modifiers)) if err := action.Do(cdp.WithExecutor(m.ctx, m.session)); err != nil { - return err + return fmt.Errorf("mouse move: %w", err) } } + return nil } -func (m *Mouse) up(x float64, y float64, opts *MouseDownUpOptions) error { +func (m *Mouse) up(opts *MouseDownUpOptions) error { m.button = input.None action := input.DispatchMouseEvent(input.MouseReleased, m.x, m.y). WithButton(input.MouseButton(opts.Button)). WithModifiers(input.Modifier(m.keyboard.modifiers)). WithClickCount(opts.ClickCount) if err := action.Do(cdp.WithExecutor(m.ctx, m.session)); err != nil { - return err + return fmt.Errorf("mouse up: %w", err) } + return nil } @@ -112,6 +116,7 @@ func (m *Mouse) Click(x float64, y float64, opts goja.Value) { } } +// DblClick will trigger Click twice in quick succession. func (m *Mouse) DblClick(x float64, y float64, opts goja.Value) { mouseOpts := NewMouseDblClickOptions() if err := mouseOpts.Parse(m.ctx, opts); err != nil { @@ -123,35 +128,35 @@ func (m *Mouse) DblClick(x float64, y float64, opts goja.Value) { } // Down will trigger a MouseDown event in the browser. -func (m *Mouse) Down(x float64, y float64, opts goja.Value) { +func (m *Mouse) Down(opts goja.Value) { mouseOpts := NewMouseDownUpOptions() if err := mouseOpts.Parse(m.ctx, opts); err != nil { k6ext.Panic(m.ctx, "parsing mouse down options: %w", err) } - if err := m.down(x, y, mouseOpts); err != nil { - k6ext.Panic(m.ctx, "pressing the mouse button on x:%f y:%f: %w", x, y, err) + if err := m.down(mouseOpts); err != nil { + k6ext.Panic(m.ctx, "pressing the mouse button on x:%f y:%f: %w", m.x, m.y, err) } } // Move will trigger a MouseMoved event in the browser. func (m *Mouse) Move(x float64, y float64, opts goja.Value) { - mouseOpts := NewMouseDownUpOptions() + mouseOpts := NewMouseMoveOptions() if err := mouseOpts.Parse(m.ctx, opts); err != nil { k6ext.Panic(m.ctx, "parsing mouse move options: %w", err) } - if err := m.down(x, y, mouseOpts); err != nil { + if err := m.move(x, y, mouseOpts); err != nil { k6ext.Panic(m.ctx, "moving the mouse pointer to x:%f y:%f: %w", x, y, err) } } // Up will trigger a MouseUp event in the browser. -func (m *Mouse) Up(x float64, y float64, opts goja.Value) { +func (m *Mouse) Up(opts goja.Value) { mouseOpts := NewMouseDownUpOptions() if err := mouseOpts.Parse(m.ctx, opts); err != nil { k6ext.Panic(m.ctx, "parsing mouse up options: %w", err) } - if err := m.up(x, y, mouseOpts); err != nil { - k6ext.Panic(m.ctx, "releasing the mouse button on x:%f y:%f: %w", x, y, err) + if err := m.up(mouseOpts); err != nil { + k6ext.Panic(m.ctx, "releasing the mouse button on x:%f y:%f: %w", m.x, m.y, err) } } diff --git a/tests/mouse_test.go b/tests/mouse_test.go index 1f0ab8e23..2458f9592 100644 --- a/tests/mouse_test.go +++ b/tests/mouse_test.go @@ -5,30 +5,115 @@ import ( "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" - - "github.com/grafana/xk6-browser/common" ) -func TestMouseDblClick(t *testing.T) { +func TestMouseActions(t *testing.T) { t.Parallel() - b := newTestBrowser(t, withFileServer()) - p := b.NewPage(nil) - opts := &common.FrameGotoOptions{ - Timeout: common.DefaultTimeout, - } - _, err := p.Goto( - b.staticURL("dbl_click.html"), - opts, - ) - require.NoError(t, err) - - p.Mouse.DblClick(35, 17, nil) - - v := p.Evaluate(`() => window.dblclick`) - bv := asBool(t, v) - assert.True(t, bv, "failed to double click the link") - - got := p.InnerText("#counter", nil) - assert.Equal(t, "2", got) + t.Run("click", func(t *testing.T) { + t.Parallel() + + tb := newTestBrowser(t) + p := tb.NewPage(nil) + m := p.GetMouse() + + // Set up a page with a button that changes text when clicked + buttonHTML := ` + + ` + p.SetContent(buttonHTML, nil) + button, err := p.Query("button") + require.NoError(t, err) + + // Simulate a click at the button coordinates + box := button.BoundingBox() + m.Click(box.X, box.Y, nil) + + // Verify the button's text changed + assert.Equal(t, "Clicked!", button.TextContent()) + }) + + t.Run("double_click", func(t *testing.T) { + t.Parallel() + + tb := newTestBrowser(t) + p := tb.NewPage(nil) + m := p.GetMouse() + + // Set up a page with a button that changes text on double click and also counts clicks + buttonHTML := ` + + +
+ ` + p.SetContent(buttonHTML, nil) + button, err := p.Query("button") + require.NoError(t, err) + + // Get the button's bounding box for accurate clicking + box := button.BoundingBox() + + // Simulate a double click at the button coordinates + m.DblClick(box.X, box.Y, nil) + + // Verify the button's text changed + assert.Equal(t, "Double Clicked!", button.TextContent()) + + // Also verify that the element was clicked twice + clickCountDiv, err := p.Query("div#clicks") + require.NoError(t, err) + assert.Equal(t, "2", clickCountDiv.TextContent()) + }) + + t.Run("move", func(t *testing.T) { + t.Parallel() + + tb := newTestBrowser(t) + p := tb.NewPage(nil) + m := p.GetMouse() + + // Set up a page with an area that detects mouse move + areaHTML := ` + + ` + p.SetContent(areaHTML, nil) + area, err := p.Query("div") + require.NoError(t, err) + + // Simulate mouse move within the div + box := area.BoundingBox() + m.Move(box.X+50, box.Y+50, nil) // Move to the center of the div + assert.Equal(t, "Mouse Moved", area.TextContent()) + }) + + t.Run("move_down_up", func(t *testing.T) { + t.Parallel() + + tb := newTestBrowser(t) + p := tb.NewPage(nil) + m := p.GetMouse() + + // Set up a page with a button that tracks mouse down and up + buttonHTML := ` + + ` + p.SetContent(buttonHTML, nil) + button, err := p.Query("button") + require.NoError(t, err) + + box := button.BoundingBox() + m.Move(box.X, box.Y, nil) + m.Down(nil) + assert.Equal(t, "Mouse Down", button.TextContent()) + m.Up(nil) + assert.Equal(t, "Mouse Up", button.TextContent()) + }) } diff --git a/tests/static/dbl_click.html b/tests/static/dbl_click.html deleted file mode 100644 index 02d7cf3e1..000000000 --- a/tests/static/dbl_click.html +++ /dev/null @@ -1,26 +0,0 @@ - - - - -