Adding SetUploadFiles and changing SendKeys behavior
Added high level action SetUploadFiles to set the upload files for a input[type="file"] node, and modified SendKeys to recognize the nodes. Additionally, added unit test for both, and updated the examples/upload/main.go to use the SendKeys variant.
This commit is contained in:
parent
0b9790e5a8
commit
df490a3025
|
@ -10,8 +10,6 @@ import (
|
||||||
"os"
|
"os"
|
||||||
|
|
||||||
cdp "github.com/knq/chromedp"
|
cdp "github.com/knq/chromedp"
|
||||||
cdptypes "github.com/knq/chromedp/cdp"
|
|
||||||
"github.com/knq/chromedp/cdp/dom"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
var (
|
var (
|
||||||
|
@ -97,14 +95,9 @@ func main() {
|
||||||
}
|
}
|
||||||
|
|
||||||
func upload(filepath string, sz *string) cdp.Tasks {
|
func upload(filepath string, sz *string) cdp.Tasks {
|
||||||
var ids []cdptypes.NodeID
|
|
||||||
return cdp.Tasks{
|
return cdp.Tasks{
|
||||||
cdp.Navigate(fmt.Sprintf("http://localhost:%d", *flagPort)),
|
cdp.Navigate(fmt.Sprintf("http://localhost:%d", *flagPort)),
|
||||||
cdp.WaitVisible(`input[name="upload"]`),
|
cdp.SendKeys(`input[name="upload"]`, filepath, cdp.NodeVisible),
|
||||||
cdp.NodeIDs(`input[name="upload"]`, &ids, cdp.NodeVisible),
|
|
||||||
cdp.ActionFunc(func(ctxt context.Context, h cdptypes.Handler) error {
|
|
||||||
return dom.SetFileInputFiles(ids[0], []string{filepath}).Do(ctxt, h)
|
|
||||||
}),
|
|
||||||
cdp.Click(`input[name="submit"]`),
|
cdp.Click(`input[name="submit"]`),
|
||||||
cdp.Text(`#result`, sz, cdp.ByID, cdp.NodeVisible),
|
cdp.Text(`#result`, sz, cdp.ByID, cdp.NodeVisible),
|
||||||
}
|
}
|
||||||
|
|
6
input.go
6
input.go
|
@ -147,8 +147,10 @@ func ClickCount(n int) MouseOption {
|
||||||
// KeyAction will synthesize a keyDown, char, and keyUp event for each rune
|
// KeyAction will synthesize a keyDown, char, and keyUp event for each rune
|
||||||
// contained in keys along with any supplied key options.
|
// contained in keys along with any supplied key options.
|
||||||
//
|
//
|
||||||
// Note: only well known, "printable" characters will have "char" events
|
// Only well-known, "printable" characters will have char events synthesized.
|
||||||
// synthesized.
|
//
|
||||||
|
// Please see the chromedp/kb package for implementation details and the list
|
||||||
|
// of well-known keys.
|
||||||
func KeyAction(keys string, opts ...KeyOption) Action {
|
func KeyAction(keys string, opts ...KeyOption) 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
|
||||||
|
|
35
query.go
35
query.go
|
@ -329,15 +329,48 @@ func DoubleClick(sel interface{}, opts ...QueryOption) Action {
|
||||||
|
|
||||||
// SendKeys synthesizes the key up, char, and down events as needed for the
|
// SendKeys synthesizes the key up, char, and down events as needed for the
|
||||||
// runes in v, sending them to the first node matching the selector.
|
// runes in v, sending them to the first node matching the selector.
|
||||||
|
//
|
||||||
|
// Note: when selector matches a input[type="file"] node, then dom.SetFileInputFiles
|
||||||
|
// is used to set the upload path of the input node to v.
|
||||||
func SendKeys(sel interface{}, v string, opts ...QueryOption) Action {
|
func SendKeys(sel interface{}, v string, opts ...QueryOption) Action {
|
||||||
return QueryAfter(sel, func(ctxt context.Context, h cdp.Handler, nodes ...*cdp.Node) error {
|
return QueryAfter(sel, func(ctxt context.Context, h cdp.Handler, nodes ...*cdp.Node) error {
|
||||||
if len(nodes) < 1 {
|
if len(nodes) < 1 {
|
||||||
return fmt.Errorf("selector `%s` did not return any nodes", sel)
|
return fmt.Errorf("selector `%s` did not return any nodes", sel)
|
||||||
}
|
}
|
||||||
return KeyActionNode(nodes[0], v).Do(ctxt, h)
|
|
||||||
|
n := nodes[0]
|
||||||
|
|
||||||
|
// grab type attribute from node
|
||||||
|
typ, attrs := "", n.Attributes
|
||||||
|
n.RLock()
|
||||||
|
for i := 0; i < len(attrs); i += 2 {
|
||||||
|
if attrs[i] == "type" {
|
||||||
|
typ = attrs[i+1]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
n.RUnlock()
|
||||||
|
|
||||||
|
// when working with input[type="file"], call dom.SetFileInputFiles
|
||||||
|
if n.NodeName == "INPUT" && typ == "file" {
|
||||||
|
return dom.SetFileInputFiles(n.NodeID, []string{v}).Do(ctxt, h)
|
||||||
|
}
|
||||||
|
|
||||||
|
return KeyActionNode(n, v).Do(ctxt, h)
|
||||||
}, append(opts, NodeVisible)...)
|
}, append(opts, NodeVisible)...)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// SetUploadFiles sets the files to upload (ie, for a input[type="file"] node)
|
||||||
|
// for the first node matching the selector.
|
||||||
|
func SetUploadFiles(sel interface{}, files []string, opts ...QueryOption) Action {
|
||||||
|
return QueryAfter(sel, func(ctxt context.Context, h cdp.Handler, nodes ...*cdp.Node) error {
|
||||||
|
if len(nodes) < 1 {
|
||||||
|
return fmt.Errorf("selector `%s` did not return any nodes", sel)
|
||||||
|
}
|
||||||
|
|
||||||
|
return dom.SetFileInputFiles(nodes[0].NodeID, files).Do(ctxt, h)
|
||||||
|
}, opts...)
|
||||||
|
}
|
||||||
|
|
||||||
// Screenshot takes a screenshot of the first node matching the selector.
|
// Screenshot takes a screenshot of the first node matching the selector.
|
||||||
func Screenshot(sel interface{}, picbuf *[]byte, opts ...QueryOption) Action {
|
func Screenshot(sel interface{}, picbuf *[]byte, opts ...QueryOption) Action {
|
||||||
if picbuf == nil {
|
if picbuf == nil {
|
||||||
|
|
|
@ -2,6 +2,10 @@ package chromedp
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
|
"io/ioutil"
|
||||||
|
"net/http"
|
||||||
|
"net/http/httptest"
|
||||||
|
"os"
|
||||||
"reflect"
|
"reflect"
|
||||||
"testing"
|
"testing"
|
||||||
"time"
|
"time"
|
||||||
|
@ -853,3 +857,96 @@ func TestMatchedStyle(t *testing.T) {
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
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, 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())
|
||||||
|
if _, err = tmpfile.WriteString(uploadHTML); err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
if err = tmpfile.Close(); err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
c := testAllocate(t, "")
|
||||||
|
defer c.Release()
|
||||||
|
|
||||||
|
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) {
|
||||||
|
test = test
|
||||||
|
|
||||||
|
c := testAllocate(t, "")
|
||||||
|
defer c.Release()
|
||||||
|
|
||||||
|
var result string
|
||||||
|
err = c.Run(defaultContext, Tasks{
|
||||||
|
Navigate(s.URL),
|
||||||
|
test.a,
|
||||||
|
Click(`input[name="submit"]`),
|
||||||
|
Text(`#result`, &result, ByID, NodeVisible),
|
||||||
|
})
|
||||||
|
if 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)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
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>`
|
||||||
|
)
|
||||||
|
|
Loading…
Reference in New Issue
Block a user