chromedp/query_test.go
Daniel Martí fb23c1750a fix data races in table-driven parallel subtests
t.Parallel effectively fires off a goroutine, so we can't use the test
range variable directly. That can result in different subtests using the
same test case data, causing sporadic failures, or some test cases
rarely being actually tested.

This was uncovered while stress-testing the test suite for an unrelated
refactor.

While at it, one test case in TestMouseClickNode was incorrect.
contextmenu fires on a right click, so ModifierNone won't fire it. This
wasn't caught before, as this test case was almost never ran. After the
data race fix, the test case failed consistently, before being fixed.
2019-04-01 16:48:49 +01:00

1107 lines
24 KiB
Go

package chromedp
import (
"bytes"
"fmt"
"image"
_ "image/png"
"io/ioutil"
"net/http"
"net/http/httptest"
"os"
"reflect"
"testing"
"github.com/chromedp/cdproto/cdp"
"github.com/chromedp/cdproto/css"
"github.com/chromedp/cdproto/dom"
"github.com/chromedp/cdproto/emulation"
"github.com/chromedp/chromedp/kb"
)
func TestNodes(t *testing.T) {
t.Parallel()
ctx, cancel := testAllocate(t, "table.html")
defer cancel()
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},
}
for i, test := range tests {
var nodes []*cdp.Node
if err := Run(ctx, Nodes(test.sel, &nodes, test.by)); 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))
}
}
}
func TestNodeIDs(t *testing.T) {
t.Parallel()
ctx, cancel := testAllocate(t, "table.html")
defer cancel()
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},
}
for i, test := range tests {
var ids []cdp.NodeID
if err := Run(ctx, NodeIDs(test.sel, &ids, test.by)); err != nil {
t.Fatal(err)
}
if len(ids) != test.len {
t.Errorf("test %d expected to have %d node id's: got %d", i, test.len, len(ids))
}
}
}
func TestFocusBlur(t *testing.T) {
t.Parallel()
ctx, cancel := testAllocate(t, "js.html")
defer cancel()
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},
}
err := Run(ctx, Click("#input1", ByID))
if err != nil {
t.Fatal(err)
}
for i, test := range tests {
if err := Run(ctx, Focus(test.sel, test.by)); err != nil {
t.Fatalf("test %d got error: %v", i, err)
}
var value string
if err := Run(ctx, Value(test.sel, &value, test.by)); err != nil {
t.Fatalf("test %d got error: %v", i, err)
}
if value != "9999" {
t.Errorf("test %d expected value is '9999', got: '%s'", i, value)
}
if err := Run(ctx, Blur(test.sel, test.by)); err != nil {
t.Fatalf("test %d got error: %v", i, err)
}
if err := Run(ctx, Value(test.sel, &value, test.by)); err != nil {
t.Fatalf("test %d got error: %v", i, err)
}
if value != "0" {
t.Errorf("test %d expected value is '0', got: '%s'", i, value)
}
}
}
func TestDimensions(t *testing.T) {
t.Parallel()
ctx, cancel := testAllocate(t, "image.html")
defer cancel()
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},
}
for i, test := range tests {
var model *dom.BoxModel
if err := Run(ctx, Dimensions(test.sel, &model)); 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) {
t.Parallel()
ctx, cancel := testAllocate(t, "form.html")
defer cancel()
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"},
}
for i, test := range tests {
var text string
if err := Run(ctx, Text(test.sel, &text, test.by)); 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) {
t.Parallel()
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},
}
for i, test := range tests {
t.Run(fmt.Sprintf("test %d", i), func(t *testing.T) {
test := test
t.Parallel()
ctx, cancel := testAllocate(t, "form.html")
defer cancel()
var val string
err := Run(ctx, Value(test.sel, &val, test.by))
if err != nil {
t.Fatalf("got error: %v", err)
}
if val == "" {
t.Errorf("expected `%s` to have non empty value", test.sel)
}
if err := Run(ctx, Clear(test.sel, test.by)); err != nil {
t.Fatalf("got error: %v", err)
}
if err := Run(ctx, Value(test.sel, &val, test.by)); err != nil {
t.Fatalf("got error: %v", err)
}
if val != "" {
t.Errorf("expected empty value for `%s`, got: %s", test.sel, val)
}
})
}
}
func TestReset(t *testing.T) {
t.Parallel()
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"},
}
for i, test := range tests {
t.Run(fmt.Sprintf("test %d", i), func(t *testing.T) {
test := test
t.Parallel()
ctx, cancel := testAllocate(t, "form.html")
defer cancel()
err := Run(ctx, SetValue(test.sel, test.value, test.by))
if err != nil {
t.Fatalf("got error: %v", err)
}
if err := Run(ctx, Reset(test.sel, test.by)); err != nil {
t.Fatalf("got error: %v", err)
}
var value string
if err := Run(ctx, Value(test.sel, &value, test.by)); err != nil {
t.Fatalf("got error: %v", err)
}
if value != test.exp {
t.Errorf("expected value after reset is %s, got: '%s'", test.exp, value)
}
})
}
}
func TestValue(t *testing.T) {
t.Parallel()
ctx, cancel := testAllocate(t, "form.html")
defer cancel()
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},
}
for i, test := range tests {
var value string
if err := Run(ctx, Value(test.sel, &value, test.by)); 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) {
t.Parallel()
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},
}
for i, test := range tests {
t.Run(fmt.Sprintf("test %d", i), func(t *testing.T) {
test := test
t.Parallel()
ctx, cancel := testAllocate(t, "form.html")
defer cancel()
err := Run(ctx, SetValue(test.sel, "FOOBAR", test.by))
if err != nil {
t.Fatalf("got error: %v", err)
}
var value string
if err := Run(ctx, Value(test.sel, &value, test.by)); err != nil {
t.Fatalf("got error: %v", err)
}
if value != "FOOBAR" {
t.Errorf("expected `FOOBAR`, got: %s", value)
}
})
}
}
func TestAttributes(t *testing.T) {
t.Parallel()
ctx, cancel := testAllocate(t, "image.html")
defer cancel()
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",
},
},
}
for i, test := range tests {
var attrs map[string]string
if err := Run(ctx, Attributes(test.sel, &attrs, test.by)); 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 TestAttributesAll(t *testing.T) {
t.Parallel()
ctx, cancel := testAllocate(t, "image.html")
defer cancel()
tests := []struct {
sel string
by QueryOption
exp []map[string]string
}{
{
"img", ByQueryAll,
[]map[string]string{
{
"alt": "Brankas - Easy Money Management",
"id": "icon-brankas",
"src": "images/brankas.png",
},
{
"alt": "How people build software",
"id": "icon-github",
"src": "images/github.png",
},
},
},
}
for i, test := range tests {
var attrs []map[string]string
if err := Run(ctx, AttributesAll(test.sel, &attrs, test.by)); 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) {
t.Parallel()
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",
},
},
}
for i, test := range tests {
t.Run(fmt.Sprintf("test %d", i), func(t *testing.T) {
test := test
t.Parallel()
ctx, cancel := testAllocate(t, "image.html")
defer cancel()
err := Run(ctx, SetAttributes(test.sel, test.attrs, test.by))
if err != nil {
t.Fatalf("got error: %v", err)
}
var attrs map[string]string
if err := Run(ctx, Attributes(test.sel, &attrs, test.by)); err != nil {
t.Fatalf("got error: %v", err)
}
if !reflect.DeepEqual(test.exp, attrs) {
t.Errorf("expected %v, got: %v", test.exp, attrs)
}
})
}
}
func TestAttributeValue(t *testing.T) {
t.Parallel()
ctx, cancel := testAllocate(t, "image.html")
defer cancel()
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"},
}
for i, test := range tests {
var value string
var ok bool
if err := Run(ctx, AttributeValue(test.sel, test.attr, &value, &ok, test.by)); 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) {
t.Parallel()
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"},
}
for i, test := range tests {
t.Run(fmt.Sprintf("test %d", i), func(t *testing.T) {
test := test
t.Parallel()
ctx, cancel := testAllocate(t, "form.html")
defer cancel()
err := Run(ctx, SetAttributeValue(test.sel, test.attr, test.exp, test.by))
if err != nil {
t.Fatalf("got error: %v", err)
}
var value string
var ok bool
if err := Run(ctx, AttributeValue(test.sel, test.attr, &value, &ok, test.by)); err != nil {
t.Fatalf("got error: %v", err)
}
if !ok {
t.Fatalf("failed to get attribute %s on %s", test.attr, test.sel)
}
if value != test.exp {
t.Errorf("expected %s to be %s, got: %s", test.attr, test.exp, value)
}
})
}
}
func TestRemoveAttribute(t *testing.T) {
t.Parallel()
tests := []struct {
sel string
by QueryOption
attr string
}{
{"/html/body/img", BySearch, "alt"},
{"img", ByQueryAll, "alt"},
{"img", ByQuery, "alt"},
{"#icon-github", ByID, "alt"},
}
for i, test := range tests {
t.Run(fmt.Sprintf("test %d", i), func(t *testing.T) {
test := test
t.Parallel()
ctx, cancel := testAllocate(t, "image.html")
defer cancel()
err := Run(ctx, RemoveAttribute(test.sel, test.attr))
if err != nil {
t.Fatalf("got error: %v", err)
}
var value string
var ok bool
if err := Run(ctx, AttributeValue(test.sel, test.attr, &value, &ok, test.by)); err != nil {
t.Fatalf("got error: %v", err)
}
if ok || value != "" {
t.Fatalf("expected attribute %s removed from element %s", test.attr, test.sel)
}
})
}
}
func TestClick(t *testing.T) {
t.Parallel()
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},
}
for i, test := range tests {
t.Run(fmt.Sprintf("test %d", i), func(t *testing.T) {
test := test
t.Parallel()
ctx, cancel := testAllocate(t, "form.html")
defer cancel()
err := Run(ctx, Click(test.sel, test.by))
if err != nil {
t.Fatalf("got error: %v", err)
}
if err := Run(ctx, WaitVisible("#icon-brankas", ByID)); err != nil {
t.Fatalf("got error: %v", err)
}
var title string
if err := Run(ctx, Title(&title)); err != nil {
t.Fatalf("got error: %v", err)
}
if title != "this is title" {
t.Errorf("expected title to be 'chromedp - Google Search', got: '%s'", title)
}
})
}
}
func TestDoubleClick(t *testing.T) {
t.Parallel()
tests := []struct {
sel string
by QueryOption
}{
{`/html/body/input[2]`, BySearch},
{`body > input[type="button"]:nth-child(2)`, ByQueryAll},
{`body > input[type="button"]:nth-child(2)`, ByQuery},
{`#button1`, ByID},
}
for i, test := range tests {
t.Run(fmt.Sprintf("test %d", i), func(t *testing.T) {
test := test
t.Parallel()
ctx, cancel := testAllocate(t, "js.html")
defer cancel()
err := Run(ctx, DoubleClick(test.sel, test.by))
if err != nil {
t.Fatalf("got error: %v", err)
}
var value string
if err := Run(ctx, Value("#input1", &value, ByID)); err != nil {
t.Fatalf("got error: %v", err)
}
if value != "1" {
t.Errorf("expected value to be '1', got: '%s'", value)
}
})
}
}
func TestSendKeys(t *testing.T) {
t.Parallel()
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"},
}
for i, test := range tests {
t.Run(fmt.Sprintf("test %d", i), func(t *testing.T) {
test := test
t.Parallel()
ctx, cancel := testAllocate(t, "visible.html")
defer cancel()
err := Run(ctx, SendKeys(test.sel, test.keys, test.by))
if err != nil {
t.Fatalf("got error: %v", err)
}
var val string
if err := Run(ctx, Value(test.sel, &val, test.by)); err != nil {
t.Fatalf("got error: %v", err)
}
if val != test.exp {
t.Errorf("expected value %s, got: %s", test.exp, val)
}
})
}
}
func TestScreenshot(t *testing.T) {
t.Parallel()
ctx, cancel := testAllocate(t, "image.html")
defer cancel()
tests := []struct {
sel string
by QueryOption
size int
}{
{"/html/body/img", BySearch, 239},
{"img", ByQueryAll, 239},
{"#icon-github", ByID, 120},
}
// a smaller viewport speeds up this test
width, height := 650, 450
if err := Run(ctx, emulation.SetDeviceMetricsOverride(
int64(width), int64(height), 1.0, false,
)); err != nil {
t.Fatal(err)
}
for i, test := range tests {
var buf []byte
if err := Run(ctx, Screenshot(test.sel, &buf)); err != nil {
t.Fatalf("test %d got error: %v", i, err)
}
if len(buf) == 0 {
t.Fatalf("test %d failed to capture screenshot", i)
}
config, format, err := image.DecodeConfig(bytes.NewReader(buf))
if err != nil {
t.Fatal(err)
}
if want := "png"; format != want {
t.Fatalf("expected format to be %q, got %q", want, format)
}
if config.Width != test.size || config.Height != test.size {
t.Fatalf("expected dimensions to be %d*%d, got %d*%d",
test.size, test.size, config.Width, config.Height)
}
}
}
func TestSubmit(t *testing.T) {
t.Parallel()
tests := []struct {
sel string
by QueryOption
}{
{`//*[@id="keyword"]`, BySearch},
{`#form > input[type="text"]:nth-child(4)`, ByQuery},
{`#form > input[type="text"]`, ByQueryAll},
{"#form", ByID},
}
for i, test := range tests {
t.Run(fmt.Sprintf("test %d", i), func(t *testing.T) {
test := test
t.Parallel()
ctx, cancel := testAllocate(t, "form.html")
defer cancel()
err := Run(ctx, Submit(test.sel, test.by))
if err != nil {
t.Fatalf("got error: %v", err)
}
if err := Run(ctx, WaitVisible("#icon-brankas", ByID)); err != nil {
t.Fatalf("got error: %v", err)
}
var title string
if err := Run(ctx, Title(&title)); err != nil {
t.Fatalf("got error: %v", err)
}
if title != "this is title" {
t.Errorf("expected title to be 'this is title', got: '%s'", title)
}
})
}
}
func TestComputedStyle(t *testing.T) {
t.Parallel()
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) {
test := test
t.Parallel()
ctx, cancel := testAllocate(t, "js.html")
defer cancel()
var styles []*css.ComputedProperty
err := Run(ctx, ComputedStyle(test.sel, &styles, test.by))
if err != nil {
t.Fatalf("got error: %v", err)
}
for _, style := range styles {
if style.Name == "background-color" {
if style.Value != "rgb(255, 0, 0)" {
t.Logf("expected style 'rgb(255, 0, 0)' got: %s", style.Value)
}
}
}
if err := Run(ctx, Click("#input1", ByID)); err != nil {
t.Fatalf("got error: %v", err)
}
if err := Run(ctx, ComputedStyle(test.sel, &styles, test.by)); err != nil {
t.Fatalf("got error: %v", err)
}
for _, style := range styles {
if style.Name == "background-color" {
if style.Value != "rgb(255, 255, 0)" {
t.Fatalf("expected style 'rgb(255, 255, 0)' got: %s", style.Value)
}
}
}
})
}
}
func TestMatchedStyle(t *testing.T) {
t.Parallel()
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) {
test := test
t.Parallel()
ctx, cancel := testAllocate(t, "js.html")
defer cancel()
var styles *css.GetMatchedStylesForNodeReturns
err := Run(ctx, 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.
})
}
}
func TestFileUpload(t *testing.T) {
t.Parallel()
// create test server
mux := http.NewServeMux()
mux.HandleFunc("/", func(res http.ResponseWriter, req *http.Request) {
fmt.Fprintf(res, "%s", uploadHTML)
})
mux.HandleFunc("/upload", func(res http.ResponseWriter, req *http.Request) {
f, _, err := req.FormFile("upload")
if err != nil {
http.Error(res, err.Error(), http.StatusBadRequest)
return
}
defer f.Close()
buf, err := ioutil.ReadAll(f)
if err != nil {
http.Error(res, err.Error(), http.StatusBadRequest)
return
}
fmt.Fprintf(res, resultHTML, len(buf))
})
s := httptest.NewServer(mux)
defer s.Close()
// create temporary file on disk
tmpfile, err := ioutil.TempFile("", "chromedp-upload-test")
if err != nil {
t.Fatal(err)
}
defer os.Remove(tmpfile.Name())
defer tmpfile.Close()
if _, err := tmpfile.WriteString(uploadHTML); err != nil {
t.Fatal(err)
}
if err := tmpfile.Close(); err != nil {
t.Fatal(err)
}
tests := []struct {
a Action
}{
{SendKeys(`input[name="upload"]`, tmpfile.Name(), NodeVisible)},
{SetUploadFiles(`input[name="upload"]`, []string{tmpfile.Name()}, NodeVisible)},
}
for i, test := range tests {
t.Run(fmt.Sprintf("test %d", i), func(t *testing.T) {
// TODO: refactor the test so the subtests can run in
// parallel
//t.Parallel()
ctx, cancel := testAllocate(t, "")
defer cancel()
var result string
if err := Run(ctx, Tasks{
Navigate(s.URL),
test.a,
Click(`input[name="submit"]`),
Text(`#result`, &result, ByID, NodeVisible),
}); err != nil {
t.Fatalf("test %d expected no error, got: %v", i, err)
}
if result != fmt.Sprintf("%d", len(uploadHTML)) {
t.Errorf("test %d expected result to be %d, got: %s", i, len(uploadHTML), result)
}
})
}
}
func TestInnerHTML(t *testing.T) {
t.Parallel()
ctx, cancel := testAllocate(t, "table.html")
defer cancel()
tests := []struct {
sel string
by QueryOption
}{
{"/html/body/table/thead", BySearch},
{"thead", ByQueryAll},
{"thead", ByQuery},
}
for i, test := range tests {
var html string
if err := Run(ctx, InnerHTML(test.sel, &html)); err != nil {
t.Fatalf("test %d got error: %v", i, err)
}
if html == "" {
t.Fatalf("test %d: InnerHTML is empty", i)
}
}
}
func TestOuterHTML(t *testing.T) {
t.Parallel()
ctx, cancel := testAllocate(t, "table.html")
defer cancel()
tests := []struct {
sel string
by QueryOption
}{
{"/html/body/table/thead/tr", BySearch},
{"thead tr", ByQueryAll},
{"thead tr", ByQuery},
}
for i, test := range tests {
var html string
if err := Run(ctx, OuterHTML(test.sel, &html)); err != nil {
t.Fatalf("test %d got error: %v", i, err)
}
if html == "" {
t.Fatalf("test %d: OuterHTML is empty", i)
}
}
}
func TestScrollIntoView(t *testing.T) {
t.Parallel()
ctx, cancel := testAllocate(t, "image.html")
defer cancel()
tests := []struct {
sel string
by QueryOption
}{
{"/html/body/img", BySearch},
{"img", ByQueryAll},
{"img", ByQuery},
{"#icon-github", ByID},
}
for i, test := range tests {
if err := Run(ctx, ScrollIntoView(test.sel, test.by)); err != nil {
t.Fatalf("test %d got error: %v", i, err)
}
// TODO test scroll event
}
}
const (
uploadHTML = `<!doctype html>
<html>
<body>
<form method="POST" action="/upload" enctype="multipart/form-data">
<input name="upload" type="file"/>
<input name="submit" type="submit"/>
</form>
</body>
</html>`
resultHTML = `<!doctype html>
<html>
<body>
<div id="result">%d</div>
</body>
</html>`
)