1
0
mirror of https://github.com/MontFerret/ferret.git synced 2025-11-06 08:39:09 +02:00

Adds function to type value into input (#81)

* adds function to type input

* changes per feedback

* more PR feedback changes

* add context.Background()
This commit is contained in:
Adam Argo
2018-10-08 20:07:08 -07:00
committed by Tim Voronov
parent 05a7582bba
commit 3829dffb91
4 changed files with 112 additions and 59 deletions

View File

@@ -1,6 +1,6 @@
LET g = DOCUMENT("https://www.google.com/", true) LET g = DOCUMENT("https://www.google.com/", true)
INPUT(g, 'input[name="q"]', "ferret") INPUT(g, 'input[name="q"]', "ferret", 25)
CLICK(g, 'input[name="btnK"]') CLICK(g, 'input[name="btnK"]')
WAIT_NAVIGATION(g) WAIT_NAVIGATION(g)

View File

@@ -3,6 +3,10 @@ package dynamic
import ( import (
"context" "context"
"fmt" "fmt"
"hash/fnv"
"sync"
"time"
"github.com/MontFerret/ferret/pkg/html/dynamic/eval" "github.com/MontFerret/ferret/pkg/html/dynamic/eval"
"github.com/MontFerret/ferret/pkg/html/dynamic/events" "github.com/MontFerret/ferret/pkg/html/dynamic/events"
"github.com/MontFerret/ferret/pkg/runtime/core" "github.com/MontFerret/ferret/pkg/runtime/core"
@@ -10,13 +14,11 @@ import (
"github.com/MontFerret/ferret/pkg/runtime/values" "github.com/MontFerret/ferret/pkg/runtime/values"
"github.com/mafredri/cdp" "github.com/mafredri/cdp"
"github.com/mafredri/cdp/protocol/dom" "github.com/mafredri/cdp/protocol/dom"
"github.com/mafredri/cdp/protocol/input"
"github.com/mafredri/cdp/protocol/page" "github.com/mafredri/cdp/protocol/page"
"github.com/mafredri/cdp/rpcc" "github.com/mafredri/cdp/rpcc"
"github.com/pkg/errors" "github.com/pkg/errors"
"github.com/rs/zerolog" "github.com/rs/zerolog"
"hash/fnv"
"sync"
"time"
) )
const BlankPageURL = "about:blank" const BlankPageURL = "about:blank"
@@ -416,27 +418,21 @@ func (doc *HTMLDocument) ClickBySelectorAll(selector values.String) (values.Bool
return values.False, nil return values.False, nil
} }
func (doc *HTMLDocument) InputBySelector(selector values.String, value core.Value) (values.Boolean, error) { func (doc *HTMLDocument) InputBySelector(selector values.String, value core.Value, delay values.Int) (values.Boolean, error) {
ctx := context.Background()
valStr := value.String()
res, err := eval.Eval( res, err := eval.Eval(
doc.client, doc.client,
fmt.Sprintf( fmt.Sprintf(`
`
var el = document.querySelector(%s); var el = document.querySelector(%s);
if (el == null) { if (el == null) {
return false; return false;
} }
el.focus();
var evt = new window.Event('input', { bubbles: true });
el.value = %s
el.dispatchEvent(evt);
return true; return true;
`, `, eval.ParamString(selector.String())),
eval.ParamString(selector.String()),
eval.ParamString(value.String()),
),
true, true,
false, false,
) )
@@ -445,11 +441,25 @@ func (doc *HTMLDocument) InputBySelector(selector values.String, value core.Valu
return values.False, err return values.False, err
} }
if res.Type() == core.BooleanType { if res.Type() == core.BooleanType && res.(values.Boolean) == values.False {
return res.(values.Boolean), nil return values.False, nil
} }
return values.False, nil delayMs := time.Duration(delay)
time.Sleep(delayMs * time.Millisecond)
for _, ch := range valStr {
for _, ev := range []string{"keyDown", "keyUp"} {
ke := input.NewDispatchKeyEventArgs(ev).SetText(string(ch))
if err := doc.client.Input.DispatchKeyEvent(ctx, ke); err != nil {
return values.False, err
}
time.Sleep(delayMs * time.Millisecond)
}
}
return values.True, nil
} }
func (doc *HTMLDocument) WaitForSelector(selector values.String, timeout values.Int) error { func (doc *HTMLDocument) WaitForSelector(selector values.String, timeout values.Int) error {

View File

@@ -3,6 +3,12 @@ package dynamic
import ( import (
"context" "context"
"encoding/json" "encoding/json"
"hash/fnv"
"strconv"
"strings"
"sync"
"time"
"github.com/MontFerret/ferret/pkg/html/common" "github.com/MontFerret/ferret/pkg/html/common"
"github.com/MontFerret/ferret/pkg/html/dynamic/eval" "github.com/MontFerret/ferret/pkg/html/dynamic/eval"
"github.com/MontFerret/ferret/pkg/html/dynamic/events" "github.com/MontFerret/ferret/pkg/html/dynamic/events"
@@ -10,12 +16,8 @@ import (
"github.com/MontFerret/ferret/pkg/runtime/values" "github.com/MontFerret/ferret/pkg/runtime/values"
"github.com/mafredri/cdp" "github.com/mafredri/cdp"
"github.com/mafredri/cdp/protocol/dom" "github.com/mafredri/cdp/protocol/dom"
"github.com/mafredri/cdp/protocol/input"
"github.com/rs/zerolog" "github.com/rs/zerolog"
"hash/fnv"
"strconv"
"strings"
"sync"
"time"
) )
const DefaultTimeout = time.Second * 30 const DefaultTimeout = time.Second * 30
@@ -586,11 +588,31 @@ func (el *HTMLElement) Click() (values.Boolean, error) {
return events.DispatchEvent(ctx, el.client, el.id, "click") return events.DispatchEvent(ctx, el.client, el.id, "click")
} }
func (el *HTMLElement) Input(value core.Value) error { func (el *HTMLElement) Input(value core.Value, delay values.Int) error {
ctx, cancel := contextWithTimeout() ctx, cancel := contextWithTimeout()
defer cancel() defer cancel()
return el.client.DOM.SetAttributeValue(ctx, dom.NewSetAttributeValueArgs(el.id, "value", value.String())) if err := el.client.DOM.Focus(ctx, dom.NewFocusArgs().SetNodeID(el.id)); err != nil {
return err
}
delayMs := time.Duration(delay)
time.Sleep(delayMs * time.Millisecond)
valStr := value.String()
for _, ch := range valStr {
for _, ev := range []string{"keyDown", "keyUp"} {
ke := input.NewDispatchKeyEventArgs(ev).SetText(string(ch))
if err := el.client.Input.DispatchKeyEvent(ctx, ke); err != nil {
return err
}
time.Sleep(delayMs * time.Millisecond)
}
}
return nil
} }
func (el *HTMLElement) IsConnected() values.Boolean { func (el *HTMLElement) IsConnected() values.Boolean {

View File

@@ -2,71 +2,92 @@ package html
import ( import (
"context" "context"
"github.com/MontFerret/ferret/pkg/html/dynamic" "github.com/MontFerret/ferret/pkg/html/dynamic"
"github.com/MontFerret/ferret/pkg/runtime/core" "github.com/MontFerret/ferret/pkg/runtime/core"
"github.com/MontFerret/ferret/pkg/runtime/values" "github.com/MontFerret/ferret/pkg/runtime/values"
) )
/* /*
* Sends a value to an underlying input element. * Types a value to an underlying input element.
* @param source (Document | Element) - Event target. * @param source (Document | Element) - Event target.
* @param valueOrSelector (String) - Selector or a value. * @param valueOrSelector (String) - Selector or a value.
* @param value (String) - Target value. * @param value (String) - Target value.
* @param delay (Int, optional) - Waits delay milliseconds between keystrokes
* @returns (Boolean) - Returns true if an element was found. * @returns (Boolean) - Returns true if an element was found.
*/ */
func Input(_ context.Context, args ...core.Value) (core.Value, error) { func Input(_ context.Context, args ...core.Value) (core.Value, error) {
err := core.ValidateArgs(args, 2, 3) err := core.ValidateArgs(args, 2, 4)
if err != nil { if err != nil {
return values.None, err return values.None, err
} }
// TYPE(el, "foobar") arg1 := args[0]
if len(args) == 2 { err = core.ValidateType(arg1, core.HTMLDocumentType, core.HTMLElementType)
arg1 := args[0]
err := core.ValidateType(arg1, core.HTMLElementType) if err != nil {
return values.False, err
}
switch args[0].(type) {
case *dynamic.HTMLDocument:
doc, ok := arg1.(*dynamic.HTMLDocument)
if !ok {
return values.False, core.Errors(core.ErrInvalidType, ErrNotDynamic)
}
// selector
arg2 := args[1]
err = core.ValidateType(arg2, core.StringType)
if err != nil { if err != nil {
return values.False, err return values.False, err
} }
delay := values.Int(0)
if len(args) == 4 {
arg4 := args[3]
err = core.ValidateType(arg4, core.IntType)
if err != nil {
return values.False, err
}
delay = arg4.(values.Int)
}
return doc.InputBySelector(arg2.(values.String), args[2], delay)
case *dynamic.HTMLElement:
el, ok := arg1.(*dynamic.HTMLElement) el, ok := arg1.(*dynamic.HTMLElement)
if !ok { if !ok {
return values.False, core.Errors(core.ErrInvalidType, ErrNotDynamic) return values.False, core.Errors(core.ErrInvalidType, ErrNotDynamic)
} }
err = el.Input(args[1]) delay := values.Int(0)
if len(args) == 3 {
arg3 := args[2]
err = core.ValidateType(arg3, core.IntType)
if err != nil {
return values.False, err
}
delay = arg3.(values.Int)
}
err = el.Input(args[1], delay)
if err != nil { if err != nil {
return values.False, err return values.False, err
} }
return values.True, nil return values.True, nil
default:
return values.False, core.Errors(core.ErrInvalidArgument)
} }
arg1 := args[0]
err = core.ValidateType(arg1, core.HTMLDocumentType)
if err != nil {
return values.False, err
}
arg2 := args[1]
err = core.ValidateType(arg2, core.StringType)
if err != nil {
return values.False, err
}
doc, ok := arg1.(*dynamic.HTMLDocument)
if !ok {
return values.False, core.Errors(core.ErrInvalidType, ErrNotDynamic)
}
return doc.InputBySelector(arg2.(values.String), args[2])
} }