diff --git a/chromedp.go b/chromedp.go index 1baf97b..3490d70 100644 --- a/chromedp.go +++ b/chromedp.go @@ -116,14 +116,14 @@ func (c *CDP) AddTarget(ctxt context.Context, t client.Target) { // create target manager h, err := NewTargetHandler(t, c.logf, c.debugf, c.errorf) if err != nil { - c.errorf("could not create handler for %s, got: %v", t, err) + c.errorf("could not create handler for %s: %v", t, err) return } // run err = h.Run(ctxt) if err != nil { - c.errorf("could not start handler for %s, got: %v", t, err) + c.errorf("could not start handler for %s: %v", t, err) return } diff --git a/chromedp_test.go b/chromedp_test.go index 1aac60e..40d8ef6 100644 --- a/chromedp_test.go +++ b/chromedp_test.go @@ -4,18 +4,63 @@ import ( "context" "log" "os" - "strings" "testing" ) var pool *Pool - var defaultContext = context.Background() +var testdataDir string + +func testAllocate(t *testing.T, path string) *Res { + c, err := pool.Allocate(defaultContext) + if err != nil { + t.Fatalf("could not allocate from pool: %v", err) + } + + err = WithLogf(t.Logf)(c.c) + if err != nil { + t.Fatalf("could not set logf: %v", err) + } + + err = WithDebugf(t.Logf)(c.c) + if err != nil { + t.Fatalf("could not set debugf: %v", err) + } + + err = WithErrorf(t.Errorf)(c.c) + if err != nil { + t.Fatalf("could not set errorf: %v", err) + } + + h := c.c.GetHandlerByIndex(0) + th, ok := h.(*TargetHandler) + if !ok { + t.Fatalf("handler is invalid type") + } + + th.logf = t.Logf + th.debugf = t.Logf + th.errorf = func(s string, v ...interface{}) { + t.Logf("target handler error: "+s, v...) + } + + if path != "" { + err = c.Run(defaultContext, Navigate(testdataDir+"/"+path)) + if err != nil { + t.Fatalf("could not navigate to testdata/%s: %v", path, err) + } + } + + return c +} func TestMain(m *testing.M) { var err error - pool, err = NewPool(PoolLog(log.Printf, log.Printf, log.Printf)) + testdataDir = "file:" + os.Getenv("GOPATH") + "/src/github.com/knq/chromedp/testdata" + + //pool, err = NewPool(PoolLog(log.Printf, log.Printf, log.Printf)) + pool, err = NewPool() if err != nil { log.Fatal(err) } @@ -29,51 +74,3 @@ func TestMain(m *testing.M) { os.Exit(code) } - -func TestNavigate(t *testing.T) { - var err error - - c, err := pool.Allocate(defaultContext) - if err != nil { - t.Fatal(err) - } - defer c.Release() - - err = c.Run(defaultContext, Navigate("https://www.google.com/")) - if err != nil { - t.Fatal(err) - } - - err = c.Run(defaultContext, WaitVisible(`#hplogo`, ByID)) - if err != nil { - t.Fatal(err) - } - - var urlstr string - err = c.Run(defaultContext, Location(&urlstr)) - if err != nil { - t.Fatal(err) - } - if !strings.HasPrefix(urlstr, "https://www.google.") { - t.Errorf("expected to be on google domain, at: %s", urlstr) - } - - var title string - err = c.Run(defaultContext, Title(&title)) - if err != nil { - t.Fatal(err) - } - if !strings.Contains(strings.ToLower(title), "google") { - t.Errorf("expected title to contain google, instead title is: %s", title) - } -} - -func TestSendKeys(t *testing.T) { - var err error - - c, err := pool.Allocate(defaultContext) - if err != nil { - t.Fatal(err) - } - defer c.Release() -} diff --git a/handler.go b/handler.go index 18bf06d..3872bf3 100644 --- a/handler.go +++ b/handler.go @@ -50,7 +50,7 @@ type TargetHandler struct { lastm sync.Mutex // res is the id->result channel map. - res map[int64]chan easyjson.RawMessage + res map[int64]chan *cdp.Message resrw sync.RWMutex // logging funcs @@ -87,7 +87,7 @@ func (h *TargetHandler) Run(ctxt context.Context) error { h.qcmd = make(chan *cdp.Message) h.qres = make(chan *cdp.Message) h.qevents = make(chan *cdp.Message) - h.res = make(map[int64]chan easyjson.RawMessage) + h.res = make(map[int64]chan *cdp.Message) h.detached = make(chan *inspector.EventDetached) h.pageWaitGroup = new(sync.WaitGroup) h.domWaitGroup = new(sync.WaitGroup) @@ -108,7 +108,7 @@ func (h *TargetHandler) Run(ctxt context.Context) error { } { err = a.Do(ctxt, h) if err != nil { - return fmt.Errorf("unable to execute %s, got: %v", reflect.TypeOf(a), err) + return fmt.Errorf("unable to execute %s: %v", reflect.TypeOf(a), err) } } @@ -117,7 +117,7 @@ func (h *TargetHandler) Run(ctxt context.Context) error { // get page resources tree, err := page.GetResourceTree().Do(ctxt, h) if err != nil { - return fmt.Errorf("unable to get resource tree, got: %v", err) + return fmt.Errorf("unable to get resource tree: %v", err) } h.frames[tree.Frame.ID] = tree.Frame @@ -182,19 +182,19 @@ func (h *TargetHandler) run(ctxt context.Context) { case ev := <-h.qevents: err = h.processEvent(ctxt, ev) if err != nil { - h.errorf("could not process event %s, got: %v", ev.Method, err) + h.errorf("could not process event %s: %v", ev.Method, err) } case res := <-h.qres: err = h.processResult(res) if err != nil { - h.errorf("could not process result for message %d, got: %v", res.ID, err) + h.errorf("could not process result for message %d: %v", res.ID, err) } case cmd := <-h.qcmd: err = h.processCommand(cmd) if err != nil { - h.errorf("could not process command message %d, got: %v", cmd.ID, err) + h.errorf("could not process command message %d: %v", cmd.ID, err) } case <-ctxt.Done(): @@ -271,7 +271,7 @@ func (h *TargetHandler) processEvent(ctxt context.Context, msg *cdp.Message) err func (h *TargetHandler) documentUpdated(ctxt context.Context) { f, err := h.WaitFrame(ctxt, EmptyFrameID) if err != nil { - h.errorf("could not get current frame, got: %v", err) + h.errorf("could not get current frame: %v", err) return } @@ -286,7 +286,7 @@ func (h *TargetHandler) documentUpdated(ctxt context.Context) { f.Nodes = make(map[cdp.NodeID]*cdp.Node) f.Root, err = dom.GetDocument().WithPierce(true).Do(ctxt, h) if err != nil { - h.errorf("could not retrieve document root for %s, got: %v", f.ID, err) + h.errorf("could not retrieve document root for %s: %v", f.ID, err) return } f.Root.Invalidated = make(chan struct{}) @@ -304,11 +304,7 @@ func (h *TargetHandler) processResult(msg *cdp.Message) error { } defer close(ch) - if msg.Error != nil { - return msg.Error - } - - ch <- msg.Result + ch <- msg return nil } @@ -346,7 +342,7 @@ func (h *TargetHandler) Execute(ctxt context.Context, commandType cdp.MethodType id := h.next() // save channel - ch := make(chan easyjson.RawMessage, 1) + ch := make(chan *cdp.Message, 1) h.resrw.Lock() h.res[id] = ch h.resrw.Unlock() @@ -364,8 +360,15 @@ func (h *TargetHandler) Execute(ctxt context.Context, commandType cdp.MethodType select { case msg := <-ch: - if res != nil { - errch <- easyjson.Unmarshal(msg, res) + switch { + case msg == nil: + errch <- cdp.ErrChannelClosed + + case msg.Error != nil: + errch <- msg.Error + + case res != nil: + errch <- easyjson.Unmarshal(msg.Result, res) } case <-ctxt.Done(): @@ -559,7 +562,7 @@ func (h *TargetHandler) pageEvent(ctxt context.Context, ev interface{}) { f, err := h.WaitFrame(ctxt, id) if err != nil { - h.errorf("could not get frame %s, got: %v", id, err) + h.errorf("could not get frame %s: %v", id, err) return } @@ -579,7 +582,7 @@ func (h *TargetHandler) domEvent(ctxt context.Context, ev interface{}) { // wait current frame f, err := h.WaitFrame(ctxt, EmptyFrameID) if err != nil { - h.errorf("error processing DOM event %s: error waiting for frame, got: %v", reflect.TypeOf(ev), err) + h.errorf("could not process DOM event %s: %v", reflect.TypeOf(ev), err) return } @@ -643,12 +646,15 @@ func (h *TargetHandler) domEvent(ctxt context.Context, ev interface{}) { return } - s := strings.TrimPrefix(strings.TrimSuffix(runtime.FuncForPC(reflect.ValueOf(op).Pointer()).Name(), ".func1"), "github.com/knq/chromedp.") - // retrieve node n, err := h.WaitNode(ctxt, f, id) if err != nil { - h.errorf("error could not perform (%s) operation on node %d (wait node error), got: %v", s, id, err) + s := strings.TrimSuffix(runtime.FuncForPC(reflect.ValueOf(op).Pointer()).Name(), ".func1") + i := strings.LastIndex(s, ".") + if i != -1 { + s = s[i+1:] + } + h.errorf("could not perform (%s) operation on node %d (wait node): %v", s, id, err) return } diff --git a/nav_test.go b/nav_test.go new file mode 100644 index 0000000..9b938f4 --- /dev/null +++ b/nav_test.go @@ -0,0 +1,41 @@ +package chromedp + +import ( + "strings" + "testing" +) + +func TestNavigate(t *testing.T) { + var err error + + c := testAllocate(t, "") + defer c.Release() + + err = c.Run(defaultContext, Navigate("https://www.google.com/")) + if err != nil { + t.Fatal(err) + } + + err = c.Run(defaultContext, WaitVisible(`#hplogo`, ByID)) + if err != nil { + t.Fatal(err) + } + + var urlstr string + err = c.Run(defaultContext, Location(&urlstr)) + if err != nil { + t.Fatal(err) + } + if !strings.HasPrefix(urlstr, "https://www.google.") { + t.Errorf("expected to be on google domain, at: %s", urlstr) + } + + var title string + err = c.Run(defaultContext, Title(&title)) + if err != nil { + t.Fatal(err) + } + if !strings.Contains(strings.ToLower(title), "google") { + t.Errorf("expected title to contain google, instead title is: %s", title) + } +} diff --git a/query_test.go b/query_test.go new file mode 100644 index 0000000..cbab270 --- /dev/null +++ b/query_test.go @@ -0,0 +1,602 @@ +package chromedp + +import ( + "reflect" + "testing" + "time" + + "github.com/knq/chromedp/cdp" + "github.com/knq/chromedp/cdp/dom" + "github.com/knq/chromedp/kb" +) + +func TestNodes(t *testing.T) { + c := testAllocate(t, "table.html") + defer c.Release() + + tests := []struct { + sel string + by QueryOption + len int + }{ + {"/html/body/table/tbody[1]/tr[2]/td", BySearch, 3}, + {"body > table > tbody:nth-child(2) > tr:nth-child(2) > td:not(:last-child)", ByQueryAll, 2}, + {"body > table > tbody:nth-child(2) > tr:nth-child(2) > td", ByQuery, 1}, + {"#footer", ByID, 1}, + } + + var err error + for i, test := range tests { + var nodes []*cdp.Node + err = c.Run(defaultContext, Nodes(test.sel, &nodes, test.by)) + if err != nil { + t.Fatalf("test %d got error: %v", i, err) + } + if len(nodes) != test.len { + t.Errorf("test %d expected to have %d nodes: got %d", i, test.len, len(nodes)) + } + } +} + +// TODO: +// NodeIDs +// Focus +// Blur + +func TestDimensions(t *testing.T) { + c := testAllocate(t, "image.html") + defer c.Release() + + tests := []struct { + sel string + by QueryOption + width int64 + height int64 + }{ + {"/html/body/img", BySearch, 239, 239}, + {"img", ByQueryAll, 239, 239}, + {"img", ByQuery, 239, 239}, + {"#icon-github", ByID, 120, 120}, + } + + var err error + for i, test := range tests { + var model *dom.BoxModel + err = c.Run(defaultContext, Dimensions(test.sel, &model)) + if err != nil { + t.Fatalf("test %d got error: %v", i, err) + } + if model.Height != test.height || model.Width != test.width { + t.Errorf("test %d expected %dx%d, got: %dx%d", i, test.width, test.height, model.Height, model.Width) + } + } +} + +func TestText(t *testing.T) { + c := testAllocate(t, "form.html") + defer c.Release() + + tests := []struct { + sel string + by QueryOption + exp string + }{ + {"#foo", ByID, "insert"}, + {"body > form > span", ByQueryAll, "insert"}, + {"body > form > span:nth-child(2)", ByQuery, "keyword"}, + {"/html/body/form/span[2]", BySearch, "keyword"}, + } + + var err error + for i, test := range tests { + var text string + err = c.Run(defaultContext, Text(test.sel, &text, test.by)) + if err != nil { + t.Fatalf("test %d got error: %v", i, err) + } + if text != test.exp { + t.Errorf("test %d expected `%s`, got: %s", i, test.exp, text) + } + } +} + +func TestClear(t *testing.T) { + tests := []struct { + sel string + by QueryOption + }{ + // input fields + {`//*[@id="form"]/input[1]`, BySearch}, + {`#form > input[type="text"]:nth-child(4)`, ByQuery}, + {`#form > input[type="text"]`, ByQueryAll}, + {`#keyword`, ByID}, + + // textarea fields + {`//*[@id="bar"]`, BySearch}, + {`#form > textarea`, ByQuery}, + {`#form > textarea`, ByQueryAll}, + {`#bar`, ByID}, + + // input + textarea fields + {`//*[@id="form"]/input`, BySearch}, + {`#form > input[type="text"]`, ByQueryAll}, + } + + var err error + for i, test := range tests { + c := testAllocate(t, "form.html") + defer c.Release() + + var val string + err = c.Run(defaultContext, Value(test.sel, &val, test.by)) + if err != nil { + t.Fatalf("test %d got error: %v", i, err) + } + if val == "" { + t.Errorf("test %d expected `%s` to have non empty value", i, test.sel) + } + + err = c.Run(defaultContext, Clear(test.sel, test.by)) + if err != nil { + t.Fatalf("test %d got error: %v", i, err) + } + + err = c.Run(defaultContext, Value(test.sel, &val, test.by)) + if err != nil { + t.Fatalf("test %d got error: %v", i, err) + } + if val != "" { + t.Errorf("test %d expected empty value for `%s`, got: %s", i, test.sel, val) + } + } +} + +func TestReset(t *testing.T) { + tests := []struct { + sel string + by QueryOption + value string + exp string + }{ + {`//*[@id="keyword"]`, BySearch, "foobar", "chromedp"}, + {`#form > input[type="text"]:nth-child(6)`, ByQuery, "foobar", "foo"}, + {`#form > input[type="text"]`, ByQueryAll, "foobar", "chromedp"}, + {"#bar", ByID, "foobar", "bar"}, + } + + var err error + for i, test := range tests { + c := testAllocate(t, "form.html") + defer c.Release() + + err = c.Run(defaultContext, SetValue(test.sel, test.value, test.by)) + if err != nil { + t.Fatalf("test %d got error: %v", i, err) + } + + err = c.Run(defaultContext, Reset(test.sel, test.by)) + if err != nil { + t.Fatalf("test %d got error: %v", i, err) + } + + var value string + err = c.Run(defaultContext, Value(test.sel, &value, test.by)) + if err != nil { + t.Fatalf("test %d got error: %v", i, err) + } + if value != test.exp { + t.Errorf("test %d expected value after reset is %s, got: '%s'", i, test.exp, value) + } + } +} + +func TestValue(t *testing.T) { + c := testAllocate(t, "form.html") + defer c.Release() + + tests := []struct { + sel string + by QueryOption + }{ + {`//*[@id="form"]/input[1]`, BySearch}, + {`#form > input[type="text"]:nth-child(4)`, ByQuery}, + {`#form > input[type="text"]`, ByQueryAll}, + {`#keyword`, ByID}, + } + + var err error + for i, test := range tests { + var value string + err = c.Run(defaultContext, Value(test.sel, &value, test.by)) + if err != nil { + t.Fatalf("test %d got error: %v", i, err) + } + if value != "chromedp" { + t.Errorf("test %d expected `chromedp`, got: %s", i, value) + } + } +} + +func TestSetValue(t *testing.T) { + c := testAllocate(t, "form.html") + defer c.Release() + + tests := []struct { + sel string + by QueryOption + }{ + {`//*[@id="form"]/input[1]`, BySearch}, + {`#form > input[type="text"]:nth-child(4)`, ByQuery}, + {`#form > input[type="text"]`, ByQueryAll}, + {`#bar`, ByID}, + } + + var err error + for i, test := range tests { + if i != 0 { + err = c.Run(defaultContext, Reload()) + if err != nil { + t.Fatalf("test %d got error: %v", i, err) + } + time.Sleep(50 * time.Millisecond) + } + + err = c.Run(defaultContext, SetValue(test.sel, "FOOBAR", test.by)) + if err != nil { + t.Fatalf("test %d got error: %v", i, err) + } + + var value string + err = c.Run(defaultContext, Value(test.sel, &value, test.by)) + if err != nil { + t.Fatalf("test %d got error: %v", i, err) + } + if value != "FOOBAR" { + t.Errorf("test %d expected `FOOBAR`, got: %s", i, value) + } + } +} + +func TestAttributes(t *testing.T) { + c := testAllocate(t, "image.html") + defer c.Release() + + tests := []struct { + sel string + by QueryOption + exp map[string]string + }{ + {`//*[@id="icon-brankas"]`, BySearch, + map[string]string{ + "alt": "Brankas - Easy Money Management", + "id": "icon-brankas", + "src": "images/brankas.png", + }}, + {"body > img:first-child", ByQuery, + map[string]string{ + "alt": "Brankas - Easy Money Management", + "id": "icon-brankas", + "src": "images/brankas.png", + }}, + {"body > img:nth-child(2)", ByQueryAll, + map[string]string{ + "alt": `How people build software`, + "id": "icon-github", + "src": "images/github.png", + }}, + {"#icon-github", ByID, + map[string]string{ + "alt": "How people build software", + "id": "icon-github", + "src": "images/github.png", + }}, + } + + var err error + for i, test := range tests { + var attrs map[string]string + err = c.Run(defaultContext, Attributes(test.sel, &attrs, test.by)) + if err != nil { + t.Fatalf("test %d got error: %v", i, err) + } + + if !reflect.DeepEqual(test.exp, attrs) { + t.Errorf("test %d expected %v, got: %v", i, test.exp, attrs) + } + } +} + +func TestSetAttributes(t *testing.T) { + tests := []struct { + sel string + by QueryOption + attrs map[string]string + exp map[string]string + }{ + {`//*[@id="icon-brankas"]`, BySearch, + map[string]string{"data-url": "brankas"}, + map[string]string{ + "alt": "Brankas - Easy Money Management", + "id": "icon-brankas", + "src": "images/brankas.png", + "data-url": "brankas"}}, + {"body > img:first-child", ByQuery, + map[string]string{"data-url": "brankas"}, + map[string]string{ + "alt": "Brankas - Easy Money Management", + "id": "icon-brankas", + "src": "images/brankas.png", + "data-url": "brankas", + }}, + {"body > img:nth-child(2)", ByQueryAll, + map[string]string{"width": "100", "height": "200"}, + map[string]string{ + "alt": `How people build software`, + "id": "icon-github", + "src": "images/github.png", + "width": "100", + "height": "200", + }}, + {"#icon-github", ByID, + map[string]string{"width": "100", "height": "200"}, + map[string]string{ + "alt": "How people build software", + "id": "icon-github", + "src": "images/github.png", + "width": "100", + "height": "200", + }}, + } + + var err error + for i, test := range tests { + c := testAllocate(t, "image.html") + defer c.Release() + + err = c.Run(defaultContext, SetAttributes(test.sel, test.attrs, test.by)) + if err != nil { + t.Fatalf("test %d got error: %v", i, err) + } + + var attrs map[string]string + err = c.Run(defaultContext, Attributes(test.sel, &attrs, test.by)) + if err != nil { + t.Fatalf("test %d got error: %v", i, err) + } + + if !reflect.DeepEqual(test.exp, attrs) { + t.Errorf("test %d expected %v, got: %v", i, test.exp, attrs) + } + } +} + +func TestAttributeValue(t *testing.T) { + c := testAllocate(t, "image.html") + defer c.Release() + + tests := []struct { + sel string + by QueryOption + attr string + exp string + }{ + {`//*[@id="icon-brankas"]`, BySearch, "alt", "Brankas - Easy Money Management"}, + {"body > img:first-child", ByQuery, "alt", "Brankas - Easy Money Management"}, + {"body > img:nth-child(2)", ByQueryAll, "alt", "How people build software"}, + {"#icon-github", ByID, "alt", "How people build software"}, + } + + var err error + for i, test := range tests { + var value string + var ok bool + + err = c.Run(defaultContext, AttributeValue(test.sel, test.attr, &value, &ok, test.by)) + if err != nil { + t.Fatalf("test %d got error: %v", i, err) + } + + if !ok { + t.Fatalf("test %d failed to get attribute %s on %s", i, test.attr, test.sel) + } + + if value != test.exp { + t.Errorf("test %d expected %s to be %s, got: %s", i, test.attr, test.exp, value) + } + } +} + +func TestSetAttributeValue(t *testing.T) { + tests := []struct { + sel string + by QueryOption + attr string + exp string + }{ + {`//*[@id="keyword"]`, BySearch, "foo", "bar"}, + {`#form > input[type="text"]:nth-child(6)`, ByQuery, "foo", "bar"}, + {`#form > input[type="text"]`, ByQueryAll, "foo", "bar"}, + {"#bar", ByID, "foo", "bar"}, + } + + var err error + for i, test := range tests { + c := testAllocate(t, "form.html") + defer c.Release() + + err = c.Run(defaultContext, SetAttributeValue(test.sel, test.attr, test.exp, test.by)) + if err != nil { + t.Fatalf("test %d got error: %v", i, err) + } + + var value string + var ok bool + err = c.Run(defaultContext, AttributeValue(test.sel, test.attr, &value, &ok, test.by)) + if err != nil { + t.Fatalf("test %d got error: %v", i, err) + } + if !ok { + t.Fatalf("test %d failed to get attribute %s on %s", i, test.attr, test.sel) + } + if value != test.exp { + t.Errorf("test %d expected %s to be %s, got: %s", i, test.attr, test.exp, value) + } + } +} + +func TestRemoveAttribute(t *testing.T) { + c := testAllocate(t, "image.html") + defer c.Release() + + tests := []struct { + sel string + by QueryOption + attr string + }{ + {"/html/body/img", BySearch, "alt"}, + {"img", ByQueryAll, "alt"}, + {"img", ByQuery, "alt"}, + {"#icon-github", ByID, "alt"}, + } + + var err error + for i, test := range tests { + if i != 0 { + err = c.Run(defaultContext, Reload()) + if err != nil { + t.Fatalf("test %d got error: %v", i, err) + } + time.Sleep(50 * time.Millisecond) + } + + err = c.Run(defaultContext, RemoveAttribute(test.sel, test.attr)) + if err != nil { + t.Fatalf("test %d got error: %v", i, err) + } + + var value string + var ok bool + err = c.Run(defaultContext, AttributeValue(test.sel, test.attr, &value, &ok, test.by)) + if err != nil { + t.Fatalf("test %d got error: %v", i, err) + } + if ok || value != "" { + t.Fatalf("test %d expected attribute %s removed from element %s", i, test.attr, test.sel) + } + } +} + +func TestClick(t *testing.T) { + tests := []struct { + sel string + by QueryOption + }{ + {`//*[@id="form"]/input[4]`, BySearch}, + {`#form > input[type="submit"]:nth-child(11)`, ByQuery}, + {`#form > input[type="submit"]:nth-child(11)`, ByQueryAll}, + {"#btn2", ByID}, + } + + var err error + for i, test := range tests { + c := testAllocate(t, "form.html") + defer c.Release() + + err = c.Run(defaultContext, Click(test.sel, test.by)) + if err != nil { + t.Fatalf("test %d got error: %v", i, err) + } + + err = c.Run(defaultContext, Sleep(time.Second*2)) + if err != nil { + t.Fatalf("test %d got error: %v", i, err) + } + + var title string + err = c.Run(defaultContext, Title(&title)) + if err != nil { + t.Fatalf("test %d got error: %v", i, err) + } + if title != "chromedp - Google Search" { + t.Errorf("test %d expected title to be 'chromedp - Google Search', got: '%s'", i, title) + } + } +} + +// DoubleClick + +func TestSendKeys(t *testing.T) { + tests := []struct { + sel string + by QueryOption + keys string + exp string + }{ + {`//*[@id="input1"]`, BySearch, "INSERT ", "INSERT some value"}, + {`#box4 > input:nth-child(1)`, ByQuery, "insert ", "insert some value"}, + {`#box4 > textarea`, ByQueryAll, "prefix " + kb.End + "\b\b SUFFIX\n", "prefix textar SUFFIX\n"}, + {"#textarea1", ByID, "insert ", "insert textarea"}, + {"#textarea1", ByID, kb.End + "\b\b\n\naoeu\n\nfoo\n\nbar\n\n", "textar\n\naoeu\n\nfoo\n\nbar\n\n"}, + {"#select1", ByID, kb.ArrowDown + kb.ArrowDown, "three"}, + } + + var err error + for i, test := range tests { + c := testAllocate(t, "visible.html") + defer c.Release() + + err = c.Run(defaultContext, SendKeys(test.sel, test.keys, test.by)) + if err != nil { + t.Fatalf("test %d got error: %v", i, err) + } + + var val string + err = c.Run(defaultContext, Value(test.sel, &val, test.by)) + if err != nil { + t.Fatalf("test %d got error: %v", i, err) + } + if val != test.exp { + t.Errorf("test %d expected value %s, got: %s", i, test.exp, val) + } + } +} + +func TestSubmit(t *testing.T) { + tests := []struct { + sel string + by QueryOption + }{ + {`//*[@id="keyword"]`, BySearch}, + {`#form > input[type="text"]:nth-child(4)`, ByQuery}, + {`#form > input[type="text"]`, ByQueryAll}, + {"#form", ByID}, + } + + var err error + for i, test := range tests { + c := testAllocate(t, "form.html") + defer c.Release() + + err = c.Run(defaultContext, Submit(test.sel, test.by)) + if err != nil { + t.Fatalf("test %d got error: %v", i, err) + } + + err = c.Run(defaultContext, Sleep(time.Second*2)) + if err != nil { + t.Fatalf("test %d got error: %v", i, err) + } + + var title string + err = c.Run(defaultContext, Title(&title)) + if err != nil { + t.Fatalf("test %d got error: %v", i, err) + } + if title != "chromedp - Google Search" { + t.Errorf("test %d expected title to be 'chromedp - Google Search', got: '%s'", i, title) + } + } +} + +// ComputedStyle +// MatchedStyle diff --git a/testdata/form.html b/testdata/form.html new file mode 100644 index 0000000..715b467 --- /dev/null +++ b/testdata/form.html @@ -0,0 +1,20 @@ + + + + + + +
+ insert keyword
+
+
+
+ + +
+ + \ No newline at end of file diff --git a/testdata/image.html b/testdata/image.html new file mode 100644 index 0000000..910c9b5 --- /dev/null +++ b/testdata/image.html @@ -0,0 +1,7 @@ + + + + Brankas - Easy Money Management + How people build software + + \ No newline at end of file diff --git a/testdata/images/brankas.png b/testdata/images/brankas.png new file mode 100644 index 0000000..59a8f71 Binary files /dev/null and b/testdata/images/brankas.png differ diff --git a/testdata/images/github.png b/testdata/images/github.png new file mode 100644 index 0000000..ea6ff54 Binary files /dev/null and b/testdata/images/github.png differ diff --git a/testdata/table.html b/testdata/table.html new file mode 100644 index 0000000..bb43b4a --- /dev/null +++ b/testdata/table.html @@ -0,0 +1,34 @@ + + + + table + + + + + + + + + + + + + + + + + + + + + + + + + + + +
row 1 headerrow 2 headerrow 3 header
1.11.21.3
2.12.22.3
+ + \ No newline at end of file