Fixing issue when node is offscreen and more.
- Adding ScrollIntoNode action. - Adding isOnViewJS snippet. - Fixing race issue in some unit tests.
This commit is contained in:
parent
d63d697c77
commit
f85f3777fa
7
input.go
7
input.go
|
@ -55,10 +55,10 @@ func MouseClickNode(n *cdp.Node, opts ...MouseOption) Action {
|
|||
return ActionFunc(func(ctxt context.Context, h cdp.Handler) error {
|
||||
var err error
|
||||
|
||||
/*err = dom.Focus(n.NodeID).Do(ctxt, h)
|
||||
err = ScrollIntoNode(n).Do(ctxt, h)
|
||||
if err != nil {
|
||||
return err
|
||||
}*/
|
||||
}
|
||||
|
||||
box, err := dom.GetBoxModel(n.NodeID).Do(ctxt, h)
|
||||
if err != nil {
|
||||
|
@ -78,9 +78,6 @@ func MouseClickNode(n *cdp.Node, opts ...MouseOption) Action {
|
|||
x /= int64(c / 2)
|
||||
y /= int64(c / 2)
|
||||
|
||||
/*var pos []int64
|
||||
err = EvaluateAsDevTools(fmt.Sprintf(scrollJS, x, y), &pos).Do(ctxt, h)*/
|
||||
|
||||
return MouseClickXY(x, y, opts...).Do(ctxt, h)
|
||||
})
|
||||
}
|
||||
|
|
|
@ -17,7 +17,7 @@ func TestMouseClickXY(t *testing.T) {
|
|||
defer c.Release()
|
||||
|
||||
var err error
|
||||
err = c.Run(defaultContext, Sleep(time.Millisecond*50))
|
||||
err = c.Run(defaultContext, Sleep(time.Millisecond*100))
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
@ -37,12 +37,14 @@ func TestMouseClickXY(t *testing.T) {
|
|||
t.Fatalf("test %d got error: %v", i, err)
|
||||
}
|
||||
|
||||
var value string
|
||||
err = c.Run(defaultContext, Value("#input1", &value, ByID))
|
||||
time.Sleep(time.Millisecond * 100)
|
||||
|
||||
var xstr, ystr string
|
||||
err = c.Run(defaultContext, Value("#input1", &xstr, ByID))
|
||||
if err != nil {
|
||||
t.Fatalf("test %d got error: %v", i, err)
|
||||
}
|
||||
x, err := strconv.ParseInt(value, 10, 64)
|
||||
x, err := strconv.ParseInt(xstr, 10, 64)
|
||||
if err != nil {
|
||||
t.Fatalf("test %d got error: %v", i, err)
|
||||
}
|
||||
|
@ -50,11 +52,11 @@ func TestMouseClickXY(t *testing.T) {
|
|||
t.Fatalf("test %d expected x to be: %d, got: %d", i, test.x, x)
|
||||
}
|
||||
|
||||
err = c.Run(defaultContext, Value("#input2", &value, ByID))
|
||||
err = c.Run(defaultContext, Value("#input2", &ystr, ByID))
|
||||
if err != nil {
|
||||
t.Fatalf("test %d got error: %v", i, err)
|
||||
}
|
||||
y, err := strconv.ParseInt(value, 10, 64)
|
||||
y, err := strconv.ParseInt(ystr, 10, 64)
|
||||
if err != nil {
|
||||
t.Fatalf("test %d got error: %v", i, err)
|
||||
}
|
||||
|
@ -66,18 +68,18 @@ func TestMouseClickXY(t *testing.T) {
|
|||
|
||||
func TestMouseClickNode(t *testing.T) {
|
||||
tests := []struct {
|
||||
exp string
|
||||
opt MouseOption
|
||||
by QueryOption
|
||||
sel, exp string
|
||||
opt MouseOption
|
||||
by QueryOption
|
||||
}{
|
||||
{"foo", ButtonType(input.ButtonNone), ByID},
|
||||
{"bar", ButtonType(input.ButtonLeft), ByID},
|
||||
{"bar-middle", ButtonType(input.ButtonMiddle), ByID},
|
||||
{"bar-right", ButtonType(input.ButtonRight), ByID},
|
||||
{"bar2", ClickCount(2), ByID},
|
||||
{"button2", "foo", ButtonType(input.ButtonNone), ByID},
|
||||
{"button2", "bar", ButtonType(input.ButtonLeft), ByID},
|
||||
{"button2", "bar-middle", ButtonType(input.ButtonMiddle), ByID},
|
||||
{"input3", "bar-right", ButtonType(input.ButtonRight), ByID},
|
||||
{"input3", "bar-right", ButtonModifiers(input.ModifierNone), ByID},
|
||||
{"input3", "bar-right", Button("right"), ByID},
|
||||
}
|
||||
|
||||
var err error
|
||||
for i, test := range tests {
|
||||
t.Run(fmt.Sprintf("test %d", i), func(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
@ -85,8 +87,9 @@ func TestMouseClickNode(t *testing.T) {
|
|||
c := testAllocate(t, "input.html")
|
||||
defer c.Release()
|
||||
|
||||
var err error
|
||||
var nodes []*cdp.Node
|
||||
err = c.Run(defaultContext, Nodes("#button2", &nodes, test.by))
|
||||
err = c.Run(defaultContext, Nodes(test.sel, &nodes, test.by))
|
||||
if err != nil {
|
||||
t.Fatalf("got error: %v", err)
|
||||
}
|
||||
|
@ -124,7 +127,6 @@ func TestMouseClickOffscreenNode(t *testing.T) {
|
|||
{"#button3", 10, ByID},
|
||||
}
|
||||
|
||||
var err error
|
||||
for i, test := range tests {
|
||||
t.Run(fmt.Sprintf("test %d", i), func(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
@ -132,6 +134,7 @@ func TestMouseClickOffscreenNode(t *testing.T) {
|
|||
c := testAllocate(t, "input.html")
|
||||
defer c.Release()
|
||||
|
||||
var err error
|
||||
var nodes []*cdp.Node
|
||||
err = c.Run(defaultContext, Nodes(test.sel, &nodes, test.by))
|
||||
if err != nil {
|
||||
|
@ -141,6 +144,15 @@ func TestMouseClickOffscreenNode(t *testing.T) {
|
|||
t.Fatalf("expected nodes to have exactly 1 element, got: %d", len(nodes))
|
||||
}
|
||||
|
||||
var ok bool
|
||||
err = c.Run(defaultContext, EvaluateAsDevTools(fmt.Sprintf(isOnViewJS, nodes[0].FullXPath()), &ok))
|
||||
if err != nil {
|
||||
t.Fatalf("got error: %v", err)
|
||||
}
|
||||
if ok {
|
||||
t.Fatal("expected node to be offscreen")
|
||||
}
|
||||
|
||||
for i := test.exp; i > 0; i-- {
|
||||
err = c.Run(defaultContext, MouseClickNode(nodes[0]))
|
||||
if err != nil {
|
||||
|
@ -148,7 +160,7 @@ func TestMouseClickOffscreenNode(t *testing.T) {
|
|||
}
|
||||
}
|
||||
|
||||
time.Sleep(time.Millisecond * 50)
|
||||
time.Sleep(time.Millisecond * 100)
|
||||
|
||||
var value int
|
||||
err = c.Run(defaultContext, Evaluate("window.document.test_i", &value))
|
||||
|
@ -175,7 +187,6 @@ func TestKeyAction(t *testing.T) {
|
|||
{"#input4", "\n\nfoo\n\nbar\n\n", ByID},
|
||||
}
|
||||
|
||||
var err error
|
||||
for i, test := range tests {
|
||||
t.Run(fmt.Sprintf("test %d", i), func(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
@ -183,6 +194,7 @@ func TestKeyAction(t *testing.T) {
|
|||
c := testAllocate(t, "input.html")
|
||||
defer c.Release()
|
||||
|
||||
var err error
|
||||
var nodes []*cdp.Node
|
||||
err = c.Run(defaultContext, Nodes(test.sel, &nodes, test.by))
|
||||
if err != nil {
|
||||
|
@ -227,7 +239,6 @@ func TestKeyActionNode(t *testing.T) {
|
|||
{"#input4", "\n\nfoo\n\nbar\n\n", ByID},
|
||||
}
|
||||
|
||||
var err error
|
||||
for i, test := range tests {
|
||||
t.Run(fmt.Sprintf("test %d", i), func(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
@ -235,6 +246,7 @@ func TestKeyActionNode(t *testing.T) {
|
|||
c := testAllocate(t, "input.html")
|
||||
defer c.Release()
|
||||
|
||||
var err error
|
||||
var nodes []*cdp.Node
|
||||
err = c.Run(defaultContext, Nodes(test.sel, &nodes, test.by))
|
||||
if err != nil {
|
||||
|
|
25
nav.go
25
nav.go
|
@ -3,6 +3,7 @@ package chromedp
|
|||
import (
|
||||
"context"
|
||||
"errors"
|
||||
"fmt"
|
||||
|
||||
"github.com/knq/chromedp/cdp"
|
||||
"github.com/knq/chromedp/cdp/page"
|
||||
|
@ -130,3 +131,27 @@ func Title(title *string) Action {
|
|||
|
||||
return EvaluateAsDevTools(`document.title`, title)
|
||||
}
|
||||
|
||||
// ScrollIntoNode scrolls the window to the specified node.
|
||||
func ScrollIntoNode(n *cdp.Node, opts ...QueryOption) Action {
|
||||
return ActionFunc(func(ctxt context.Context, h cdp.Handler) error {
|
||||
var res bool
|
||||
err := EvaluateAsDevTools(fmt.Sprintf(isOnViewJS, n.FullXPath()), &res).Do(ctxt, h)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if res {
|
||||
return nil
|
||||
}
|
||||
|
||||
err = EvaluateAsDevTools(fmt.Sprintf(scrollIntoViewJS, n.FullXPath()), &res).Do(ctxt, h)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if !res {
|
||||
return fmt.Errorf("could not scroll into node %d", n.NodeID)
|
||||
}
|
||||
|
||||
return nil
|
||||
})
|
||||
}
|
||||
|
|
|
@ -821,4 +821,33 @@ func TestComputedStyle(t *testing.T) {
|
|||
}
|
||||
}
|
||||
|
||||
// MatchedStyle
|
||||
func TestMatchedStyle(t *testing.T) {
|
||||
tests := []struct {
|
||||
sel string
|
||||
by QueryOption
|
||||
}{
|
||||
{`//*[@id="input1"]`, BySearch},
|
||||
{`body > input[type="number"]:nth-child(1)`, ByQueryAll},
|
||||
{`body > input[type="number"]:nth-child(1)`, ByQuery},
|
||||
{"#input1", ByID},
|
||||
}
|
||||
|
||||
for i, test := range tests {
|
||||
t.Run(fmt.Sprintf("test %d", i), func(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
c := testAllocate(t, "js.html")
|
||||
defer c.Release()
|
||||
|
||||
time.Sleep(time.Millisecond * 50)
|
||||
|
||||
var styles *css.GetMatchedStylesForNodeReturns
|
||||
err := c.Run(defaultContext, MatchedStyle(test.sel, &styles, test.by))
|
||||
if err != nil {
|
||||
t.Fatalf("got error: %v", err)
|
||||
}
|
||||
|
||||
// TODO: Add logic to check if the style returned is true and valid.
|
||||
})
|
||||
}
|
||||
}
|
||||
|
|
9
testdata/input.html
vendored
9
testdata/input.html
vendored
|
@ -1,6 +1,6 @@
|
|||
<!doctype html>
|
||||
<html>
|
||||
<body style="background-color: white">
|
||||
<body style="background-color: white;">
|
||||
<script>
|
||||
window.document.test_i = 0
|
||||
</script>
|
||||
|
@ -11,7 +11,7 @@
|
|||
<input id="input3" type="text" value="foo"/>
|
||||
<textarea id="input4"/></textarea>
|
||||
<input id="button2" type="button" value="button 2"/>
|
||||
<input id="button3" type="button" style="float: right;margin-left: 500px;" value="offscreen button" onclick="javascript:window.document.test_i++;return false;"/>
|
||||
<input id="button3" type="button" style="float: right;margin-right: -500px" value="offscreen button" onclick="javascript:window.document.test_i++;return false;"/>
|
||||
<script>
|
||||
document.addEventListener("click", function(){
|
||||
document.getElementById('input1').value = event.clientX;
|
||||
|
@ -26,9 +26,8 @@
|
|||
e.preventDefault();
|
||||
document.getElementById('input3').value = 'bar2';return false;
|
||||
}, false);
|
||||
document.getElementById('button2').addEventListener('contextmenu', function(e) {
|
||||
e.preventDefault();
|
||||
document.getElementById('input3').value = 'bar-right';return false;
|
||||
document.getElementById('input3').addEventListener('contextmenu', function(e) {
|
||||
this.value = 'bar-right';return false;
|
||||
}, false);
|
||||
document.getElementById('button2').addEventListener('auxclick', function(e) {
|
||||
e.preventDefault();
|
||||
|
|
20
util.go
20
util.go
|
@ -38,6 +38,26 @@ const (
|
|||
return true;
|
||||
})($x('%s'))`
|
||||
|
||||
// isOnViewJS is a javascript snippet that will get the specified node
|
||||
// position relative to the viewport and returns true or false depending
|
||||
// on if the specified node is on view port.
|
||||
isOnViewJS = `(function(a) {
|
||||
var rect = a[0].getBoundingClientRect();
|
||||
return (
|
||||
rect.top >= 0 &&
|
||||
rect.left >= 0 &&
|
||||
rect.bottom <= window.innerHeight &&
|
||||
rect.right <= window.innerWidth
|
||||
);
|
||||
})($x('%s'))`
|
||||
|
||||
// scrollIntoViewJS is a javascript snippet that scrolls the window to the
|
||||
// specified node.
|
||||
scrollIntoViewJS = `(function(a) {
|
||||
a[0].scrollIntoView(true);
|
||||
return true
|
||||
})($x('%s'))`
|
||||
|
||||
// scrollJS is a javascript snippet that scrolls the window to the
|
||||
// specified x, y coordinates and then returns the actual window x/y after
|
||||
// execution.
|
||||
|
|
Loading…
Reference in New Issue
Block a user