From f85f3777fa42ead495885f8dab90534832c1830d Mon Sep 17 00:00:00 2001 From: Randy Cahyana Date: Sat, 25 Feb 2017 14:34:53 +0700 Subject: [PATCH] Fixing issue when node is offscreen and more. - Adding ScrollIntoNode action. - Adding isOnViewJS snippet. - Fixing race issue in some unit tests. --- input.go | 7 ++---- input_test.go | 52 ++++++++++++++++++++++++++++----------------- nav.go | 25 ++++++++++++++++++++++ query_test.go | 31 ++++++++++++++++++++++++++- testdata/input.html | 9 ++++---- util.go | 20 +++++++++++++++++ 6 files changed, 113 insertions(+), 31 deletions(-) diff --git a/input.go b/input.go index 64d43b7..5c578d1 100644 --- a/input.go +++ b/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) }) } diff --git a/input_test.go b/input_test.go index 773080a..638909e 100644 --- a/input_test.go +++ b/input_test.go @@ -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 { diff --git a/nav.go b/nav.go index 6904e11..61b82e2 100644 --- a/nav.go +++ b/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 + }) +} diff --git a/query_test.go b/query_test.go index 3fc948b..3126ea4 100644 --- a/query_test.go +++ b/query_test.go @@ -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. + }) + } +} diff --git a/testdata/input.html b/testdata/input.html index d9c717b..dd47200 100644 --- a/testdata/input.html +++ b/testdata/input.html @@ -1,6 +1,6 @@ - + @@ -11,7 +11,7 @@ - +