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 {
|
return ActionFunc(func(ctxt context.Context, h cdp.Handler) error {
|
||||||
var err error
|
var err error
|
||||||
|
|
||||||
/*err = dom.Focus(n.NodeID).Do(ctxt, h)
|
err = ScrollIntoNode(n).Do(ctxt, h)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}*/
|
}
|
||||||
|
|
||||||
box, err := dom.GetBoxModel(n.NodeID).Do(ctxt, h)
|
box, err := dom.GetBoxModel(n.NodeID).Do(ctxt, h)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
@ -78,9 +78,6 @@ func MouseClickNode(n *cdp.Node, opts ...MouseOption) Action {
|
||||||
x /= int64(c / 2)
|
x /= int64(c / 2)
|
||||||
y /= 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)
|
return MouseClickXY(x, y, opts...).Do(ctxt, h)
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
|
@ -17,7 +17,7 @@ func TestMouseClickXY(t *testing.T) {
|
||||||
defer c.Release()
|
defer c.Release()
|
||||||
|
|
||||||
var err error
|
var err error
|
||||||
err = c.Run(defaultContext, Sleep(time.Millisecond*50))
|
err = c.Run(defaultContext, Sleep(time.Millisecond*100))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
}
|
}
|
||||||
|
@ -37,12 +37,14 @@ func TestMouseClickXY(t *testing.T) {
|
||||||
t.Fatalf("test %d got error: %v", i, err)
|
t.Fatalf("test %d got error: %v", i, err)
|
||||||
}
|
}
|
||||||
|
|
||||||
var value string
|
time.Sleep(time.Millisecond * 100)
|
||||||
err = c.Run(defaultContext, Value("#input1", &value, ByID))
|
|
||||||
|
var xstr, ystr string
|
||||||
|
err = c.Run(defaultContext, Value("#input1", &xstr, ByID))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatalf("test %d got error: %v", i, err)
|
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 {
|
if err != nil {
|
||||||
t.Fatalf("test %d got error: %v", i, err)
|
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)
|
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 {
|
if err != nil {
|
||||||
t.Fatalf("test %d got error: %v", i, err)
|
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 {
|
if err != nil {
|
||||||
t.Fatalf("test %d got error: %v", i, err)
|
t.Fatalf("test %d got error: %v", i, err)
|
||||||
}
|
}
|
||||||
|
@ -66,18 +68,18 @@ func TestMouseClickXY(t *testing.T) {
|
||||||
|
|
||||||
func TestMouseClickNode(t *testing.T) {
|
func TestMouseClickNode(t *testing.T) {
|
||||||
tests := []struct {
|
tests := []struct {
|
||||||
exp string
|
sel, exp string
|
||||||
opt MouseOption
|
opt MouseOption
|
||||||
by QueryOption
|
by QueryOption
|
||||||
}{
|
}{
|
||||||
{"foo", ButtonType(input.ButtonNone), ByID},
|
{"button2", "foo", ButtonType(input.ButtonNone), ByID},
|
||||||
{"bar", ButtonType(input.ButtonLeft), ByID},
|
{"button2", "bar", ButtonType(input.ButtonLeft), ByID},
|
||||||
{"bar-middle", ButtonType(input.ButtonMiddle), ByID},
|
{"button2", "bar-middle", ButtonType(input.ButtonMiddle), ByID},
|
||||||
{"bar-right", ButtonType(input.ButtonRight), ByID},
|
{"input3", "bar-right", ButtonType(input.ButtonRight), ByID},
|
||||||
{"bar2", ClickCount(2), ByID},
|
{"input3", "bar-right", ButtonModifiers(input.ModifierNone), ByID},
|
||||||
|
{"input3", "bar-right", Button("right"), ByID},
|
||||||
}
|
}
|
||||||
|
|
||||||
var err error
|
|
||||||
for i, test := range tests {
|
for i, test := range tests {
|
||||||
t.Run(fmt.Sprintf("test %d", i), func(t *testing.T) {
|
t.Run(fmt.Sprintf("test %d", i), func(t *testing.T) {
|
||||||
t.Parallel()
|
t.Parallel()
|
||||||
|
@ -85,8 +87,9 @@ func TestMouseClickNode(t *testing.T) {
|
||||||
c := testAllocate(t, "input.html")
|
c := testAllocate(t, "input.html")
|
||||||
defer c.Release()
|
defer c.Release()
|
||||||
|
|
||||||
|
var err error
|
||||||
var nodes []*cdp.Node
|
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 {
|
if err != nil {
|
||||||
t.Fatalf("got error: %v", err)
|
t.Fatalf("got error: %v", err)
|
||||||
}
|
}
|
||||||
|
@ -124,7 +127,6 @@ func TestMouseClickOffscreenNode(t *testing.T) {
|
||||||
{"#button3", 10, ByID},
|
{"#button3", 10, ByID},
|
||||||
}
|
}
|
||||||
|
|
||||||
var err error
|
|
||||||
for i, test := range tests {
|
for i, test := range tests {
|
||||||
t.Run(fmt.Sprintf("test %d", i), func(t *testing.T) {
|
t.Run(fmt.Sprintf("test %d", i), func(t *testing.T) {
|
||||||
t.Parallel()
|
t.Parallel()
|
||||||
|
@ -132,6 +134,7 @@ func TestMouseClickOffscreenNode(t *testing.T) {
|
||||||
c := testAllocate(t, "input.html")
|
c := testAllocate(t, "input.html")
|
||||||
defer c.Release()
|
defer c.Release()
|
||||||
|
|
||||||
|
var err error
|
||||||
var nodes []*cdp.Node
|
var nodes []*cdp.Node
|
||||||
err = c.Run(defaultContext, Nodes(test.sel, &nodes, test.by))
|
err = c.Run(defaultContext, Nodes(test.sel, &nodes, test.by))
|
||||||
if err != nil {
|
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))
|
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-- {
|
for i := test.exp; i > 0; i-- {
|
||||||
err = c.Run(defaultContext, MouseClickNode(nodes[0]))
|
err = c.Run(defaultContext, MouseClickNode(nodes[0]))
|
||||||
if err != nil {
|
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
|
var value int
|
||||||
err = c.Run(defaultContext, Evaluate("window.document.test_i", &value))
|
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},
|
{"#input4", "\n\nfoo\n\nbar\n\n", ByID},
|
||||||
}
|
}
|
||||||
|
|
||||||
var err error
|
|
||||||
for i, test := range tests {
|
for i, test := range tests {
|
||||||
t.Run(fmt.Sprintf("test %d", i), func(t *testing.T) {
|
t.Run(fmt.Sprintf("test %d", i), func(t *testing.T) {
|
||||||
t.Parallel()
|
t.Parallel()
|
||||||
|
@ -183,6 +194,7 @@ func TestKeyAction(t *testing.T) {
|
||||||
c := testAllocate(t, "input.html")
|
c := testAllocate(t, "input.html")
|
||||||
defer c.Release()
|
defer c.Release()
|
||||||
|
|
||||||
|
var err error
|
||||||
var nodes []*cdp.Node
|
var nodes []*cdp.Node
|
||||||
err = c.Run(defaultContext, Nodes(test.sel, &nodes, test.by))
|
err = c.Run(defaultContext, Nodes(test.sel, &nodes, test.by))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
@ -227,7 +239,6 @@ func TestKeyActionNode(t *testing.T) {
|
||||||
{"#input4", "\n\nfoo\n\nbar\n\n", ByID},
|
{"#input4", "\n\nfoo\n\nbar\n\n", ByID},
|
||||||
}
|
}
|
||||||
|
|
||||||
var err error
|
|
||||||
for i, test := range tests {
|
for i, test := range tests {
|
||||||
t.Run(fmt.Sprintf("test %d", i), func(t *testing.T) {
|
t.Run(fmt.Sprintf("test %d", i), func(t *testing.T) {
|
||||||
t.Parallel()
|
t.Parallel()
|
||||||
|
@ -235,6 +246,7 @@ func TestKeyActionNode(t *testing.T) {
|
||||||
c := testAllocate(t, "input.html")
|
c := testAllocate(t, "input.html")
|
||||||
defer c.Release()
|
defer c.Release()
|
||||||
|
|
||||||
|
var err error
|
||||||
var nodes []*cdp.Node
|
var nodes []*cdp.Node
|
||||||
err = c.Run(defaultContext, Nodes(test.sel, &nodes, test.by))
|
err = c.Run(defaultContext, Nodes(test.sel, &nodes, test.by))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
|
25
nav.go
25
nav.go
|
@ -3,6 +3,7 @@ package chromedp
|
||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
"errors"
|
"errors"
|
||||||
|
"fmt"
|
||||||
|
|
||||||
"github.com/knq/chromedp/cdp"
|
"github.com/knq/chromedp/cdp"
|
||||||
"github.com/knq/chromedp/cdp/page"
|
"github.com/knq/chromedp/cdp/page"
|
||||||
|
@ -130,3 +131,27 @@ func Title(title *string) Action {
|
||||||
|
|
||||||
return EvaluateAsDevTools(`document.title`, title)
|
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>
|
<!doctype html>
|
||||||
<html>
|
<html>
|
||||||
<body style="background-color: white">
|
<body style="background-color: white;">
|
||||||
<script>
|
<script>
|
||||||
window.document.test_i = 0
|
window.document.test_i = 0
|
||||||
</script>
|
</script>
|
||||||
|
@ -11,7 +11,7 @@
|
||||||
<input id="input3" type="text" value="foo"/>
|
<input id="input3" type="text" value="foo"/>
|
||||||
<textarea id="input4"/></textarea>
|
<textarea id="input4"/></textarea>
|
||||||
<input id="button2" type="button" value="button 2"/>
|
<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>
|
<script>
|
||||||
document.addEventListener("click", function(){
|
document.addEventListener("click", function(){
|
||||||
document.getElementById('input1').value = event.clientX;
|
document.getElementById('input1').value = event.clientX;
|
||||||
|
@ -26,9 +26,8 @@
|
||||||
e.preventDefault();
|
e.preventDefault();
|
||||||
document.getElementById('input3').value = 'bar2';return false;
|
document.getElementById('input3').value = 'bar2';return false;
|
||||||
}, false);
|
}, false);
|
||||||
document.getElementById('button2').addEventListener('contextmenu', function(e) {
|
document.getElementById('input3').addEventListener('contextmenu', function(e) {
|
||||||
e.preventDefault();
|
this.value = 'bar-right';return false;
|
||||||
document.getElementById('input3').value = 'bar-right';return false;
|
|
||||||
}, false);
|
}, false);
|
||||||
document.getElementById('button2').addEventListener('auxclick', function(e) {
|
document.getElementById('button2').addEventListener('auxclick', function(e) {
|
||||||
e.preventDefault();
|
e.preventDefault();
|
||||||
|
|
20
util.go
20
util.go
|
@ -38,6 +38,26 @@ const (
|
||||||
return true;
|
return true;
|
||||||
})($x('%s'))`
|
})($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
|
// scrollJS is a javascript snippet that scrolls the window to the
|
||||||
// specified x, y coordinates and then returns the actual window x/y after
|
// specified x, y coordinates and then returns the actual window x/y after
|
||||||
// execution.
|
// execution.
|
||||||
|
|
Loading…
Reference in New Issue
Block a user