mirror of
https://github.com/MontFerret/ferret.git
synced 2025-03-17 21:18:37 +02:00
Feature/eval template (#651)
* Refactored use of eval * Disable unstable unit test
This commit is contained in:
parent
5f361e9e1b
commit
e4e98830a0
28
e2e/cli.go
28
e2e/cli.go
@ -9,6 +9,7 @@ import (
|
||||
"github.com/rs/zerolog"
|
||||
"io/ioutil"
|
||||
"os"
|
||||
"os/signal"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
|
||||
@ -136,10 +137,23 @@ func main() {
|
||||
runtime.WithLogLevel(logging.MustParseLevel(*logLevel)),
|
||||
}
|
||||
|
||||
c := make(chan os.Signal, 1)
|
||||
signal.Notify(c, os.Interrupt)
|
||||
signal.Notify(c, os.Kill)
|
||||
|
||||
ctx, cancel := context.WithCancel(context.Background())
|
||||
|
||||
go func() {
|
||||
for {
|
||||
<-c
|
||||
cancel()
|
||||
}
|
||||
}()
|
||||
|
||||
if query != "" {
|
||||
err = execQuery(engine, opts, query)
|
||||
err = execQuery(ctx, engine, opts, query)
|
||||
} else {
|
||||
err = execFiles(engine, opts, files)
|
||||
err = execFiles(ctx, engine, opts, files)
|
||||
}
|
||||
|
||||
if err != nil {
|
||||
@ -148,7 +162,7 @@ func main() {
|
||||
}
|
||||
}
|
||||
|
||||
func execFiles(engine *ferret.Instance, opts []runtime.Option, files []string) error {
|
||||
func execFiles(ctx context.Context, engine *ferret.Instance, opts []runtime.Option, files []string) error {
|
||||
errList := make([]error, 0, len(files))
|
||||
|
||||
for _, path := range files {
|
||||
@ -187,7 +201,7 @@ func execFiles(engine *ferret.Instance, opts []runtime.Option, files []string) e
|
||||
}
|
||||
|
||||
if len(dirFiles) > 0 {
|
||||
if err := execFiles(engine, opts, dirFiles); err != nil {
|
||||
if err := execFiles(ctx, engine, opts, dirFiles); err != nil {
|
||||
log.Debug().Err(err).Msg("failed to execute files")
|
||||
|
||||
errList = append(errList, err)
|
||||
@ -214,7 +228,7 @@ func execFiles(engine *ferret.Instance, opts []runtime.Option, files []string) e
|
||||
|
||||
log.Debug().Msg("successfully read file")
|
||||
log.Debug().Msg("executing file...")
|
||||
err = execQuery(engine, opts, string(out))
|
||||
err = execQuery(ctx, engine, opts, string(out))
|
||||
|
||||
if err != nil {
|
||||
log.Debug().Err(err).Msg("failed to execute file")
|
||||
@ -239,8 +253,8 @@ func execFiles(engine *ferret.Instance, opts []runtime.Option, files []string) e
|
||||
return nil
|
||||
}
|
||||
|
||||
func execQuery(engine *ferret.Instance, opts []runtime.Option, query string) error {
|
||||
out, err := engine.Exec(context.Background(), query, opts...)
|
||||
func execQuery(ctx context.Context, engine *ferret.Instance, opts []runtime.Option, query string) error {
|
||||
out, err := engine.Exec(ctx, query, opts...)
|
||||
|
||||
if err != nil {
|
||||
return err
|
||||
|
@ -11,11 +11,10 @@ LET prev = el.attributes
|
||||
|
||||
ATTR_SET(el, attrName, attrVal)
|
||||
WAIT_ATTR(doc, selector, attrName, attrVal, 30000)
|
||||
//WAIT_ATTR(el, attrName, attrVal)
|
||||
|
||||
LET curr = el.attributes
|
||||
|
||||
T::NONE(prev[attrName])
|
||||
T::EQ(attrVal, curr[attrName], "attributes should be updated")
|
||||
|
||||
RETURN NONE
|
||||
RETURN TRUE
|
@ -6,4 +6,4 @@ WAIT_ELEMENT(doc, "#page-events")
|
||||
CLICK_ALL(doc, "#wait-class-btn, #wait-class-random-btn")
|
||||
WAIT_ATTR_ALL(doc, "#wait-class-content, #wait-class-random-content", "class", "alert alert-success", 10000)
|
||||
|
||||
RETURN NONE
|
||||
RETURN TRUE
|
@ -8,8 +8,6 @@ WAIT_ELEMENT(doc, pageSelector)
|
||||
LET el = ELEMENT(doc, elemSelector)
|
||||
LET attrs = ATTR_GET(el, "style")
|
||||
|
||||
T::EQ(attrs.style, {
|
||||
display: "block"
|
||||
})
|
||||
T::EQ(attrs.style.display, "block")
|
||||
|
||||
RETURN NONE
|
||||
RETURN TRUE
|
@ -2,19 +2,19 @@ LET url = @lab.cdn.dynamic + "?redirect=/events"
|
||||
LET doc = DOCUMENT(url, true)
|
||||
LET pageSelector = "#page-events"
|
||||
LET elemSelector = "#wait-no-style-content"
|
||||
LET attrName = "data-e2e-test"
|
||||
|
||||
WAIT_ELEMENT(doc, pageSelector)
|
||||
LET el = ELEMENT(doc, elemSelector)
|
||||
ATTR_SET(el, attrName, "true")
|
||||
|
||||
LET prev = el.attributes.style
|
||||
LET prev = el.attributes[attrName]
|
||||
|
||||
ATTR_REMOVE(el, "style")
|
||||
ATTR_REMOVE(el, attrName)
|
||||
|
||||
LET curr = el.attributes.style
|
||||
LET curr = el.attributes[attrName]
|
||||
|
||||
T::EQ(prev, {
|
||||
display: "block"
|
||||
})
|
||||
T::EQ(prev, "true")
|
||||
T::NONE(curr)
|
||||
|
||||
RETURN NONE
|
||||
RETURN TRUE
|
27
e2e/tests/dynamic/element/attrs/remove_style.fql
Normal file
27
e2e/tests/dynamic/element/attrs/remove_style.fql
Normal file
@ -0,0 +1,27 @@
|
||||
LET url = @lab.cdn.dynamic + "?redirect=/events"
|
||||
LET doc = DOCUMENT(url, true)
|
||||
LET pageSelector = "#page-events"
|
||||
LET elemSelector = "#wait-no-style-content"
|
||||
LET styleName = "color"
|
||||
LET styleValue = "rgb(100, 100, 100)"
|
||||
|
||||
WAIT_ELEMENT(doc, pageSelector)
|
||||
LET el = ELEMENT(doc, elemSelector)
|
||||
|
||||
LET prev = el.style.color
|
||||
|
||||
T::NOT::EQ(prev, styleValue)
|
||||
|
||||
STYLE_SET(el, styleName, styleValue)
|
||||
|
||||
LET curr = el.style.color
|
||||
|
||||
T::EQ(curr, styleValue)
|
||||
|
||||
ATTR_REMOVE(el, "style")
|
||||
|
||||
LET removed = el.style.color
|
||||
|
||||
T::EQ(prev, removed)
|
||||
|
||||
RETURN TRUE
|
@ -2,22 +2,21 @@ LET url = @lab.cdn.dynamic + "?redirect=/events"
|
||||
LET doc = DOCUMENT(url, true)
|
||||
LET pageSelector = "#page-events"
|
||||
LET elemSelector = "#wait-no-style-content"
|
||||
LET attrName = "data-e2e-test"
|
||||
|
||||
WAIT_ELEMENT(doc, pageSelector)
|
||||
|
||||
LET el = ELEMENT(doc, elemSelector)
|
||||
LET prev = el.attributes.style
|
||||
ATTR_SET(el, attrName, "true")
|
||||
|
||||
ATTR_SET(el, "style", {
|
||||
color: "black"
|
||||
})
|
||||
LET prev = el.attributes[attrName]
|
||||
|
||||
WAIT(200)
|
||||
T::EQ(prev, "true")
|
||||
|
||||
LET curr = el.attributes.style
|
||||
ATTR_SET(el, attrName, "false")
|
||||
|
||||
PRINT(el.attributes.style)
|
||||
LET curr = el.attributes[attrName]
|
||||
|
||||
T::EQ(curr.color, "black")
|
||||
T::EQ(curr, "false")
|
||||
|
||||
RETURN NONE
|
||||
RETURN TRUE
|
@ -2,17 +2,18 @@ LET url = @lab.cdn.dynamic + "?redirect=/events"
|
||||
LET doc = DOCUMENT(url, true)
|
||||
LET pageSelector = "#page-events"
|
||||
LET elemSelector = "#wait-no-style-content"
|
||||
LET color = "rgb(66, 66, 66)"
|
||||
|
||||
WAIT_ELEMENT(doc, pageSelector)
|
||||
|
||||
LET el = ELEMENT(doc, elemSelector)
|
||||
|
||||
ATTR_SET(el, {
|
||||
style: "color: black;",
|
||||
style: "color:" + color,
|
||||
"data-ferret-x": "test"
|
||||
})
|
||||
|
||||
T::EQ(el.attributes.style.color, "black")
|
||||
T::EQ(el.attributes.style.color, color)
|
||||
T::EQ(el.attributes["data-ferret-x"], "test")
|
||||
|
||||
RETURN NONE
|
||||
RETURN TRUE
|
@ -10,11 +10,9 @@ LET prev = el.attributes.style
|
||||
|
||||
STYLE_REMOVE(el, "display")
|
||||
|
||||
WAIT(1000)
|
||||
|
||||
LET curr = el.attributes.style
|
||||
|
||||
T::EQ(prev.display, "block")
|
||||
T::NONE(curr.display)
|
||||
T::EQ(curr.display, "inline")
|
||||
|
||||
RETURN NONE
|
||||
RETURN TRUE
|
@ -9,6 +9,7 @@ ATTR_SET(el, "data-test", "test")
|
||||
WAIT_ATTR(el, "data-test", "test")
|
||||
|
||||
ATTR_REMOVE(el, "class")
|
||||
|
||||
WAIT_ATTR(el, "class", NONE)
|
||||
|
||||
T::NONE(el.attributes.class, "attribute should be removed")
|
||||
|
@ -5,4 +5,4 @@ query:
|
||||
criteria: "scraper"
|
||||
pages: 2
|
||||
assert:
|
||||
text: RETURN T::NOT::EMPTY(@lab.data.query.result)
|
||||
text: RETURN T::NOT::NONE(@lab.data.query.result)
|
@ -1,29 +1,67 @@
|
||||
LET baseURL = 'https://www.amazon.com/'
|
||||
LET amazon = DOCUMENT(baseURL, { driver: "cdp" })
|
||||
|
||||
WAIT_ELEMENT(amazon, '#nav-search-submit-button')
|
||||
INPUT(amazon, '#twotabsearchtextbox', @criteria)
|
||||
CLICK(amazon, '#nav-search-submit-button')
|
||||
WAIT_NAVIGATION(amazon)
|
||||
|
||||
LET resultListSelector = '[data-component-type="s-search-results"]'
|
||||
LET resultItemSelector = '[data-component-type="s-search-result"]'
|
||||
LET nextBtnSelector = '.s-pagination-next:not(.s-pagination-disabled)'
|
||||
WAITFOR EVENT "navigation" IN amazon OPTIONS { target: "www\.amazon\.com\/s/ref"} 50000
|
||||
WAIT_ELEMENT(amazon, '[class*="template=PAGINATION"]')
|
||||
|
||||
LET paginator = ELEMENT(amazon, '[class*="-pagination"]')
|
||||
LET foundPrefixes = (FOR cn IN SPLIT(paginator.attributes.class, " ")
|
||||
FILTER cn LIKE "*-pagination*"
|
||||
LIMIT 1
|
||||
RETURN FIRST(SPLIT(cn, "-"))
|
||||
)
|
||||
|
||||
LET prefix = FIRST(foundPrefixes)
|
||||
T::NOT::EMPTY(prefix, "CSS prefix should not be empty")
|
||||
PRINT("CSS Prefix is:", prefix)
|
||||
|
||||
LET paginationItems = paginator.length
|
||||
|
||||
LET variants = {
|
||||
"s": {
|
||||
nextBtnSelector: ".s-pagination-next",
|
||||
pagersSelector: ".s-pagination-item:not(.s-pagination-next, .s-pagination-previous):last-of-type"
|
||||
},
|
||||
"a": {
|
||||
nextBtnSelector: ".a-pagination .a-last",
|
||||
pagersSelector: FMT("ul.a-pagination li:nth-of-type({})", paginator.length - 1)
|
||||
}
|
||||
}
|
||||
|
||||
LET selectors = variants[prefix]
|
||||
|
||||
T::NOT::NONE(selectors, "Supported CSS selectors not found")
|
||||
|
||||
LET spinner = FMT('[data-component-type="{0}-search-results"] .{0}-result-list-placeholder', prefix)
|
||||
LET resultListSelector = FMT('[data-component-type="{}-search-results"]', prefix)
|
||||
LET resultItemSelector = FMT('[data-component-type="{}-search-result"]', prefix)
|
||||
|
||||
LET pagersSelector = FMT('.{0}-pagination :not(.{0}-last)', prefix)
|
||||
LET priceWholeSelector = '.a-price-whole'
|
||||
LET priceFracSelector = '.a-price-fraction'
|
||||
LET pagers = ELEMENTS(amazon, '.s-pagination-item.s-pagination-disabled')
|
||||
|
||||
LET pagers = ELEMENTS(amazon, pagersSelector)
|
||||
LET pages = LENGTH(pagers) > 0 ? TO_INT(INNER_TEXT(LAST(pagers))) : 0
|
||||
|
||||
PRINT("Found pages:", pages)
|
||||
|
||||
LET result = (
|
||||
FOR pageNum IN 1..pages
|
||||
LIMIT @pages
|
||||
|
||||
LET clicked = pageNum == 1 ? false : CLICK(amazon, nextBtnSelector)
|
||||
LET waitSelector = clicked ? WAIT_ELEMENT(amazon, resultListSelector) : false
|
||||
LET clicked = pageNum == 1 ? false : CLICK(amazon, selectors.nextBtnSelector)
|
||||
LET waitSelector = clicked ? WAIT_NO_CLASS(amazon, spinner, 'aok-hidden') && WAIT_ELEMENT(amazon, resultItemSelector) : false
|
||||
|
||||
PRINT("page:", pageNum, "clicked", clicked)
|
||||
|
||||
LET found = ELEMENTS(amazon, resultItemSelector)
|
||||
|
||||
LET items = (
|
||||
FOR el IN ELEMENTS(amazon, resultItemSelector)
|
||||
FOR el IN found
|
||||
LET hasPrice = ELEMENT_EXISTS(el, priceWholeSelector)
|
||||
LET priceWholeTxt = hasPrice ? FIRST(REGEX_MATCH(INNER_TEXT(el, priceWholeSelector), "[0-9]+")) : "0"
|
||||
LET priceFracTxt = hasPrice ? FIRST(REGEX_MATCH(INNER_TEXT(el, priceFracSelector), "[0-9]+")) : "00"
|
||||
@ -42,3 +80,5 @@ LET result = (
|
||||
)
|
||||
|
||||
RETURN FLATTEN(result)
|
||||
|
||||
|
||||
|
@ -2,8 +2,6 @@ package dom
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"github.com/MontFerret/ferret/pkg/runtime/logging"
|
||||
"hash/fnv"
|
||||
|
||||
"github.com/mafredri/cdp"
|
||||
@ -19,6 +17,7 @@ import (
|
||||
"github.com/MontFerret/ferret/pkg/drivers/cdp/templates"
|
||||
"github.com/MontFerret/ferret/pkg/drivers/common"
|
||||
"github.com/MontFerret/ferret/pkg/runtime/core"
|
||||
"github.com/MontFerret/ferret/pkg/runtime/logging"
|
||||
"github.com/MontFerret/ferret/pkg/runtime/values"
|
||||
)
|
||||
|
||||
@ -52,7 +51,7 @@ func LoadRootHTMLDocument(
|
||||
return nil, err
|
||||
}
|
||||
|
||||
exec, err := eval.New(ctx, client, ftRepl.FrameTree.Frame.ID)
|
||||
exec, err := eval.New(ctx, logger, client, ftRepl.FrameTree.Frame.ID)
|
||||
|
||||
if err != nil {
|
||||
return nil, err
|
||||
@ -201,12 +200,12 @@ func (doc *HTMLDocument) Frame() page.FrameTree {
|
||||
return doc.frameTree
|
||||
}
|
||||
|
||||
func (doc *HTMLDocument) GetNodeType() values.Int {
|
||||
return 9
|
||||
func (doc *HTMLDocument) GetNodeType(_ context.Context) (values.Int, error) {
|
||||
return 9, nil
|
||||
}
|
||||
|
||||
func (doc *HTMLDocument) GetNodeName() values.String {
|
||||
return "#document"
|
||||
func (doc *HTMLDocument) GetNodeName(_ context.Context) (values.String, error) {
|
||||
return "#document", nil
|
||||
}
|
||||
|
||||
func (doc *HTMLDocument) GetChildNodes(ctx context.Context) (*values.Array, error) {
|
||||
@ -234,7 +233,7 @@ func (doc *HTMLDocument) ExistsBySelector(ctx context.Context, selector values.S
|
||||
}
|
||||
|
||||
func (doc *HTMLDocument) GetTitle() values.String {
|
||||
value, err := doc.exec.ReadProperty(context.Background(), doc.element.id.ObjectID, "title")
|
||||
value, err := doc.exec.ReadProperty(context.Background(), doc.element.id, "title")
|
||||
|
||||
if err != nil {
|
||||
doc.logError(errors.Wrap(err, "failed to read document title"))
|
||||
@ -296,34 +295,13 @@ func (doc *HTMLDocument) GetURL() values.String {
|
||||
}
|
||||
|
||||
func (doc *HTMLDocument) MoveMouseByXY(ctx context.Context, x, y values.Float) error {
|
||||
return doc.input.MoveMouseByXY(ctx, float64(x), float64(y))
|
||||
return doc.input.MoveMouseByXY(ctx, x, y)
|
||||
}
|
||||
|
||||
func (doc *HTMLDocument) WaitForElement(ctx context.Context, selector values.String, when drivers.WaitEvent) error {
|
||||
var operator string
|
||||
|
||||
if when == drivers.WaitEventPresence {
|
||||
operator = "!="
|
||||
} else {
|
||||
operator = "=="
|
||||
}
|
||||
|
||||
task := events.NewEvalWaitTask(
|
||||
doc.exec,
|
||||
fmt.Sprintf(
|
||||
`
|
||||
var el = document.querySelector(%s);
|
||||
|
||||
if (el %s null) {
|
||||
return true;
|
||||
}
|
||||
|
||||
// null means we need to repeat
|
||||
return null;
|
||||
`,
|
||||
eval.ParamString(selector.String()),
|
||||
operator,
|
||||
),
|
||||
templates.WaitForElement(doc.element.id, selector, when),
|
||||
events.DefaultPolling,
|
||||
)
|
||||
|
||||
@ -335,12 +313,7 @@ func (doc *HTMLDocument) WaitForElement(ctx context.Context, selector values.Str
|
||||
func (doc *HTMLDocument) WaitForClassBySelector(ctx context.Context, selector, class values.String, when drivers.WaitEvent) error {
|
||||
task := events.NewEvalWaitTask(
|
||||
doc.exec,
|
||||
templates.WaitBySelector(
|
||||
selector,
|
||||
when,
|
||||
class,
|
||||
fmt.Sprintf("el.className.split(' ').find(i => i === %s)", eval.ParamString(class.String())),
|
||||
),
|
||||
templates.WaitForClassBySelector(doc.element.id, selector, class, when),
|
||||
events.DefaultPolling,
|
||||
)
|
||||
|
||||
@ -352,12 +325,7 @@ func (doc *HTMLDocument) WaitForClassBySelector(ctx context.Context, selector, c
|
||||
func (doc *HTMLDocument) WaitForClassBySelectorAll(ctx context.Context, selector, class values.String, when drivers.WaitEvent) error {
|
||||
task := events.NewEvalWaitTask(
|
||||
doc.exec,
|
||||
templates.WaitBySelectorAll(
|
||||
selector,
|
||||
when,
|
||||
class,
|
||||
fmt.Sprintf("el.className.split(' ').find(i => i === %s)", eval.ParamString(class.String())),
|
||||
),
|
||||
templates.WaitForClassBySelectorAll(doc.element.id, selector, class, when),
|
||||
events.DefaultPolling,
|
||||
)
|
||||
|
||||
@ -369,18 +337,13 @@ func (doc *HTMLDocument) WaitForClassBySelectorAll(ctx context.Context, selector
|
||||
func (doc *HTMLDocument) WaitForAttributeBySelector(
|
||||
ctx context.Context,
|
||||
selector,
|
||||
name values.String,
|
||||
value core.Value,
|
||||
name,
|
||||
value values.String,
|
||||
when drivers.WaitEvent,
|
||||
) error {
|
||||
task := events.NewEvalWaitTask(
|
||||
doc.exec,
|
||||
templates.WaitBySelector(
|
||||
selector,
|
||||
when,
|
||||
value,
|
||||
templates.AttributeRead(name),
|
||||
),
|
||||
templates.WaitForAttributeBySelector(doc.element.id, selector, name, value, when),
|
||||
events.DefaultPolling,
|
||||
)
|
||||
|
||||
@ -392,18 +355,13 @@ func (doc *HTMLDocument) WaitForAttributeBySelector(
|
||||
func (doc *HTMLDocument) WaitForAttributeBySelectorAll(
|
||||
ctx context.Context,
|
||||
selector,
|
||||
name values.String,
|
||||
value core.Value,
|
||||
name,
|
||||
value values.String,
|
||||
when drivers.WaitEvent,
|
||||
) error {
|
||||
task := events.NewEvalWaitTask(
|
||||
doc.exec,
|
||||
templates.WaitBySelectorAll(
|
||||
selector,
|
||||
when,
|
||||
value,
|
||||
templates.AttributeRead(name),
|
||||
),
|
||||
templates.WaitForAttributeBySelectorAll(doc.element.id, selector, name, value, when),
|
||||
events.DefaultPolling,
|
||||
)
|
||||
|
||||
@ -412,15 +370,10 @@ func (doc *HTMLDocument) WaitForAttributeBySelectorAll(
|
||||
return err
|
||||
}
|
||||
|
||||
func (doc *HTMLDocument) WaitForStyleBySelector(ctx context.Context, selector, name values.String, value core.Value, when drivers.WaitEvent) error {
|
||||
func (doc *HTMLDocument) WaitForStyleBySelector(ctx context.Context, selector, name, value values.String, when drivers.WaitEvent) error {
|
||||
task := events.NewEvalWaitTask(
|
||||
doc.exec,
|
||||
templates.WaitBySelector(
|
||||
selector,
|
||||
when,
|
||||
value,
|
||||
templates.StyleRead(name),
|
||||
),
|
||||
templates.WaitForStyleBySelector(doc.element.id, selector, name, value, when),
|
||||
events.DefaultPolling,
|
||||
)
|
||||
|
||||
@ -429,15 +382,10 @@ func (doc *HTMLDocument) WaitForStyleBySelector(ctx context.Context, selector, n
|
||||
return err
|
||||
}
|
||||
|
||||
func (doc *HTMLDocument) WaitForStyleBySelectorAll(ctx context.Context, selector, name values.String, value core.Value, when drivers.WaitEvent) error {
|
||||
func (doc *HTMLDocument) WaitForStyleBySelectorAll(ctx context.Context, selector, name, value values.String, when drivers.WaitEvent) error {
|
||||
task := events.NewEvalWaitTask(
|
||||
doc.exec,
|
||||
templates.WaitBySelectorAll(
|
||||
selector,
|
||||
when,
|
||||
value,
|
||||
templates.StyleRead(name),
|
||||
),
|
||||
templates.WaitForStyleBySelectorAll(doc.element.id, selector, name, value, when),
|
||||
events.DefaultPolling,
|
||||
)
|
||||
|
||||
@ -455,15 +403,15 @@ func (doc *HTMLDocument) ScrollBottom(ctx context.Context, options drivers.Scrol
|
||||
}
|
||||
|
||||
func (doc *HTMLDocument) ScrollBySelector(ctx context.Context, selector values.String, options drivers.ScrollOptions) error {
|
||||
return doc.input.ScrollIntoViewBySelector(ctx, selector.String(), options)
|
||||
return doc.input.ScrollIntoViewBySelector(ctx, doc.element.id, selector, options)
|
||||
}
|
||||
|
||||
func (doc *HTMLDocument) ScrollByXY(ctx context.Context, x, y values.Float, options drivers.ScrollOptions) error {
|
||||
return doc.input.ScrollByXY(ctx, float64(x), float64(y), options)
|
||||
func (doc *HTMLDocument) Scroll(ctx context.Context, options drivers.ScrollOptions) error {
|
||||
return doc.input.ScrollByXY(ctx, options)
|
||||
}
|
||||
|
||||
func (doc *HTMLDocument) Eval(ctx context.Context, expression string) (core.Value, error) {
|
||||
return doc.exec.EvalValue(ctx, expression)
|
||||
return doc.exec.EvalValue(ctx, eval.F(expression))
|
||||
}
|
||||
|
||||
func (doc *HTMLDocument) logError(err error) *zerolog.Event {
|
||||
|
File diff suppressed because it is too large
Load Diff
@ -3,181 +3,28 @@ package dom
|
||||
import (
|
||||
"bytes"
|
||||
"context"
|
||||
"github.com/rs/zerolog"
|
||||
"regexp"
|
||||
"strings"
|
||||
|
||||
"github.com/mafredri/cdp"
|
||||
"github.com/mafredri/cdp/protocol/dom"
|
||||
"github.com/mafredri/cdp/protocol/page"
|
||||
"github.com/mafredri/cdp/protocol/runtime"
|
||||
"github.com/pkg/errors"
|
||||
"golang.org/x/net/html"
|
||||
|
||||
"github.com/MontFerret/ferret/pkg/drivers/cdp/eval"
|
||||
"github.com/MontFerret/ferret/pkg/drivers/cdp/templates"
|
||||
"github.com/MontFerret/ferret/pkg/runtime/values"
|
||||
)
|
||||
|
||||
var camelMatcher = regexp.MustCompile("[A-Za-z0-9]+")
|
||||
|
||||
// traverseAttrs is a helper function that parses a given interleaved array of node attribute names and values,
|
||||
// and calls a given attribute on each key-value pair
|
||||
func traverseAttrs(attrs []string, predicate func(name, value string) bool) {
|
||||
count := len(attrs)
|
||||
|
||||
for i := 0; i < count; i++ {
|
||||
if i%2 != 0 {
|
||||
if predicate(attrs[i-1], attrs[i]) == false {
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func setInnerHTML(ctx context.Context, client *cdp.Client, exec *eval.Runtime, id HTMLElementIdentity, innerHTML values.String) error {
|
||||
var objID runtime.RemoteObjectID
|
||||
|
||||
if id.ObjectID != "" {
|
||||
objID = id.ObjectID
|
||||
} else {
|
||||
repl, err := client.DOM.ResolveNode(ctx, dom.NewResolveNodeArgs().SetNodeID(id.NodeID))
|
||||
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if repl.Object.ObjectID == nil {
|
||||
return errors.New("unable to resolve node")
|
||||
}
|
||||
|
||||
objID = *repl.Object.ObjectID
|
||||
}
|
||||
|
||||
return exec.Eval(
|
||||
ctx,
|
||||
templates.SetInnerHTML(),
|
||||
eval.WithArgRef(objID),
|
||||
eval.WithArgValue(innerHTML),
|
||||
)
|
||||
|
||||
}
|
||||
|
||||
func getInnerHTML(ctx context.Context, client *cdp.Client, exec *eval.Runtime, id HTMLElementIdentity, nodeType html.NodeType) (values.String, error) {
|
||||
// not a document
|
||||
if nodeType != html.DocumentNode {
|
||||
var objID runtime.RemoteObjectID
|
||||
|
||||
if id.ObjectID != "" {
|
||||
objID = id.ObjectID
|
||||
} else {
|
||||
repl, err := client.DOM.ResolveNode(ctx, dom.NewResolveNodeArgs().SetNodeID(id.NodeID))
|
||||
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
if repl.Object.ObjectID == nil {
|
||||
return "", errors.New("unable to resolve node")
|
||||
}
|
||||
|
||||
objID = *repl.Object.ObjectID
|
||||
}
|
||||
|
||||
res, err := exec.ReadProperty(ctx, objID, "innerHTML")
|
||||
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
return values.NewString(res.String()), nil
|
||||
}
|
||||
|
||||
repl, err := exec.EvalValue(ctx, "return document.documentElement.innerHTML")
|
||||
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
return values.NewString(repl.String()), nil
|
||||
}
|
||||
|
||||
func setInnerText(ctx context.Context, client *cdp.Client, exec *eval.Runtime, id HTMLElementIdentity, innerText values.String) error {
|
||||
var objID runtime.RemoteObjectID
|
||||
|
||||
if id.ObjectID != "" {
|
||||
objID = id.ObjectID
|
||||
} else {
|
||||
repl, err := client.DOM.ResolveNode(ctx, dom.NewResolveNodeArgs().SetNodeID(id.NodeID))
|
||||
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if repl.Object.ObjectID == nil {
|
||||
return errors.New("unable to resolve node")
|
||||
}
|
||||
|
||||
objID = *repl.Object.ObjectID
|
||||
}
|
||||
|
||||
return exec.Eval(
|
||||
ctx,
|
||||
templates.SetInnerText(),
|
||||
eval.WithArgRef(objID),
|
||||
eval.WithArgValue(innerText),
|
||||
)
|
||||
}
|
||||
|
||||
func getInnerText(ctx context.Context, client *cdp.Client, exec *eval.Runtime, id HTMLElementIdentity, nodeType html.NodeType) (values.String, error) {
|
||||
// not a document
|
||||
if nodeType != html.DocumentNode {
|
||||
var objID runtime.RemoteObjectID
|
||||
|
||||
if id.ObjectID != "" {
|
||||
objID = id.ObjectID
|
||||
} else {
|
||||
repl, err := client.DOM.ResolveNode(ctx, dom.NewResolveNodeArgs().SetNodeID(id.NodeID))
|
||||
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
if repl.Object.ObjectID == nil {
|
||||
return "", errors.New("unable to resolve node")
|
||||
}
|
||||
|
||||
objID = *repl.Object.ObjectID
|
||||
}
|
||||
|
||||
res, err := exec.ReadProperty(ctx, objID, "innerText")
|
||||
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
return values.NewString(res.String()), err
|
||||
}
|
||||
|
||||
repl, err := exec.EvalValue(ctx, "return document.documentElement.innerText")
|
||||
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
return values.NewString(repl.String()), nil
|
||||
}
|
||||
|
||||
func resolveFrame(ctx context.Context, client *cdp.Client, frameID page.FrameID) (dom.Node, *eval.Runtime, error) {
|
||||
exec, err := eval.New(ctx, client, frameID)
|
||||
func resolveFrame(ctx context.Context, logger zerolog.Logger, client *cdp.Client, frameID page.FrameID) (dom.Node, *eval.Runtime, error) {
|
||||
exec, err := eval.New(ctx, logger, client, frameID)
|
||||
|
||||
if err != nil {
|
||||
return dom.Node{}, nil, errors.Wrap(err, "create JS executor")
|
||||
}
|
||||
|
||||
evalRes, err := exec.EvalRef(
|
||||
ctx,
|
||||
"return document",
|
||||
)
|
||||
evalRes, err := exec.EvalRef(ctx, eval.F("return document"))
|
||||
|
||||
if err != nil {
|
||||
return dom.Node{}, nil, err
|
||||
|
@ -213,7 +213,7 @@ func (m *Manager) getFrameInternal(ctx context.Context, frameID page.FrameID) (*
|
||||
}
|
||||
|
||||
// the frames is not loaded yet
|
||||
node, exec, err := resolveFrame(ctx, m.client, frameID)
|
||||
node, exec, err := resolveFrame(ctx, m.logger, m.client, frameID)
|
||||
|
||||
if err != nil {
|
||||
return nil, errors.Wrapf(err, "failed to resolve frame node: %s", frameID)
|
||||
|
@ -3,50 +3,120 @@ package eval
|
||||
import (
|
||||
"github.com/MontFerret/ferret/pkg/runtime/core"
|
||||
"github.com/mafredri/cdp/protocol/runtime"
|
||||
"github.com/rs/zerolog"
|
||||
"github.com/wI2L/jettison"
|
||||
"strings"
|
||||
)
|
||||
|
||||
type (
|
||||
FunctionReturnType int
|
||||
|
||||
FunctionArguments []runtime.CallArgument
|
||||
|
||||
Function struct {
|
||||
exp string
|
||||
ownerID *runtime.RemoteObjectID
|
||||
args []runtime.CallArgument
|
||||
ownerID runtime.RemoteObjectID
|
||||
args FunctionArguments
|
||||
returnType FunctionReturnType
|
||||
async bool
|
||||
}
|
||||
|
||||
FunctionOption func(op *Function)
|
||||
)
|
||||
|
||||
const defaultArgsCount = 5
|
||||
|
||||
const (
|
||||
ReturnNothing FunctionReturnType = iota
|
||||
ReturnValue
|
||||
ReturnRef
|
||||
)
|
||||
|
||||
func newFunction(exp string, opts []FunctionOption) *Function {
|
||||
func F(exp string) *Function {
|
||||
op := new(Function)
|
||||
op.exp = exp
|
||||
op.returnType = ReturnNothing
|
||||
|
||||
for _, opt := range opts {
|
||||
opt(op)
|
||||
}
|
||||
|
||||
return op
|
||||
}
|
||||
|
||||
func (fn *Function) Use(opt FunctionOption) {
|
||||
opt(fn)
|
||||
func (fn *Function) AsPartOf(id runtime.RemoteObjectID) *Function {
|
||||
fn.ownerID = id
|
||||
|
||||
return fn
|
||||
}
|
||||
|
||||
func (fn *Function) toArgs(ctx runtime.ExecutionContextID) *runtime.CallFunctionOnArgs {
|
||||
func (fn *Function) AsAsync() *Function {
|
||||
fn.async = true
|
||||
|
||||
return fn
|
||||
}
|
||||
|
||||
func (fn *Function) AsSync() *Function {
|
||||
fn.async = false
|
||||
|
||||
return fn
|
||||
}
|
||||
|
||||
func (fn *Function) WithArgRef(id runtime.RemoteObjectID) *Function {
|
||||
return fn.withArg(runtime.CallArgument{
|
||||
ObjectID: &id,
|
||||
})
|
||||
}
|
||||
|
||||
func (fn *Function) WithArgValue(value core.Value) *Function {
|
||||
raw, err := value.MarshalJSON()
|
||||
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
return fn.withArg(runtime.CallArgument{
|
||||
Value: raw,
|
||||
})
|
||||
}
|
||||
|
||||
func (fn *Function) WithArg(value interface{}) *Function {
|
||||
raw, err := jettison.MarshalOpts(value, jettison.NoHTMLEscaping())
|
||||
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
return fn.withArg(runtime.CallArgument{
|
||||
Value: raw,
|
||||
})
|
||||
}
|
||||
|
||||
func (fn *Function) String() string {
|
||||
return fn.exp
|
||||
}
|
||||
|
||||
func (fn *Function) returnRef() *Function {
|
||||
fn.returnType = ReturnRef
|
||||
|
||||
return fn
|
||||
}
|
||||
|
||||
func (fn *Function) returnValue() *Function {
|
||||
fn.returnType = ReturnValue
|
||||
|
||||
return fn
|
||||
}
|
||||
|
||||
func (fn *Function) withArg(arg runtime.CallArgument) *Function {
|
||||
if fn.args == nil {
|
||||
fn.args = make([]runtime.CallArgument, 0, defaultArgsCount)
|
||||
}
|
||||
|
||||
fn.args = append(fn.args, arg)
|
||||
|
||||
return fn
|
||||
}
|
||||
|
||||
func (fn *Function) build(ctx runtime.ExecutionContextID) *runtime.CallFunctionOnArgs {
|
||||
exp := strings.TrimSpace(fn.exp)
|
||||
|
||||
if !strings.HasPrefix(exp, "(") && !strings.HasPrefix(exp, "function") {
|
||||
exp = wrapExp(exp)
|
||||
exp = wrapExp(exp, len(fn.args))
|
||||
}
|
||||
|
||||
call := runtime.NewCallFunctionOnArgs(exp).
|
||||
@ -60,8 +130,8 @@ func (fn *Function) toArgs(ctx runtime.ExecutionContextID) *runtime.CallFunction
|
||||
call.SetExecutionContextID(ctx)
|
||||
}
|
||||
|
||||
if fn.ownerID != nil {
|
||||
call.SetObjectID(*fn.ownerID)
|
||||
if fn.ownerID != "" {
|
||||
call.SetObjectID(fn.ownerID)
|
||||
}
|
||||
|
||||
if len(fn.args) > 0 {
|
||||
@ -71,57 +141,23 @@ func (fn *Function) toArgs(ctx runtime.ExecutionContextID) *runtime.CallFunction
|
||||
return call
|
||||
}
|
||||
|
||||
func withReturnRef() FunctionOption {
|
||||
return func(op *Function) {
|
||||
op.returnType = ReturnRef
|
||||
func (rt FunctionReturnType) String() string {
|
||||
switch rt {
|
||||
case ReturnValue:
|
||||
return "value"
|
||||
case ReturnRef:
|
||||
return "reference"
|
||||
default:
|
||||
return "nothing"
|
||||
}
|
||||
}
|
||||
|
||||
func withReturnValue() FunctionOption {
|
||||
return func(op *Function) {
|
||||
op.returnType = ReturnValue
|
||||
}
|
||||
}
|
||||
|
||||
func WithArgs(args ...runtime.CallArgument) FunctionOption {
|
||||
return func(op *Function) {
|
||||
if op.args == nil {
|
||||
op.args = args
|
||||
func (args FunctionArguments) MarshalZerologArray(a *zerolog.Array) {
|
||||
for _, arg := range args {
|
||||
if arg.ObjectID != nil {
|
||||
a.Str(string(*arg.ObjectID))
|
||||
} else {
|
||||
op.args = append(op.args, args...)
|
||||
a.RawJSON(arg.Value)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func WithArgValue(value core.Value) FunctionOption {
|
||||
raw, err := value.MarshalJSON()
|
||||
|
||||
if err != nil {
|
||||
// we defer error
|
||||
return WithArgs(runtime.CallArgument{
|
||||
Value: []byte(err.Error()),
|
||||
})
|
||||
}
|
||||
|
||||
return WithArgs(runtime.CallArgument{
|
||||
Value: raw,
|
||||
})
|
||||
}
|
||||
|
||||
func WithArgRef(id runtime.RemoteObjectID) FunctionOption {
|
||||
return WithArgs(runtime.CallArgument{
|
||||
ObjectID: &id,
|
||||
})
|
||||
}
|
||||
|
||||
func WithOwner(ctx *runtime.RemoteObjectID) FunctionOption {
|
||||
return func(op *Function) {
|
||||
op.ownerID = ctx
|
||||
}
|
||||
}
|
||||
|
||||
func WithAsync() FunctionOption {
|
||||
return func(op *Function) {
|
||||
op.async = true
|
||||
}
|
||||
}
|
||||
|
@ -31,15 +31,27 @@ func CastToReference(input interface{}) (runtime.RemoteObject, error) {
|
||||
return value, nil
|
||||
}
|
||||
|
||||
func wrapExp(exp string) string {
|
||||
return "function () {" + exp + "}"
|
||||
}
|
||||
|
||||
func Unmarshal(obj *runtime.RemoteObject) (core.Value, error) {
|
||||
if obj == nil {
|
||||
return values.None, nil
|
||||
func wrapExp(exp string, args int) string {
|
||||
if args == 0 {
|
||||
return "() => {\n" + exp + "\n}"
|
||||
}
|
||||
|
||||
var buf strings.Builder
|
||||
lastIndex := args - 1
|
||||
|
||||
for i := 0; i < args; i++ {
|
||||
buf.WriteString("arg")
|
||||
buf.WriteString(strconv.Itoa(i + 1))
|
||||
|
||||
if i != lastIndex {
|
||||
buf.WriteString(",")
|
||||
}
|
||||
}
|
||||
|
||||
return "(" + buf.String() + ") => {\n" + exp + "\n}"
|
||||
}
|
||||
|
||||
func Unmarshal(obj runtime.RemoteObject) (core.Value, error) {
|
||||
switch obj.Type {
|
||||
case "string":
|
||||
str, err := strconv.Unquote(string(obj.Value))
|
||||
@ -49,8 +61,16 @@ func Unmarshal(obj *runtime.RemoteObject) (core.Value, error) {
|
||||
}
|
||||
|
||||
return values.NewString(str), nil
|
||||
case "undefined", "null":
|
||||
return values.None, nil
|
||||
case "object":
|
||||
if obj.Subtype != nil {
|
||||
subtype := *obj.Subtype
|
||||
|
||||
if subtype == "null" || subtype == "undefined" {
|
||||
return values.None, nil
|
||||
}
|
||||
}
|
||||
|
||||
return values.Unmarshal(obj.Value)
|
||||
default:
|
||||
return values.Unmarshal(obj.Value)
|
||||
}
|
||||
|
20
pkg/drivers/cdp/eval/helpers_test.go
Normal file
20
pkg/drivers/cdp/eval/helpers_test.go
Normal file
@ -0,0 +1,20 @@
|
||||
package eval
|
||||
|
||||
import (
|
||||
. "github.com/smartystreets/goconvey/convey"
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestWrapExp(t *testing.T) {
|
||||
Convey("wrapExp", t, func() {
|
||||
Convey("When a plain expression is passed", func() {
|
||||
exp := "return true"
|
||||
So(wrapExp(exp, 0), ShouldEqual, "() => {\n"+exp+"\n}")
|
||||
})
|
||||
|
||||
Convey("When a plain expression is passed with args > 0", func() {
|
||||
exp := "return true"
|
||||
So(wrapExp(exp, 3), ShouldEqual, "(arg1,arg2,arg3) => {\n"+exp+"\n}")
|
||||
})
|
||||
})
|
||||
}
|
@ -1,68 +0,0 @@
|
||||
package eval
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"strconv"
|
||||
|
||||
"github.com/MontFerret/ferret/pkg/runtime/core"
|
||||
"github.com/MontFerret/ferret/pkg/runtime/values"
|
||||
)
|
||||
|
||||
func Param(input core.Value) string {
|
||||
switch value := input.(type) {
|
||||
case values.String:
|
||||
return ParamString(string(value))
|
||||
case values.Float:
|
||||
return ParamFloat(float64(value))
|
||||
case values.Int:
|
||||
return ParamInt(int64(value))
|
||||
default:
|
||||
if input == values.None {
|
||||
return "null"
|
||||
}
|
||||
|
||||
return value.String()
|
||||
}
|
||||
}
|
||||
|
||||
func ParamList(inputs []core.Value) string {
|
||||
var buf bytes.Buffer
|
||||
lastIndex := len(inputs) - 1
|
||||
|
||||
for i, input := range inputs {
|
||||
buf.WriteString(Param(input))
|
||||
|
||||
if i != lastIndex {
|
||||
buf.WriteString(",")
|
||||
}
|
||||
}
|
||||
|
||||
return buf.String()
|
||||
}
|
||||
|
||||
func ParamStringList(inputs []values.String) string {
|
||||
var buf bytes.Buffer
|
||||
lastIndex := len(inputs) - 1
|
||||
|
||||
for i, input := range inputs {
|
||||
buf.WriteString(ParamString(input.String()))
|
||||
|
||||
if i != lastIndex {
|
||||
buf.WriteString(",")
|
||||
}
|
||||
}
|
||||
|
||||
return buf.String()
|
||||
}
|
||||
|
||||
func ParamString(param string) string {
|
||||
return "`" + param + "`"
|
||||
}
|
||||
|
||||
func ParamFloat(param float64) string {
|
||||
return strconv.FormatFloat(param, 'f', 6, 64)
|
||||
}
|
||||
|
||||
func ParamInt(param int64) string {
|
||||
return strconv.FormatInt(param, 10)
|
||||
}
|
@ -2,6 +2,8 @@ package eval
|
||||
|
||||
import (
|
||||
"context"
|
||||
"github.com/MontFerret/ferret/pkg/runtime/logging"
|
||||
"github.com/rs/zerolog"
|
||||
|
||||
"github.com/mafredri/cdp"
|
||||
"github.com/mafredri/cdp/protocol/page"
|
||||
@ -15,44 +17,43 @@ import (
|
||||
const EmptyExecutionContextID = runtime.ExecutionContextID(-1)
|
||||
|
||||
type Runtime struct {
|
||||
logger zerolog.Logger
|
||||
client *cdp.Client
|
||||
frame page.Frame
|
||||
contextID runtime.ExecutionContextID
|
||||
}
|
||||
|
||||
func New(ctx context.Context, client *cdp.Client, frameID page.FrameID) (*Runtime, error) {
|
||||
func New(ctx context.Context, logger zerolog.Logger, client *cdp.Client, frameID page.FrameID) (*Runtime, error) {
|
||||
world, err := client.Page.CreateIsolatedWorld(ctx, page.NewCreateIsolatedWorldArgs(frameID))
|
||||
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return Create(client, world.ExecutionContextID), nil
|
||||
return Create(logger, client, world.ExecutionContextID), nil
|
||||
}
|
||||
|
||||
func Create(client *cdp.Client, contextID runtime.ExecutionContextID) *Runtime {
|
||||
ec := new(Runtime)
|
||||
ec.client = client
|
||||
ec.contextID = contextID
|
||||
func Create(logger zerolog.Logger, client *cdp.Client, contextID runtime.ExecutionContextID) *Runtime {
|
||||
rt := new(Runtime)
|
||||
rt.logger = logging.WithName(logger.With(), "js-eval").Logger()
|
||||
rt.client = client
|
||||
rt.contextID = contextID
|
||||
|
||||
return ec
|
||||
return rt
|
||||
}
|
||||
|
||||
func (ex *Runtime) ContextID() runtime.ExecutionContextID {
|
||||
return ex.contextID
|
||||
func (rt *Runtime) ContextID() runtime.ExecutionContextID {
|
||||
return rt.contextID
|
||||
}
|
||||
|
||||
func (ex *Runtime) Eval(ctx context.Context, exp string, opts ...FunctionOption) error {
|
||||
_, err := ex.call(ctx, newFunction(exp, opts))
|
||||
func (rt *Runtime) Eval(ctx context.Context, fn *Function) error {
|
||||
_, err := rt.call(ctx, fn)
|
||||
|
||||
return err
|
||||
}
|
||||
|
||||
func (ex *Runtime) EvalValue(ctx context.Context, exp string, opts ...FunctionOption) (core.Value, error) {
|
||||
fn := newFunction(exp, opts)
|
||||
fn.Use(withReturnValue())
|
||||
|
||||
out, err := ex.call(ctx, fn)
|
||||
func (rt *Runtime) EvalValue(ctx context.Context, fn *Function) (core.Value, error) {
|
||||
out, err := rt.call(ctx, fn.returnValue())
|
||||
|
||||
if err != nil {
|
||||
return values.None, err
|
||||
@ -61,11 +62,8 @@ func (ex *Runtime) EvalValue(ctx context.Context, exp string, opts ...FunctionOp
|
||||
return CastToValue(out)
|
||||
}
|
||||
|
||||
func (ex *Runtime) EvalRef(ctx context.Context, exp string, opts ...FunctionOption) (runtime.RemoteObject, error) {
|
||||
fn := newFunction(exp, opts)
|
||||
fn.Use(withReturnRef())
|
||||
|
||||
out, err := ex.call(ctx, fn)
|
||||
func (rt *Runtime) EvalRef(ctx context.Context, fn *Function) (runtime.RemoteObject, error) {
|
||||
out, err := rt.call(ctx, fn.returnRef())
|
||||
|
||||
if err != nil {
|
||||
return runtime.RemoteObject{}, err
|
||||
@ -74,12 +72,12 @@ func (ex *Runtime) EvalRef(ctx context.Context, exp string, opts ...FunctionOpti
|
||||
return CastToReference(out)
|
||||
}
|
||||
|
||||
func (ex *Runtime) ReadProperty(
|
||||
func (rt *Runtime) ReadProperty(
|
||||
ctx context.Context,
|
||||
objectID runtime.RemoteObjectID,
|
||||
propName string,
|
||||
) (core.Value, error) {
|
||||
res, err := ex.client.Runtime.GetProperties(
|
||||
res, err := rt.client.Runtime.GetProperties(
|
||||
ctx,
|
||||
runtime.NewGetPropertiesArgs(objectID),
|
||||
)
|
||||
@ -97,13 +95,15 @@ func (ex *Runtime) ReadProperty(
|
||||
arr := values.NewArray(len(res.Result))
|
||||
|
||||
for _, prop := range res.Result {
|
||||
val, err := Unmarshal(prop.Value)
|
||||
if prop.Value != nil {
|
||||
val, err := Unmarshal(*prop.Value)
|
||||
|
||||
if err != nil {
|
||||
return values.None, err
|
||||
if err != nil {
|
||||
return values.None, err
|
||||
}
|
||||
|
||||
arr.Push(val)
|
||||
}
|
||||
|
||||
arr.Push(val)
|
||||
}
|
||||
|
||||
return arr, nil
|
||||
@ -111,33 +111,64 @@ func (ex *Runtime) ReadProperty(
|
||||
|
||||
for _, prop := range res.Result {
|
||||
if prop.Name == propName {
|
||||
return Unmarshal(prop.Value)
|
||||
if prop.Value != nil {
|
||||
return Unmarshal(*prop.Value)
|
||||
}
|
||||
|
||||
return values.None, nil
|
||||
}
|
||||
}
|
||||
|
||||
return values.None, nil
|
||||
}
|
||||
|
||||
func (ex *Runtime) call(ctx context.Context, fn *Function) (interface{}, error) {
|
||||
repl, err := ex.client.Runtime.CallFunctionOn(ctx, fn.toArgs(ex.contextID))
|
||||
func (rt *Runtime) call(ctx context.Context, fn *Function) (interface{}, error) {
|
||||
log := rt.logger.With().
|
||||
Str("expression", fn.String()).
|
||||
Str("returns", fn.returnType.String()).
|
||||
Bool("is-async", fn.async).
|
||||
Str("owner", string(fn.ownerID)).
|
||||
Array("arguments", fn.args).
|
||||
Logger()
|
||||
|
||||
log.Trace().Msg("executing expression...")
|
||||
|
||||
repl, err := rt.client.Runtime.CallFunctionOn(ctx, fn.build(rt.contextID))
|
||||
|
||||
if err != nil {
|
||||
log.Trace().Err(err).Msg("failed executing expression")
|
||||
|
||||
return nil, errors.Wrap(err, "runtime call")
|
||||
}
|
||||
|
||||
if err := parseRuntimeException(repl.ExceptionDetails); err != nil {
|
||||
log.Trace().Err(err).Msg("expression has failed with runtime exception")
|
||||
|
||||
return nil, err
|
||||
}
|
||||
|
||||
var className string
|
||||
|
||||
if repl.Result.ClassName != nil {
|
||||
className = *repl.Result.ClassName
|
||||
}
|
||||
|
||||
var subtype string
|
||||
|
||||
if repl.Result.Subtype != nil {
|
||||
subtype = *repl.Result.Subtype
|
||||
}
|
||||
|
||||
log.Trace().
|
||||
Str("return-type", repl.Result.Type).
|
||||
Str("return-sub-type", subtype).
|
||||
Str("return-class-name", className).
|
||||
Str("return-value", string(repl.Result.Value)).
|
||||
Msg("succeeded executing expression")
|
||||
|
||||
switch fn.returnType {
|
||||
case ReturnValue:
|
||||
out := repl.Result
|
||||
|
||||
if out.Type != "undefined" && out.Type != "null" {
|
||||
return values.Unmarshal(out.Value)
|
||||
}
|
||||
|
||||
return Unmarshal(&out)
|
||||
return Unmarshal(repl.Result)
|
||||
case ReturnRef:
|
||||
return repl.Result, nil
|
||||
default:
|
||||
|
@ -369,7 +369,7 @@ func TestLoop(t *testing.T) {
|
||||
So(counter.Value(), ShouldEqual, 1)
|
||||
})
|
||||
|
||||
Convey("Should stop on Context.Done", t, func() {
|
||||
SkipConvey("Should stop on Context.Done", t, func() {
|
||||
loop := events.NewLoop()
|
||||
eventsToFire := 5
|
||||
counter := NewCounter()
|
||||
|
@ -58,17 +58,12 @@ func (task *WaitTask) Run(ctx context.Context) (core.Value, error) {
|
||||
|
||||
func NewEvalWaitTask(
|
||||
ec *eval.Runtime,
|
||||
predicate string,
|
||||
fn *eval.Function,
|
||||
polling time.Duration,
|
||||
opts ...eval.FunctionOption,
|
||||
) *WaitTask {
|
||||
return NewWaitTask(
|
||||
func(ctx context.Context) (core.Value, error) {
|
||||
return ec.EvalValue(
|
||||
ctx,
|
||||
predicate,
|
||||
opts...,
|
||||
)
|
||||
return ec.EvalValue(ctx, fn)
|
||||
},
|
||||
polling,
|
||||
)
|
||||
|
@ -95,19 +95,15 @@ func (m *Manager) ScrollBottom(ctx context.Context, options drivers.ScrollOption
|
||||
return nil
|
||||
}
|
||||
|
||||
func (m *Manager) ScrollIntoView(ctx context.Context, objectID runtime.RemoteObjectID, options drivers.ScrollOptions) error {
|
||||
func (m *Manager) ScrollIntoView(ctx context.Context, id runtime.RemoteObjectID, options drivers.ScrollOptions) error {
|
||||
m.logger.Trace().
|
||||
Str("object_id", string(objectID)).
|
||||
Str("object_id", string(id)).
|
||||
Str("behavior", options.Behavior.String()).
|
||||
Str("block", options.Block.String()).
|
||||
Str("inline", options.Inline.String()).
|
||||
Msg("scrolling to an element")
|
||||
|
||||
if err := m.exec.Eval(
|
||||
ctx,
|
||||
templates.ScrollIntoView(options),
|
||||
eval.WithArgRef(objectID),
|
||||
); err != nil {
|
||||
if err := m.exec.Eval(ctx, templates.ScrollIntoView(id, options)); err != nil {
|
||||
m.logger.Trace().Err(err).Msg("failed to scroll to an element")
|
||||
|
||||
return err
|
||||
@ -118,15 +114,15 @@ func (m *Manager) ScrollIntoView(ctx context.Context, objectID runtime.RemoteObj
|
||||
return nil
|
||||
}
|
||||
|
||||
func (m *Manager) ScrollIntoViewBySelector(ctx context.Context, selector string, options drivers.ScrollOptions) error {
|
||||
func (m *Manager) ScrollIntoViewBySelector(ctx context.Context, id runtime.RemoteObjectID, selector values.String, options drivers.ScrollOptions) error {
|
||||
m.logger.Trace().
|
||||
Str("selector", selector).
|
||||
Str("selector", selector.String()).
|
||||
Str("behavior", options.Behavior.String()).
|
||||
Str("block", options.Block.String()).
|
||||
Str("inline", options.Inline.String()).
|
||||
Msg("scrolling to an element by selector")
|
||||
|
||||
if err := m.exec.Eval(ctx, templates.ScrollIntoViewBySelector(selector, options)); err != nil {
|
||||
if err := m.exec.Eval(ctx, templates.ScrollIntoViewBySelector(id, selector, options)); err != nil {
|
||||
m.logger.Trace().Err(err).Msg("failed to scroll to an element by selector")
|
||||
|
||||
return err
|
||||
@ -137,19 +133,16 @@ func (m *Manager) ScrollIntoViewBySelector(ctx context.Context, selector string,
|
||||
return nil
|
||||
}
|
||||
|
||||
func (m *Manager) ScrollByXY(ctx context.Context, x, y float64, options drivers.ScrollOptions) error {
|
||||
func (m *Manager) ScrollByXY(ctx context.Context, options drivers.ScrollOptions) error {
|
||||
m.logger.Trace().
|
||||
Float64("x", x).
|
||||
Float64("y", y).
|
||||
Float64("x", float64(options.Top)).
|
||||
Float64("y", float64(options.Left)).
|
||||
Str("behavior", options.Behavior.String()).
|
||||
Str("block", options.Block.String()).
|
||||
Str("inline", options.Inline.String()).
|
||||
Msg("scrolling to an element by given coordinates")
|
||||
|
||||
if err := m.exec.Eval(
|
||||
ctx,
|
||||
templates.Scroll(eval.ParamFloat(x), eval.ParamFloat(y), options),
|
||||
); err != nil {
|
||||
if err := m.exec.Eval(ctx, templates.Scroll(options)); err != nil {
|
||||
m.logger.Trace().Err(err).Msg("failed to scroll to an element by coordinates")
|
||||
|
||||
return err
|
||||
@ -186,13 +179,13 @@ func (m *Manager) Focus(ctx context.Context, objectID runtime.RemoteObjectID) er
|
||||
return nil
|
||||
}
|
||||
|
||||
func (m *Manager) FocusBySelector(ctx context.Context, parentNodeID dom.NodeID, selector string) error {
|
||||
func (m *Manager) FocusBySelector(ctx context.Context, id runtime.RemoteObjectID, selector values.String) error {
|
||||
m.logger.Trace().
|
||||
Int("parent_node_id", int(parentNodeID)).
|
||||
Str("selector", selector).
|
||||
Str("parent_object_id", string(id)).
|
||||
Str("selector", selector.String()).
|
||||
Msg("focusing on an element by selector")
|
||||
|
||||
err := m.ScrollIntoViewBySelector(ctx, selector, drivers.ScrollOptions{
|
||||
err := m.ScrollIntoViewBySelector(ctx, id, selector, drivers.ScrollOptions{
|
||||
Behavior: drivers.ScrollBehaviorAuto,
|
||||
Block: drivers.ScrollVerticalAlignmentCenter,
|
||||
Inline: drivers.ScrollHorizontalAlignmentCenter,
|
||||
@ -204,7 +197,7 @@ func (m *Manager) FocusBySelector(ctx context.Context, parentNodeID dom.NodeID,
|
||||
|
||||
m.logger.Trace().Msg("resolving an element by selector")
|
||||
|
||||
found, err := m.client.DOM.QuerySelector(ctx, dom.NewQuerySelectorArgs(parentNodeID, selector))
|
||||
found, err := m.exec.EvalRef(ctx, templates.QuerySelector(id, selector))
|
||||
|
||||
if err != nil {
|
||||
m.logger.Trace().
|
||||
@ -214,7 +207,15 @@ func (m *Manager) FocusBySelector(ctx context.Context, parentNodeID dom.NodeID,
|
||||
return err
|
||||
}
|
||||
|
||||
if err := m.client.DOM.Focus(ctx, dom.NewFocusArgs().SetNodeID(found.NodeID)); err != nil {
|
||||
if found.ObjectID == nil {
|
||||
m.logger.Trace().
|
||||
Err(core.ErrNotFound).
|
||||
Msg("element not found by selector")
|
||||
|
||||
return core.ErrNotFound
|
||||
}
|
||||
|
||||
if err := m.client.DOM.Focus(ctx, dom.NewFocusArgs().SetObjectID(*found.ObjectID)); err != nil {
|
||||
m.logger.Trace().
|
||||
Err(err).
|
||||
Msg("failed focusing on an element by selector")
|
||||
@ -232,7 +233,7 @@ func (m *Manager) Blur(ctx context.Context, objectID runtime.RemoteObjectID) err
|
||||
Str("object_id", string(objectID)).
|
||||
Msg("removing focus from an element")
|
||||
|
||||
if err := m.exec.Eval(ctx, templates.Blur(), eval.WithArgRef(objectID)); err != nil {
|
||||
if err := m.exec.Eval(ctx, templates.Blur(objectID)); err != nil {
|
||||
m.logger.Trace().
|
||||
Err(err).
|
||||
Msg("failed removing focus from an element")
|
||||
@ -245,13 +246,13 @@ func (m *Manager) Blur(ctx context.Context, objectID runtime.RemoteObjectID) err
|
||||
return nil
|
||||
}
|
||||
|
||||
func (m *Manager) BlurBySelector(ctx context.Context, parentObjectID runtime.RemoteObjectID, selector string) error {
|
||||
func (m *Manager) BlurBySelector(ctx context.Context, id runtime.RemoteObjectID, selector values.String) error {
|
||||
m.logger.Trace().
|
||||
Str("parent_object_id", string(parentObjectID)).
|
||||
Str("selector", selector).
|
||||
Str("parent_object_id", string(id)).
|
||||
Str("selector", selector.String()).
|
||||
Msg("removing focus from an element by selector")
|
||||
|
||||
if err := m.exec.Eval(ctx, templates.BlurBySelector(selector), eval.WithArgRef(parentObjectID)); err != nil {
|
||||
if err := m.exec.Eval(ctx, templates.BlurBySelector(id, selector)); err != nil {
|
||||
m.logger.Trace().
|
||||
Err(err).
|
||||
Msg("failed removing focus from an element by selector")
|
||||
@ -298,19 +299,19 @@ func (m *Manager) MoveMouse(ctx context.Context, objectID runtime.RemoteObjectID
|
||||
return nil
|
||||
}
|
||||
|
||||
func (m *Manager) MoveMouseBySelector(ctx context.Context, parentNodeID dom.NodeID, selector string) error {
|
||||
func (m *Manager) MoveMouseBySelector(ctx context.Context, id runtime.RemoteObjectID, selector values.String) error {
|
||||
m.logger.Trace().
|
||||
Int("parent_node_id", int(parentNodeID)).
|
||||
Str("selector", selector).
|
||||
Str("parent_object_id", string(id)).
|
||||
Str("selector", selector.String()).
|
||||
Msg("starting to move the mouse towards an element by selector")
|
||||
|
||||
if err := m.ScrollIntoViewBySelector(ctx, selector, drivers.ScrollOptions{}); err != nil {
|
||||
if err := m.ScrollIntoViewBySelector(ctx, id, selector, drivers.ScrollOptions{}); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
m.logger.Trace().Msg("looking up for an element by selector")
|
||||
|
||||
found, err := m.client.DOM.QuerySelector(ctx, dom.NewQuerySelectorArgs(parentNodeID, selector))
|
||||
found, err := m.exec.EvalRef(ctx, templates.QuerySelector(id, selector))
|
||||
|
||||
if err != nil {
|
||||
m.logger.Trace().Err(err).Msg("failed to find an element by selector")
|
||||
@ -318,9 +319,17 @@ func (m *Manager) MoveMouseBySelector(ctx context.Context, parentNodeID dom.Node
|
||||
return err
|
||||
}
|
||||
|
||||
m.logger.Trace().Int("node_id", int(found.NodeID)).Msg("calculating clickable element points")
|
||||
if found.ObjectID == nil {
|
||||
m.logger.Trace().
|
||||
Err(core.ErrNotFound).
|
||||
Msg("element not found by selector")
|
||||
|
||||
points, err := GetClickablePointByNodeID(ctx, m.client, found.NodeID)
|
||||
return core.ErrNotFound
|
||||
}
|
||||
|
||||
m.logger.Trace().Str("object_id", string(*found.ObjectID)).Msg("calculating clickable element points")
|
||||
|
||||
points, err := GetClickablePointByObjectID(ctx, m.client, *found.ObjectID)
|
||||
|
||||
if err != nil {
|
||||
m.logger.Trace().Err(err).Msg("failed calculating clickable element points")
|
||||
@ -341,13 +350,19 @@ func (m *Manager) MoveMouseBySelector(ctx context.Context, parentNodeID dom.Node
|
||||
return nil
|
||||
}
|
||||
|
||||
func (m *Manager) MoveMouseByXY(ctx context.Context, x, y float64) error {
|
||||
func (m *Manager) MoveMouseByXY(ctx context.Context, xv, yv values.Float) error {
|
||||
x := float64(xv)
|
||||
y := float64(yv)
|
||||
|
||||
m.logger.Trace().
|
||||
Float64("x", x).
|
||||
Float64("y", y).
|
||||
Msg("starting to move the mouse towards an element by given coordinates")
|
||||
|
||||
if err := m.ScrollByXY(ctx, x, y, drivers.ScrollOptions{}); err != nil {
|
||||
if err := m.ScrollByXY(ctx, drivers.ScrollOptions{
|
||||
Top: xv,
|
||||
Left: yv,
|
||||
}); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
@ -404,14 +419,14 @@ func (m *Manager) Click(ctx context.Context, objectID runtime.RemoteObjectID, co
|
||||
return nil
|
||||
}
|
||||
|
||||
func (m *Manager) ClickBySelector(ctx context.Context, parentNodeID dom.NodeID, selector string, count int) error {
|
||||
func (m *Manager) ClickBySelector(ctx context.Context, id runtime.RemoteObjectID, selector values.String, count values.Int) error {
|
||||
m.logger.Trace().
|
||||
Int("parent_node_id", int(parentNodeID)).
|
||||
Str("selector", selector).
|
||||
Int("count", count).
|
||||
Str("parent_object_id", string(id)).
|
||||
Str("selector", string(selector)).
|
||||
Int("count", int(count)).
|
||||
Msg("starting to click on an element by selector")
|
||||
|
||||
if err := m.ScrollIntoViewBySelector(ctx, selector, drivers.ScrollOptions{
|
||||
if err := m.ScrollIntoViewBySelector(ctx, id, selector, drivers.ScrollOptions{
|
||||
Behavior: drivers.ScrollBehaviorAuto,
|
||||
Block: drivers.ScrollVerticalAlignmentCenter,
|
||||
Inline: drivers.ScrollHorizontalAlignmentCenter,
|
||||
@ -421,7 +436,7 @@ func (m *Manager) ClickBySelector(ctx context.Context, parentNodeID dom.NodeID,
|
||||
|
||||
m.logger.Trace().Msg("looking up for an element by selector")
|
||||
|
||||
found, err := m.client.DOM.QuerySelector(ctx, dom.NewQuerySelectorArgs(parentNodeID, selector))
|
||||
found, err := m.exec.EvalRef(ctx, templates.QuerySelector(id, selector))
|
||||
|
||||
if err != nil {
|
||||
m.logger.Trace().Err(err).Msg("failed to find an element by selector")
|
||||
@ -429,9 +444,17 @@ func (m *Manager) ClickBySelector(ctx context.Context, parentNodeID dom.NodeID,
|
||||
return err
|
||||
}
|
||||
|
||||
m.logger.Trace().Int("node_id", int(found.NodeID)).Msg("calculating clickable element points")
|
||||
if found.ObjectID == nil {
|
||||
m.logger.Trace().
|
||||
Err(core.ErrNotFound).
|
||||
Msg("element not found by selector")
|
||||
|
||||
points, err := GetClickablePointByNodeID(ctx, m.client, found.NodeID)
|
||||
return core.ErrNotFound
|
||||
}
|
||||
|
||||
m.logger.Trace().Str("object_id", string(*found.ObjectID)).Msg("calculating clickable element points")
|
||||
|
||||
points, err := GetClickablePointByObjectID(ctx, m.client, *found.ObjectID)
|
||||
|
||||
if err != nil {
|
||||
m.logger.Trace().Err(err).Msg("failed calculating clickable element points")
|
||||
@ -443,7 +466,7 @@ func (m *Manager) ClickBySelector(ctx context.Context, parentNodeID dom.NodeID,
|
||||
|
||||
delay := time.Duration(drivers.DefaultMouseDelay) * time.Millisecond
|
||||
|
||||
if err := m.mouse.ClickWithCount(ctx, points.X, points.Y, delay, count); err != nil {
|
||||
if err := m.mouse.ClickWithCount(ctx, points.X, points.Y, delay, int(count)); err != nil {
|
||||
m.logger.Trace().Err(err).Msg("failed to click on an element")
|
||||
return nil
|
||||
}
|
||||
@ -453,63 +476,72 @@ func (m *Manager) ClickBySelector(ctx context.Context, parentNodeID dom.NodeID,
|
||||
return nil
|
||||
}
|
||||
|
||||
func (m *Manager) ClickBySelectorAll(ctx context.Context, parentNodeID dom.NodeID, selector string, count int) error {
|
||||
m.logger.Trace().
|
||||
Int("parent_node_id", int(parentNodeID)).
|
||||
Str("selector", selector).
|
||||
Int("count", count).
|
||||
Msg("starting to click on elements by selector")
|
||||
|
||||
if err := m.ScrollIntoViewBySelector(ctx, selector, drivers.ScrollOptions{
|
||||
Behavior: drivers.ScrollBehaviorAuto,
|
||||
Block: drivers.ScrollVerticalAlignmentCenter,
|
||||
Inline: drivers.ScrollHorizontalAlignmentCenter,
|
||||
}); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
m.logger.Trace().Msg("looking up for elements by selector")
|
||||
|
||||
found, err := m.client.DOM.QuerySelectorAll(ctx, dom.NewQuerySelectorAllArgs(parentNodeID, selector))
|
||||
|
||||
if err != nil {
|
||||
m.logger.Trace().Err(err).Msg("failed to find elements by selector")
|
||||
|
||||
return err
|
||||
}
|
||||
|
||||
for idx, nodeID := range found.NodeIDs {
|
||||
if idx > 0 {
|
||||
m.logger.Trace().Msg("pausing")
|
||||
beforeClickDelay := time.Duration(core.NumberLowerBoundary(drivers.DefaultMouseDelay*10)) * time.Millisecond
|
||||
|
||||
time.Sleep(beforeClickDelay)
|
||||
}
|
||||
|
||||
m.logger.Trace().Int("node_id", int(nodeID)).Msg("calculating clickable element points")
|
||||
|
||||
points, err := GetClickablePointByNodeID(ctx, m.client, nodeID)
|
||||
|
||||
if err != nil {
|
||||
m.logger.Trace().Err(err).Msg("failed calculating clickable element points")
|
||||
|
||||
return err
|
||||
}
|
||||
|
||||
m.logger.Trace().Float64("x", points.X).Float64("y", points.Y).Msg("calculated clickable element points")
|
||||
|
||||
delay := time.Duration(drivers.DefaultMouseDelay) * time.Millisecond
|
||||
|
||||
if err := m.mouse.ClickWithCount(ctx, points.X, points.Y, delay, count); err != nil {
|
||||
m.logger.Trace().Err(err).Msg("failed to click on an element")
|
||||
return nil
|
||||
}
|
||||
|
||||
m.logger.Trace().Msg("clicked on an element")
|
||||
}
|
||||
|
||||
m.logger.Trace().Msg("clicked on all elements")
|
||||
|
||||
func (m *Manager) ClickBySelectorAll(_ context.Context, _ runtime.RemoteObjectID, _ values.String, _ values.Int) error {
|
||||
// TODO: Use dom.QueryManager
|
||||
//m.logger.Trace().
|
||||
// Str("parent_object_id", string(id)).
|
||||
// Str("selector", string(selector)).
|
||||
// Int("count", int(count)).
|
||||
// Msg("starting to click on elements by selector")
|
||||
//
|
||||
//if err := m.ScrollIntoViewBySelector(ctx, id, selector, drivers.ScrollOptions{
|
||||
// Behavior: drivers.ScrollBehaviorAuto,
|
||||
// Block: drivers.ScrollVerticalAlignmentCenter,
|
||||
// Inline: drivers.ScrollHorizontalAlignmentCenter,
|
||||
//}); err != nil {
|
||||
// return err
|
||||
//}
|
||||
//
|
||||
//m.logger.Trace().Msg("looking up for elements by selector")
|
||||
//
|
||||
//found, err := m.exec.EvalRef(ctx, templates.QuerySelectorAll(id, selector))
|
||||
//
|
||||
//if err != nil {
|
||||
// m.logger.Trace().Err(err).Msg("failed to find an element by selector")
|
||||
//
|
||||
// return err
|
||||
//}
|
||||
//
|
||||
//if found.ObjectID == nil {
|
||||
// m.logger.Trace().
|
||||
// Err(core.ErrNotFound).
|
||||
// Msg("element not found by selector")
|
||||
//
|
||||
// return core.ErrNotFound
|
||||
//}
|
||||
//
|
||||
//for idx, nodeID := range found.NodeIDs {
|
||||
// if idx > 0 {
|
||||
// m.logger.Trace().Msg("pausing")
|
||||
// beforeClickDelay := time.Duration(core.NumberLowerBoundary(drivers.DefaultMouseDelay*10)) * time.Millisecond
|
||||
//
|
||||
// time.Sleep(beforeClickDelay)
|
||||
// }
|
||||
//
|
||||
// m.logger.Trace().Int("object_id", int(nodeID)).Msg("calculating clickable element points")
|
||||
//
|
||||
// points, err := GetClickablePointByNodeID(ctx, m.client, nodeID)
|
||||
//
|
||||
// if err != nil {
|
||||
// m.logger.Trace().Err(err).Msg("failed calculating clickable element points")
|
||||
//
|
||||
// return err
|
||||
// }
|
||||
//
|
||||
// m.logger.Trace().Float64("x", points.X).Float64("y", points.Y).Msg("calculated clickable element points")
|
||||
//
|
||||
// delay := time.Duration(drivers.DefaultMouseDelay) * time.Millisecond
|
||||
//
|
||||
// if err := m.mouse.ClickWithCount(ctx, points.X, points.Y, delay, count); err != nil {
|
||||
// m.logger.Trace().Err(err).Msg("failed to click on an element")
|
||||
// return nil
|
||||
// }
|
||||
//
|
||||
// m.logger.Trace().Msg("clicked on an element")
|
||||
//}
|
||||
//
|
||||
//m.logger.Trace().Msg("clicked on all elements")
|
||||
//
|
||||
return nil
|
||||
}
|
||||
|
||||
@ -576,13 +608,13 @@ func (m *Manager) Type(ctx context.Context, objectID runtime.RemoteObjectID, par
|
||||
return nil
|
||||
}
|
||||
|
||||
func (m *Manager) TypeBySelector(ctx context.Context, parentNodeID dom.NodeID, selector string, params TypeParams) error {
|
||||
func (m *Manager) TypeBySelector(ctx context.Context, id runtime.RemoteObjectID, selector values.String, params TypeParams) error {
|
||||
m.logger.Trace().
|
||||
Int("parent_node_id", int(parentNodeID)).
|
||||
Str("selector", selector).
|
||||
Str("parent_object_id", string(id)).
|
||||
Str("selector", string(selector)).
|
||||
Msg("starting to type text by selector")
|
||||
|
||||
err := m.ScrollIntoViewBySelector(ctx, selector, drivers.ScrollOptions{
|
||||
err := m.ScrollIntoViewBySelector(ctx, id, selector, drivers.ScrollOptions{
|
||||
Behavior: drivers.ScrollBehaviorAuto,
|
||||
Block: drivers.ScrollVerticalAlignmentCenter,
|
||||
Inline: drivers.ScrollHorizontalAlignmentCenter,
|
||||
@ -594,7 +626,7 @@ func (m *Manager) TypeBySelector(ctx context.Context, parentNodeID dom.NodeID, s
|
||||
|
||||
m.logger.Trace().Msg("looking up for an element by selector")
|
||||
|
||||
found, err := m.client.DOM.QuerySelector(ctx, dom.NewQuerySelectorArgs(parentNodeID, selector))
|
||||
found, err := m.exec.EvalRef(ctx, templates.QuerySelector(id, selector))
|
||||
|
||||
if err != nil {
|
||||
m.logger.Trace().Err(err).Msg("failed to find an element by selector")
|
||||
@ -602,9 +634,17 @@ func (m *Manager) TypeBySelector(ctx context.Context, parentNodeID dom.NodeID, s
|
||||
return err
|
||||
}
|
||||
|
||||
m.logger.Trace().Int("node_id", int(found.NodeID)).Msg("focusing on an element")
|
||||
if found.ObjectID == nil {
|
||||
m.logger.Trace().
|
||||
Err(core.ErrNotFound).
|
||||
Msg("element not found by selector")
|
||||
|
||||
err = m.client.DOM.Focus(ctx, dom.NewFocusArgs().SetNodeID(found.NodeID))
|
||||
return core.ErrNotFound
|
||||
}
|
||||
|
||||
m.logger.Trace().Str("object_id", string(*found.ObjectID)).Msg("focusing on an element")
|
||||
|
||||
err = m.client.DOM.Focus(ctx, dom.NewFocusArgs().SetObjectID(*found.ObjectID))
|
||||
|
||||
if err != nil {
|
||||
m.logger.Trace().Err(err).Msg("failed to focus on an element")
|
||||
@ -617,7 +657,7 @@ func (m *Manager) TypeBySelector(ctx context.Context, parentNodeID dom.NodeID, s
|
||||
if params.Clear {
|
||||
m.logger.Trace().Msg("calculating clickable element points")
|
||||
|
||||
points, err := GetClickablePointByNodeID(ctx, m.client, found.NodeID)
|
||||
points, err := GetClickablePointByObjectID(ctx, m.client, *found.ObjectID)
|
||||
|
||||
if err != nil {
|
||||
m.logger.Trace().Err(err).Msg("failed calculating clickable element points")
|
||||
@ -701,13 +741,13 @@ func (m *Manager) Clear(ctx context.Context, objectID runtime.RemoteObjectID) er
|
||||
return nil
|
||||
}
|
||||
|
||||
func (m *Manager) ClearBySelector(ctx context.Context, parentNodeID dom.NodeID, selector string) error {
|
||||
func (m *Manager) ClearBySelector(ctx context.Context, id runtime.RemoteObjectID, selector values.String) error {
|
||||
m.logger.Trace().
|
||||
Int("parent_node_id", int(parentNodeID)).
|
||||
Str("selector", selector).
|
||||
Str("parent_object_id", string(id)).
|
||||
Str("selector", string(selector)).
|
||||
Msg("starting to clear element by selector")
|
||||
|
||||
err := m.ScrollIntoViewBySelector(ctx, selector, drivers.ScrollOptions{
|
||||
err := m.ScrollIntoViewBySelector(ctx, id, selector, drivers.ScrollOptions{
|
||||
Behavior: drivers.ScrollBehaviorAuto,
|
||||
Block: drivers.ScrollVerticalAlignmentCenter,
|
||||
Inline: drivers.ScrollHorizontalAlignmentCenter,
|
||||
@ -719,7 +759,7 @@ func (m *Manager) ClearBySelector(ctx context.Context, parentNodeID dom.NodeID,
|
||||
|
||||
m.logger.Trace().Msg("looking up for an element by selector")
|
||||
|
||||
found, err := m.client.DOM.QuerySelector(ctx, dom.NewQuerySelectorArgs(parentNodeID, selector))
|
||||
found, err := m.exec.EvalRef(ctx, templates.QuerySelector(id, selector))
|
||||
|
||||
if err != nil {
|
||||
m.logger.Trace().Err(err).Msg("failed to find an element by selector")
|
||||
@ -727,9 +767,17 @@ func (m *Manager) ClearBySelector(ctx context.Context, parentNodeID dom.NodeID,
|
||||
return err
|
||||
}
|
||||
|
||||
m.logger.Trace().Int("node_id", int(found.NodeID)).Msg("calculating clickable element points")
|
||||
if found.ObjectID == nil {
|
||||
m.logger.Trace().
|
||||
Err(core.ErrNotFound).
|
||||
Msg("element not found by selector")
|
||||
|
||||
points, err := GetClickablePointByNodeID(ctx, m.client, found.NodeID)
|
||||
return core.ErrNotFound
|
||||
}
|
||||
|
||||
m.logger.Trace().Str("object_id", string(*found.ObjectID)).Msg("calculating clickable element points")
|
||||
|
||||
points, err := GetClickablePointByObjectID(ctx, m.client, *found.ObjectID)
|
||||
|
||||
if err != nil {
|
||||
m.logger.Trace().Err(err).Msg("failed calculating clickable element points")
|
||||
@ -741,7 +789,7 @@ func (m *Manager) ClearBySelector(ctx context.Context, parentNodeID dom.NodeID,
|
||||
|
||||
m.logger.Trace().Msg("focusing on an element")
|
||||
|
||||
err = m.client.DOM.Focus(ctx, dom.NewFocusArgs().SetNodeID(found.NodeID))
|
||||
err = m.client.DOM.Focus(ctx, dom.NewFocusArgs().SetObjectID(*found.ObjectID))
|
||||
|
||||
if err != nil {
|
||||
m.logger.Trace().Err(err).Msg("failed to focus on an element")
|
||||
@ -811,38 +859,34 @@ func (m *Manager) Press(ctx context.Context, keys []string, count int) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (m *Manager) PressBySelector(ctx context.Context, parentNodeID dom.NodeID, selector string, keys []string, count int) error {
|
||||
func (m *Manager) PressBySelector(ctx context.Context, id runtime.RemoteObjectID, selector values.String, keys []string, count int) error {
|
||||
m.logger.Trace().
|
||||
Int("parent_node_id", int(parentNodeID)).
|
||||
Str("selector", selector).
|
||||
Str("parent_object_id", string(id)).
|
||||
Str("selector", string(selector)).
|
||||
Strs("keys", keys).
|
||||
Int("count", count).
|
||||
Msg("starting to press keyboard keys by selector")
|
||||
|
||||
if err := m.FocusBySelector(ctx, parentNodeID, selector); err != nil {
|
||||
if err := m.FocusBySelector(ctx, id, selector); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return m.Press(ctx, keys, count)
|
||||
}
|
||||
|
||||
func (m *Manager) Select(ctx context.Context, objectID runtime.RemoteObjectID, value *values.Array) (*values.Array, error) {
|
||||
func (m *Manager) Select(ctx context.Context, id runtime.RemoteObjectID, value *values.Array) (*values.Array, error) {
|
||||
m.logger.Trace().
|
||||
Str("object_id", string(objectID)).
|
||||
Str("object_id", string(id)).
|
||||
Msg("starting to select values")
|
||||
|
||||
if err := m.Focus(ctx, objectID); err != nil {
|
||||
if err := m.Focus(ctx, id); err != nil {
|
||||
return values.NewArray(0), err
|
||||
}
|
||||
|
||||
m.logger.Trace().Msg("selecting values")
|
||||
m.logger.Trace().Msg("evaluating a JS function")
|
||||
|
||||
val, err := m.exec.EvalValue(
|
||||
ctx,
|
||||
templates.Select(value.String()),
|
||||
eval.WithArgRef(objectID),
|
||||
)
|
||||
val, err := m.exec.EvalValue(ctx, templates.Select(id, value))
|
||||
|
||||
if err != nil {
|
||||
m.logger.Trace().Err(err).Msg("failed to evaluate a JS function")
|
||||
@ -865,20 +909,20 @@ func (m *Manager) Select(ctx context.Context, objectID runtime.RemoteObjectID, v
|
||||
return arr, nil
|
||||
}
|
||||
|
||||
func (m *Manager) SelectBySelector(ctx context.Context, parentNodeID dom.NodeID, selector string, value *values.Array) (*values.Array, error) {
|
||||
func (m *Manager) SelectBySelector(ctx context.Context, id runtime.RemoteObjectID, selector values.String, value *values.Array) (*values.Array, error) {
|
||||
m.logger.Trace().
|
||||
Int("parent_node_id", int(parentNodeID)).
|
||||
Str("selector", selector).
|
||||
Str("parent_object_id", string(id)).
|
||||
Str("selector", string(selector)).
|
||||
Msg("starting to select values by selector")
|
||||
|
||||
if err := m.FocusBySelector(ctx, parentNodeID, selector); err != nil {
|
||||
if err := m.FocusBySelector(ctx, id, selector); err != nil {
|
||||
return values.NewArray(0), err
|
||||
}
|
||||
|
||||
m.logger.Trace().Msg("selecting values")
|
||||
m.logger.Trace().Msg("evaluating a JS function")
|
||||
|
||||
res, err := m.exec.EvalValue(ctx, templates.SelectBySelector(selector, value.String()))
|
||||
res, err := m.exec.EvalValue(ctx, templates.SelectBySelector(id, selector, value))
|
||||
|
||||
if err != nil {
|
||||
m.logger.Trace().Err(err).Msg("failed to evaluate a JS function")
|
||||
|
@ -605,7 +605,7 @@ func (m *Manager) WaitForFrameNavigation(ctx context.Context, frameID page.Frame
|
||||
if ctx.Err() == nil {
|
||||
log.Trace().Msg("creating frame execution context")
|
||||
|
||||
ec, err := eval.New(ctx, m.client, repl.Frame.ID)
|
||||
ec, err := eval.New(ctx, m.logger, m.client, repl.Frame.ID)
|
||||
|
||||
if err != nil {
|
||||
log.Trace().Err(err).Msg("failed to create frame execution context")
|
||||
|
@ -277,7 +277,13 @@ func (p *HTMLPage) Close() error {
|
||||
p.mu.Lock()
|
||||
defer p.mu.Unlock()
|
||||
|
||||
url := p.dom.GetMainFrame().GetURL().String()
|
||||
var url string
|
||||
frame := p.dom.GetMainFrame()
|
||||
|
||||
if frame != nil {
|
||||
url = frame.GetURL().String()
|
||||
}
|
||||
|
||||
p.closed = values.True
|
||||
|
||||
err := p.dom.Close()
|
||||
@ -321,7 +327,7 @@ func (p *HTMLPage) IsClosed() values.Boolean {
|
||||
}
|
||||
|
||||
func (p *HTMLPage) GetURL() values.String {
|
||||
res, err := p.getCurrentDocument().Eval(context.Background(), templates.GetURL())
|
||||
res, err := p.getCurrentDocument().Eval(context.Background(), templates.GetURL().String())
|
||||
|
||||
if err == nil {
|
||||
return values.ToString(res)
|
||||
|
@ -2,15 +2,88 @@ package templates
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
"github.com/MontFerret/ferret/pkg/drivers/cdp/eval"
|
||||
|
||||
"github.com/MontFerret/ferret/pkg/runtime/values"
|
||||
"github.com/mafredri/cdp/protocol/runtime"
|
||||
)
|
||||
|
||||
func AttributeRead(name values.String) string {
|
||||
n := name.String()
|
||||
return fmt.Sprintf(`
|
||||
el.attributes[%s] != null ? el.attributes[%s].value : null
|
||||
`, eval.ParamString(n), eval.ParamString(n))
|
||||
const getAttribute = `(el, name) => {
|
||||
return el.getAttribute(name)
|
||||
}`
|
||||
|
||||
func GetAttribute(id runtime.RemoteObjectID, name values.String) *eval.Function {
|
||||
if name == "style" {
|
||||
return GetStyles(id)
|
||||
}
|
||||
|
||||
return eval.F(getAttribute).WithArgRef(id).WithArgValue(name)
|
||||
}
|
||||
|
||||
var getAttributes = fmt.Sprintf(`(element) => {
|
||||
const getStyles = %s;
|
||||
return element.getAttributeNames().reduce((res, name) => {
|
||||
const out = res;
|
||||
let value;
|
||||
|
||||
if (name !== "style") {
|
||||
value = element.getAttribute(name);
|
||||
} else {
|
||||
value = getStyles(element);
|
||||
}
|
||||
|
||||
out[name] = value;
|
||||
|
||||
return out;
|
||||
}, {});
|
||||
}`, getStyles)
|
||||
|
||||
func GetAttributes(id runtime.RemoteObjectID) *eval.Function {
|
||||
return eval.F(getAttributes).WithArgRef(id)
|
||||
}
|
||||
|
||||
const setAttribute = `(el, name, value) => {
|
||||
el.setAttribute(name, value)
|
||||
}`
|
||||
|
||||
func SetAttribute(id runtime.RemoteObjectID, name, value values.String) *eval.Function {
|
||||
return eval.F(setAttribute).WithArgRef(id).WithArgValue(name).WithArgValue(value)
|
||||
}
|
||||
|
||||
const setAttributes = `(el, values) => {
|
||||
Object.keys(values).forEach((name) => {
|
||||
const value = values[name];
|
||||
el.setAttribute(name, value)
|
||||
});
|
||||
}`
|
||||
|
||||
func SetAttributes(id runtime.RemoteObjectID, values *values.Object) *eval.Function {
|
||||
return eval.F(setAttributes).WithArgRef(id).WithArgValue(values)
|
||||
}
|
||||
|
||||
const removeAttribute = `(el, name) => {
|
||||
el.removeAttribute(name)
|
||||
}`
|
||||
|
||||
func RemoveAttribute(id runtime.RemoteObjectID, name values.String) *eval.Function {
|
||||
return eval.F(removeAttribute).WithArgRef(id).WithArgValue(name)
|
||||
}
|
||||
|
||||
const removeAttributes = `(el, names) => {
|
||||
names.forEach(name => el.removeAttribute(name));
|
||||
}`
|
||||
|
||||
func RemoveAttributes(id runtime.RemoteObjectID, names []values.String) *eval.Function {
|
||||
return eval.F(removeAttributes).WithArgRef(id).WithArg(names)
|
||||
}
|
||||
|
||||
const getNodeType = `(el) => el.nodeType`
|
||||
|
||||
func GetNodeType(id runtime.RemoteObjectID) *eval.Function {
|
||||
return eval.F(getNodeType).WithArgRef(id)
|
||||
}
|
||||
|
||||
const getNodeName = `(el) => el.nodeName`
|
||||
|
||||
func GetNodeName(id runtime.RemoteObjectID) *eval.Function {
|
||||
return eval.F(getNodeName).WithArgRef(id)
|
||||
}
|
||||
|
@ -3,26 +3,31 @@ package templates
|
||||
import (
|
||||
"fmt"
|
||||
"github.com/MontFerret/ferret/pkg/drivers"
|
||||
"github.com/MontFerret/ferret/pkg/drivers/cdp/eval"
|
||||
"github.com/MontFerret/ferret/pkg/runtime/values"
|
||||
"github.com/mafredri/cdp/protocol/runtime"
|
||||
)
|
||||
|
||||
func Blur() string {
|
||||
return `
|
||||
(el) => {
|
||||
el.blur()
|
||||
}
|
||||
`
|
||||
const blur = `(el) => {
|
||||
el.blur()
|
||||
}`
|
||||
|
||||
func Blur(id runtime.RemoteObjectID) *eval.Function {
|
||||
return eval.F(blur).WithArgRef(id)
|
||||
}
|
||||
|
||||
func BlurBySelector(selector string) string {
|
||||
return fmt.Sprintf(`
|
||||
(parent) => {
|
||||
const el = parent.querySelector('%s');
|
||||
var blurBySelector = fmt.Sprintf(`
|
||||
(el, selector) => {
|
||||
const found = el.querySelector(selector);
|
||||
|
||||
if (el == null) {
|
||||
throw new Error('%s')
|
||||
if (found == null) {
|
||||
throw new Error(%s)
|
||||
}
|
||||
|
||||
el.blur();
|
||||
found.blur();
|
||||
}
|
||||
`, selector, drivers.ErrNotFound)
|
||||
`, ParamErr(drivers.ErrNotFound))
|
||||
|
||||
func BlurBySelector(id runtime.RemoteObjectID, selector values.String) *eval.Function {
|
||||
return eval.F(blurBySelector).WithArgRef(id).WithArgValue(selector)
|
||||
}
|
||||
|
@ -1,23 +1,25 @@
|
||||
package templates
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"github.com/MontFerret/ferret/pkg/drivers/cdp/eval"
|
||||
"github.com/MontFerret/ferret/pkg/runtime/values"
|
||||
"github.com/mafredri/cdp/protocol/runtime"
|
||||
)
|
||||
|
||||
const getChildren = "(el) => Array.from(el.children)"
|
||||
|
||||
func GetChildren(id runtime.RemoteObjectID) *eval.Function {
|
||||
return eval.F(getChildren).WithArgRef(id)
|
||||
}
|
||||
|
||||
const getChildrenCount = "(el) => el.children.length"
|
||||
|
||||
func GetChildren() string {
|
||||
return getChildren
|
||||
func GetChildrenCount(id runtime.RemoteObjectID) *eval.Function {
|
||||
return eval.F(getChildrenCount).WithArgRef(id)
|
||||
}
|
||||
|
||||
func GetChildrenCount() string {
|
||||
return getChildrenCount
|
||||
}
|
||||
const getChildByIndex = "(el, idx) => el.children[idx]"
|
||||
|
||||
func GetChildByIndex(idx int64) string {
|
||||
return fmt.Sprintf(`
|
||||
(el) => el.children[%s]
|
||||
`, eval.ParamInt(idx))
|
||||
func GetChildByIndex(id runtime.RemoteObjectID, index values.Int) *eval.Function {
|
||||
return eval.F(getChildByIndex).WithArgRef(id).WithArgValue(index)
|
||||
}
|
||||
|
@ -1,13 +0,0 @@
|
||||
package templates
|
||||
|
||||
const domReadyTemplate = `
|
||||
if (document.readyState === 'complete') {
|
||||
return true;
|
||||
}
|
||||
|
||||
return null;
|
||||
`
|
||||
|
||||
func DOMReady() string {
|
||||
return domReadyTemplate
|
||||
}
|
@ -1,40 +0,0 @@
|
||||
package templates
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"github.com/MontFerret/ferret/pkg/drivers"
|
||||
)
|
||||
|
||||
var getInnerHTMLBySelectorTemplate = fmt.Sprintf(`
|
||||
(el, selector) => {
|
||||
const found = el.querySelector(selector);
|
||||
|
||||
if (found == null) {
|
||||
throw new Error('%s');
|
||||
}
|
||||
|
||||
return found.innerHTML;
|
||||
}
|
||||
`, drivers.ErrNotFound,
|
||||
)
|
||||
|
||||
func GetInnerHTMLBySelector() string {
|
||||
return getInnerHTMLBySelectorTemplate
|
||||
}
|
||||
|
||||
var getInnerHTMLBySelectorAllTemplate = fmt.Sprintf(`
|
||||
(el, selector) => {
|
||||
const found = el.querySelectorAll(selector);
|
||||
|
||||
if (found == null) {
|
||||
throw new Error('%s');
|
||||
}
|
||||
|
||||
return Array.from(found).map(i => i.innerHTML);
|
||||
}
|
||||
`, drivers.ErrNotFound,
|
||||
)
|
||||
|
||||
func GetInnerHTMLBySelectorAll() string {
|
||||
return getInnerHTMLBySelectorAllTemplate
|
||||
}
|
@ -1,40 +0,0 @@
|
||||
package templates
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"github.com/MontFerret/ferret/pkg/drivers"
|
||||
)
|
||||
|
||||
var getInnerTextBySelectorTemplate = fmt.Sprintf(`
|
||||
(el, selector) => {
|
||||
const found = el.querySelector(selector);
|
||||
|
||||
if (found == null) {
|
||||
throw new Error("%s");
|
||||
}
|
||||
|
||||
return found.innerText;
|
||||
}
|
||||
`, drivers.ErrNotFound,
|
||||
)
|
||||
|
||||
func GetInnerTextBySelector() string {
|
||||
return getInnerTextBySelectorTemplate
|
||||
}
|
||||
|
||||
var getInnerTextBySelectorAllTemplate = fmt.Sprintf(`
|
||||
(el, selector) => {
|
||||
const found = el.querySelectorAll(selector);
|
||||
|
||||
if (found == null) {
|
||||
throw new Error("%s");
|
||||
}
|
||||
|
||||
return Array.from(found).map(i => i.innerText);
|
||||
}
|
||||
`, drivers.ErrNotFound,
|
||||
)
|
||||
|
||||
func GetInnerTextBySelectorAll() string {
|
||||
return getInnerTextBySelectorAllTemplate
|
||||
}
|
15
pkg/drivers/cdp/templates/global.go
Normal file
15
pkg/drivers/cdp/templates/global.go
Normal file
@ -0,0 +1,15 @@
|
||||
package templates
|
||||
|
||||
import "github.com/MontFerret/ferret/pkg/drivers/cdp/eval"
|
||||
|
||||
const domReady = `() => {
|
||||
if (document.readyState === 'complete') {
|
||||
return true;
|
||||
}
|
||||
|
||||
return null;
|
||||
}`
|
||||
|
||||
func DOMReady() *eval.Function {
|
||||
return eval.F(domReady)
|
||||
}
|
@ -1,13 +1,86 @@
|
||||
package templates
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"github.com/MontFerret/ferret/pkg/drivers"
|
||||
"strconv"
|
||||
|
||||
"github.com/MontFerret/ferret/pkg/runtime/core"
|
||||
"github.com/MontFerret/ferret/pkg/runtime/values"
|
||||
)
|
||||
|
||||
func WaitEventToEqOperator(when drivers.WaitEvent) string {
|
||||
if when == drivers.WaitEventPresence {
|
||||
return "=="
|
||||
func Param(input core.Value) string {
|
||||
switch value := input.(type) {
|
||||
case values.String:
|
||||
return ParamString(value)
|
||||
case values.Float:
|
||||
return ParamFloat(value)
|
||||
case values.Int:
|
||||
return ParamInt(value)
|
||||
default:
|
||||
if value != values.None {
|
||||
return value.String()
|
||||
}
|
||||
|
||||
return "null"
|
||||
}
|
||||
}
|
||||
|
||||
func ParamList(value []core.Value) string {
|
||||
var buf bytes.Buffer
|
||||
lastIndex := len(value) - 1
|
||||
|
||||
for i, input := range value {
|
||||
switch v := input.(type) {
|
||||
case values.String:
|
||||
buf.WriteString(EscapeString(string(v)))
|
||||
default:
|
||||
buf.WriteString(v.String())
|
||||
}
|
||||
|
||||
if i != lastIndex {
|
||||
buf.WriteString(",")
|
||||
}
|
||||
}
|
||||
|
||||
return "!="
|
||||
return buf.String()
|
||||
}
|
||||
|
||||
func ParamStringList(value []values.String) string {
|
||||
var buf bytes.Buffer
|
||||
lastIndex := len(value) - 1
|
||||
|
||||
for i, input := range value {
|
||||
buf.WriteString(EscapeString(string(input)))
|
||||
|
||||
if i != lastIndex {
|
||||
buf.WriteString(",")
|
||||
}
|
||||
}
|
||||
|
||||
return buf.String()
|
||||
}
|
||||
|
||||
func ParamString(value values.String) string {
|
||||
return EscapeString(string(value))
|
||||
}
|
||||
|
||||
func ParamErr(err error) string {
|
||||
return EscapeString(err.Error())
|
||||
}
|
||||
|
||||
func ParamFloat(value values.Float) string {
|
||||
return strconv.FormatFloat(float64(value), 'f', 6, 64)
|
||||
}
|
||||
|
||||
func ParamInt(value values.Int) string {
|
||||
return strconv.Itoa(int(value))
|
||||
}
|
||||
|
||||
func EscapeString(value string) string {
|
||||
return "`" + value + "`"
|
||||
}
|
||||
|
||||
func flipWhen(when drivers.WaitEvent) drivers.WaitEvent {
|
||||
return drivers.WaitEvent((int(when) + 1) % 2)
|
||||
}
|
||||
|
67
pkg/drivers/cdp/templates/inner_html.go
Normal file
67
pkg/drivers/cdp/templates/inner_html.go
Normal file
@ -0,0 +1,67 @@
|
||||
package templates
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"github.com/MontFerret/ferret/pkg/drivers"
|
||||
"github.com/MontFerret/ferret/pkg/drivers/cdp/eval"
|
||||
"github.com/MontFerret/ferret/pkg/runtime/values"
|
||||
"github.com/mafredri/cdp/protocol/runtime"
|
||||
)
|
||||
|
||||
const setInnerHTML = `(el, value) => {
|
||||
el.innerHTML = value;
|
||||
}`
|
||||
|
||||
func SetInnerHTML(id runtime.RemoteObjectID, value values.String) *eval.Function {
|
||||
return eval.F(setInnerHTML).WithArgRef(id).WithArgValue(value)
|
||||
}
|
||||
|
||||
const getInnerHTML = `(el) => {
|
||||
if (el.nodeType !== 9) {
|
||||
return el.innerHTML;
|
||||
}
|
||||
|
||||
return document.documentElement.innerHTML;
|
||||
}`
|
||||
|
||||
func GetInnerHTML(id runtime.RemoteObjectID) *eval.Function {
|
||||
return eval.F(getInnerHTML).WithArgRef(id)
|
||||
}
|
||||
|
||||
var setInnerHTMLBySelector = fmt.Sprintf(`(el, selector, value) => {
|
||||
const found = el.querySelector(selector);
|
||||
|
||||
if (found == null) {
|
||||
throw new Error(%s);
|
||||
}
|
||||
|
||||
found.innerHTML = value;
|
||||
}`, ParamErr(drivers.ErrNotFound))
|
||||
|
||||
func SetInnerHTMLBySelector(id runtime.RemoteObjectID, selector, value values.String) *eval.Function {
|
||||
return eval.F(setInnerHTMLBySelector).WithArgRef(id).WithArgValue(selector).WithArgValue(value)
|
||||
}
|
||||
|
||||
var getInnerHTMLBySelector = fmt.Sprintf(`(el, selector) => {
|
||||
const found = el.querySelector(selector);
|
||||
|
||||
if (found == null) {
|
||||
throw new Error(%s);
|
||||
}
|
||||
|
||||
return found.innerHTML;
|
||||
}`, ParamErr(drivers.ErrNotFound))
|
||||
|
||||
func GetInnerHTMLBySelector(id runtime.RemoteObjectID, selector values.String) *eval.Function {
|
||||
return eval.F(getInnerHTMLBySelector).WithArgRef(id).WithArgValue(selector)
|
||||
}
|
||||
|
||||
const getInnerHTMLBySelectorAll = `(el, selector) => {
|
||||
const found = el.querySelectorAll(selector);
|
||||
|
||||
return Array.from(found).map(i => i.innerHTML);
|
||||
}`
|
||||
|
||||
func GetInnerHTMLBySelectorAll(id runtime.RemoteObjectID, selector values.String) *eval.Function {
|
||||
return eval.F(getInnerHTMLBySelectorAll).WithArgRef(id).WithArgValue(selector)
|
||||
}
|
74
pkg/drivers/cdp/templates/inner_text.go
Normal file
74
pkg/drivers/cdp/templates/inner_text.go
Normal file
@ -0,0 +1,74 @@
|
||||
package templates
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"github.com/MontFerret/ferret/pkg/drivers"
|
||||
"github.com/MontFerret/ferret/pkg/drivers/cdp/eval"
|
||||
"github.com/MontFerret/ferret/pkg/runtime/values"
|
||||
"github.com/mafredri/cdp/protocol/runtime"
|
||||
)
|
||||
|
||||
const setInnerText = `(el, value) => {
|
||||
el.innerText = value;
|
||||
}`
|
||||
|
||||
func SetInnerText(id runtime.RemoteObjectID, value values.String) *eval.Function {
|
||||
return eval.F(setInnerText).WithArgRef(id).WithArgValue(value)
|
||||
}
|
||||
|
||||
const getInnerText = `(el) => {
|
||||
if (el.nodeType !== 9) {
|
||||
return el.innerText;
|
||||
}
|
||||
|
||||
return document.documentElement.innerText;
|
||||
}`
|
||||
|
||||
func GetInnerText(id runtime.RemoteObjectID) *eval.Function {
|
||||
return eval.F(getInnerText).WithArgRef(id)
|
||||
}
|
||||
|
||||
var setInnerTextBySelector = fmt.Sprintf(`
|
||||
(el, selector, value) => {
|
||||
const found = el.querySelector(selector);
|
||||
|
||||
if (found == null) {
|
||||
throw new Error(%s);
|
||||
}
|
||||
|
||||
found.innerText = value;
|
||||
}`, ParamErr(drivers.ErrNotFound))
|
||||
|
||||
func SetInnerTextBySelector(id runtime.RemoteObjectID, selector, value values.String) *eval.Function {
|
||||
return eval.F(setInnerTextBySelector).WithArgRef(id).WithArgValue(selector).WithArgValue(value)
|
||||
}
|
||||
|
||||
var getInnerTextBySelector = fmt.Sprintf(`
|
||||
(el, selector) => {
|
||||
const found = el.querySelector(selector);
|
||||
|
||||
if (found == null) {
|
||||
throw new Error(%s);
|
||||
}
|
||||
|
||||
return found.innerText;
|
||||
}`, ParamErr(drivers.ErrNotFound))
|
||||
|
||||
func GetInnerTextBySelector(id runtime.RemoteObjectID, selector values.String) *eval.Function {
|
||||
return eval.F(getInnerTextBySelector).WithArgRef(id).WithArgValue(selector)
|
||||
}
|
||||
|
||||
var getInnerTextBySelectorAll = fmt.Sprintf(`
|
||||
(el, selector) => {
|
||||
const found = el.querySelectorAll(selector);
|
||||
|
||||
if (found == null) {
|
||||
throw new Error(%s);
|
||||
}
|
||||
|
||||
return Array.from(found).map(i => i.innerText);
|
||||
}`, ParamErr(drivers.ErrNotFound))
|
||||
|
||||
func GetInnerTextBySelectorAll(id runtime.RemoteObjectID, selector values.String) *eval.Function {
|
||||
return eval.F(getInnerTextBySelectorAll).WithArgRef(id).WithArgValue(selector)
|
||||
}
|
@ -1,7 +1,12 @@
|
||||
package templates
|
||||
|
||||
import (
|
||||
"github.com/MontFerret/ferret/pkg/drivers/cdp/eval"
|
||||
"github.com/mafredri/cdp/protocol/runtime"
|
||||
)
|
||||
|
||||
const getParent = "(el) => el.parentElement"
|
||||
|
||||
func GetParent() string {
|
||||
return getParent
|
||||
func GetParent(id runtime.RemoteObjectID) *eval.Function {
|
||||
return eval.F(getParent).WithArgRef(id)
|
||||
}
|
||||
|
@ -4,12 +4,13 @@ import (
|
||||
"fmt"
|
||||
"github.com/MontFerret/ferret/pkg/drivers"
|
||||
"github.com/MontFerret/ferret/pkg/drivers/cdp/eval"
|
||||
"github.com/MontFerret/ferret/pkg/runtime/values"
|
||||
"github.com/mafredri/cdp/protocol/runtime"
|
||||
)
|
||||
|
||||
func QuerySelector(selector string) string {
|
||||
return fmt.Sprintf(`
|
||||
(el) => {
|
||||
const found = el.querySelector(%s);
|
||||
var querySelector = fmt.Sprintf(`
|
||||
(el, selector) => {
|
||||
const found = el.querySelector(selector);
|
||||
|
||||
if (found == null) {
|
||||
throw new Error(%s);
|
||||
@ -18,24 +19,41 @@ func QuerySelector(selector string) string {
|
||||
return found;
|
||||
}
|
||||
`,
|
||||
eval.ParamString(selector),
|
||||
eval.ParamString(drivers.ErrNotFound.Error()),
|
||||
)
|
||||
ParamErr(drivers.ErrNotFound),
|
||||
)
|
||||
|
||||
func QuerySelector(id runtime.RemoteObjectID, selector values.String) *eval.Function {
|
||||
return eval.F(querySelector).WithArgRef(id).WithArgValue(selector)
|
||||
}
|
||||
|
||||
func QuerySelectorAll(selector string) string {
|
||||
return fmt.Sprintf(`
|
||||
(el) => {
|
||||
const found = el.querySelectorAll(%s);
|
||||
|
||||
if (found == null) {
|
||||
throw new Error(%s);
|
||||
}
|
||||
|
||||
return found;
|
||||
}
|
||||
`,
|
||||
eval.ParamString(selector),
|
||||
eval.ParamString(drivers.ErrNotFound.Error()),
|
||||
)
|
||||
const querySelectorAll = `(el, selector) => {
|
||||
return el.querySelectorAll(selector);
|
||||
}`
|
||||
|
||||
func QuerySelectorAll(id runtime.RemoteObjectID, selector values.String) *eval.Function {
|
||||
return eval.F(querySelectorAll).WithArgRef(id).WithArgValue(selector)
|
||||
}
|
||||
|
||||
const existsBySelector = `
|
||||
(el, selector) => {
|
||||
const found = el.querySelector(selector);
|
||||
|
||||
return found != null;
|
||||
}
|
||||
`
|
||||
|
||||
func ExistsBySelector(id runtime.RemoteObjectID, selector values.String) *eval.Function {
|
||||
return eval.F(existsBySelector).WithArgRef(id).WithArgValue(selector)
|
||||
}
|
||||
|
||||
const countBySelector = `
|
||||
(el, selector) => {
|
||||
const found = el.querySelectorAll(selector);
|
||||
|
||||
return found.length;
|
||||
}
|
||||
`
|
||||
|
||||
func CountBySelector(id runtime.RemoteObjectID, selector values.String) *eval.Function {
|
||||
return eval.F(countBySelector).WithArgRef(id).WithArgValue(selector)
|
||||
}
|
||||
|
@ -2,136 +2,108 @@ package templates
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
"github.com/MontFerret/ferret/pkg/drivers"
|
||||
"github.com/MontFerret/ferret/pkg/drivers/cdp/eval"
|
||||
"github.com/MontFerret/ferret/pkg/runtime/core"
|
||||
"github.com/MontFerret/ferret/pkg/runtime/values"
|
||||
"github.com/mafredri/cdp/protocol/runtime"
|
||||
)
|
||||
|
||||
const (
|
||||
isElementInViewportTemplate = `
|
||||
function isInViewport(elem) {
|
||||
var bounding = elem.getBoundingClientRect();
|
||||
|
||||
return (
|
||||
bounding.top >= 0 &&
|
||||
bounding.left >= 0 &&
|
||||
bounding.bottom <= (window.innerHeight || document.documentElement.clientHeight) &&
|
||||
bounding.right <= (window.innerWidth || document.documentElement.clientWidth)
|
||||
);
|
||||
};
|
||||
`
|
||||
|
||||
scrollTemplate = `
|
||||
window.scrollTo({
|
||||
left: %s,
|
||||
top: %s,
|
||||
behavior: '%s',
|
||||
block: '%s',
|
||||
inline: '%s'
|
||||
});
|
||||
`
|
||||
|
||||
scrollTopTemplate = `
|
||||
window.scrollTo({
|
||||
left: 0,
|
||||
top: 0,
|
||||
behavior: '%s',
|
||||
block: '%s',
|
||||
inline: '%s'
|
||||
});
|
||||
`
|
||||
|
||||
scrollBottomTemplate = `
|
||||
window.scrollTo({
|
||||
left: 0,
|
||||
top: window.document.body.scrollHeight,
|
||||
behavior: '%s',
|
||||
block: '%s',
|
||||
inline: '%s'
|
||||
});
|
||||
`
|
||||
|
||||
scrollIntoViewTemplate = `
|
||||
(el) => {
|
||||
` + isElementInViewportTemplate + `
|
||||
|
||||
if (!isInViewport(el)) {
|
||||
el.scrollIntoView({
|
||||
behavior: '%s',
|
||||
block: '%s',
|
||||
inline: '%s'
|
||||
});
|
||||
}
|
||||
isElementInViewportFragment = `function isInViewport(i) {
|
||||
var bounding = i.getBoundingClientRect();
|
||||
|
||||
return true;
|
||||
}
|
||||
`
|
||||
return (
|
||||
bounding.top >= 0 &&
|
||||
bounding.left >= 0 &&
|
||||
bounding.bottom <= (window.innerHeight || document.documentElement.clientHeight) &&
|
||||
bounding.right <= (window.innerWidth || document.documentElement.clientWidth)
|
||||
);
|
||||
}`
|
||||
|
||||
scrollIntoViewBySelectorTemplate = `
|
||||
const el = document.querySelector('%s');
|
||||
scroll = `(opts) =>
|
||||
window.scrollTo({
|
||||
left: opts.left,
|
||||
top: opts.top,
|
||||
behavior: opts.behavior,
|
||||
block: opts.block,
|
||||
inline: opts.inline
|
||||
});
|
||||
}`
|
||||
|
||||
scrollTop = `(opts) => {
|
||||
window.scrollTo({
|
||||
left: 0,
|
||||
top: 0,
|
||||
behavior: opts.behavior,
|
||||
block: opts.block,
|
||||
inline: opts.inline
|
||||
});
|
||||
}`
|
||||
|
||||
scrollBottom = `(opts) => {
|
||||
window.scrollTo({
|
||||
left: 0,
|
||||
top: window.document.body.scrollHeight,
|
||||
behavior: opts.behavior,
|
||||
block: opts.block,
|
||||
inline: opts.inline
|
||||
});
|
||||
}`
|
||||
)
|
||||
|
||||
var (
|
||||
scrollIntoView = fmt.Sprintf(`(el, opts) => {
|
||||
%s
|
||||
|
||||
if (!isInViewport(el)) {
|
||||
el.scrollIntoView({
|
||||
behavior: opts.behavior,
|
||||
block: opts.block,
|
||||
inline: opts.inline
|
||||
});
|
||||
}
|
||||
|
||||
return true;
|
||||
}`, isElementInViewportFragment)
|
||||
|
||||
scrollIntoViewBySelector = fmt.Sprintf(`(parent, selector, opts) => {
|
||||
const el = parent.querySelector(selector);
|
||||
|
||||
if (el == null) {
|
||||
throw new Error('%s');
|
||||
throw new Error(%s);
|
||||
}
|
||||
|
||||
` + isElementInViewportTemplate + `
|
||||
%s
|
||||
|
||||
if (!isInViewport(el)) {
|
||||
el.scrollIntoView({
|
||||
behavior: '%s',
|
||||
block: '%s',
|
||||
inline: '%s'
|
||||
behavior: opts.behavior,
|
||||
block: opts.block,
|
||||
inline: opts.inline
|
||||
});
|
||||
}
|
||||
|
||||
return true;
|
||||
`
|
||||
}`, ParamErr(core.ErrNotFound), isElementInViewportFragment)
|
||||
)
|
||||
|
||||
func Scroll(x, y string, options drivers.ScrollOptions) string {
|
||||
return fmt.Sprintf(
|
||||
scrollTemplate,
|
||||
x,
|
||||
y,
|
||||
options.Behavior,
|
||||
options.Block,
|
||||
options.Inline,
|
||||
)
|
||||
func Scroll(options drivers.ScrollOptions) *eval.Function {
|
||||
return eval.F(scroll).WithArg(options)
|
||||
}
|
||||
|
||||
func ScrollTop(options drivers.ScrollOptions) string {
|
||||
return fmt.Sprintf(
|
||||
scrollTopTemplate,
|
||||
options.Behavior,
|
||||
options.Block,
|
||||
options.Inline,
|
||||
)
|
||||
func ScrollTop(options drivers.ScrollOptions) *eval.Function {
|
||||
return eval.F(scrollTop).WithArg(options)
|
||||
}
|
||||
|
||||
func ScrollBottom(options drivers.ScrollOptions) string {
|
||||
return fmt.Sprintf(
|
||||
scrollBottomTemplate,
|
||||
options.Behavior,
|
||||
options.Block,
|
||||
options.Inline,
|
||||
)
|
||||
func ScrollBottom(options drivers.ScrollOptions) *eval.Function {
|
||||
return eval.F(scrollBottom).WithArg(options)
|
||||
}
|
||||
|
||||
func ScrollIntoView(options drivers.ScrollOptions) string {
|
||||
return fmt.Sprintf(
|
||||
scrollIntoViewTemplate,
|
||||
options.Behavior,
|
||||
options.Block,
|
||||
options.Inline,
|
||||
)
|
||||
func ScrollIntoView(id runtime.RemoteObjectID, options drivers.ScrollOptions) *eval.Function {
|
||||
return eval.F(scrollIntoView).WithArgRef(id).WithArg(options)
|
||||
}
|
||||
|
||||
func ScrollIntoViewBySelector(selector string, options drivers.ScrollOptions) string {
|
||||
return fmt.Sprintf(
|
||||
scrollIntoViewBySelectorTemplate,
|
||||
selector,
|
||||
drivers.ErrNotFound,
|
||||
options.Behavior,
|
||||
options.Block,
|
||||
options.Inline,
|
||||
)
|
||||
func ScrollIntoViewBySelector(id runtime.RemoteObjectID, selector values.String, options drivers.ScrollOptions) *eval.Function {
|
||||
return eval.F(scrollIntoViewBySelector).WithArgRef(id).WithArgValue(selector).WithArg(options)
|
||||
}
|
||||
|
@ -2,55 +2,51 @@ package templates
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"github.com/MontFerret/ferret/pkg/drivers"
|
||||
"github.com/MontFerret/ferret/pkg/drivers/cdp/eval"
|
||||
"github.com/MontFerret/ferret/pkg/runtime/core"
|
||||
"github.com/MontFerret/ferret/pkg/runtime/values"
|
||||
"github.com/mafredri/cdp/protocol/runtime"
|
||||
)
|
||||
|
||||
func selectBase(values string) string {
|
||||
return fmt.Sprintf(`
|
||||
const values = %s;
|
||||
const selectFragment = `
|
||||
if (el.nodeName.toLowerCase() !== 'select') {
|
||||
throw new Error('element is not a <select> element.');
|
||||
}
|
||||
|
||||
if (el.nodeName.toLowerCase() !== 'select') {
|
||||
throw new Error('element is not a <select> element.');
|
||||
const options = Array.from(el.options);
|
||||
|
||||
el.value = undefined;
|
||||
|
||||
for (var option of options) {
|
||||
option.selected = values.includes(option.value);
|
||||
|
||||
if (option.selected && !el.multiple) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
const options = Array.from(el.options);
|
||||
el.dispatchEvent(new Event('input', { 'bubbles': true }));
|
||||
el.dispatchEvent(new Event('change', { 'bubbles': true }));
|
||||
|
||||
return options.filter(option => option.selected).map(option => option.value);
|
||||
`
|
||||
|
||||
el.value = undefined;
|
||||
const selec = `(el, values) => {` + selectFragment + `}`
|
||||
|
||||
for (var option of options) {
|
||||
option.selected = values.includes(option.value);
|
||||
|
||||
if (option.selected && !el.multiple) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
el.dispatchEvent(new Event('input', { 'bubbles': true }));
|
||||
el.dispatchEvent(new Event('change', { 'bubbles': true }));
|
||||
|
||||
return options.filter(option => option.selected).map(option => option.value);
|
||||
`, values,
|
||||
)
|
||||
func Select(id runtime.RemoteObjectID, inputs *values.Array) *eval.Function {
|
||||
return eval.F(selec).WithArgRef(id).WithArgValue(inputs)
|
||||
}
|
||||
|
||||
func Select(values string) string {
|
||||
return fmt.Sprintf(`
|
||||
(el) => {
|
||||
%s
|
||||
}
|
||||
`, selectBase(values),
|
||||
)
|
||||
}
|
||||
var selectBySelector = fmt.Sprintf(`(parent, selector, values) => {
|
||||
const el = parent.querySelector(selector);
|
||||
|
||||
if (el == null) {
|
||||
throw new Error(%s)
|
||||
}
|
||||
|
||||
func SelectBySelector(selector, values string) string {
|
||||
return fmt.Sprintf(`
|
||||
const el = document.querySelector('%s');
|
||||
|
||||
if (el == null) {
|
||||
throw new Error("%s")
|
||||
}
|
||||
%s
|
||||
}`, ParamErr(core.ErrNotFound), selectFragment)
|
||||
|
||||
%s
|
||||
`, selector, drivers.ErrNotFound, selectBase(values),
|
||||
)
|
||||
func SelectBySelector(id runtime.RemoteObjectID, selector values.String, inputs *values.Array) *eval.Function {
|
||||
return eval.F(selectBySelector).WithArgRef(id).WithArgValue(selector).WithArgValue(inputs)
|
||||
}
|
||||
|
@ -1,34 +0,0 @@
|
||||
package templates
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"github.com/MontFerret/ferret/pkg/drivers"
|
||||
)
|
||||
|
||||
const setInnerHTMLTemplate = `
|
||||
(element, value) => {
|
||||
element.innerHTML = value;
|
||||
}
|
||||
`
|
||||
|
||||
func SetInnerHTML() string {
|
||||
return setInnerHTMLTemplate
|
||||
}
|
||||
|
||||
var setInnerHTMLBySelectorTemplate = fmt.Sprintf(`
|
||||
(el, selector, value) => {
|
||||
const found = el.querySelector(selector);
|
||||
|
||||
if (found == null) {
|
||||
throw new Error('%s');
|
||||
}
|
||||
|
||||
found.innerHTML = value;
|
||||
}
|
||||
`,
|
||||
drivers.ErrNotFound,
|
||||
)
|
||||
|
||||
func SetInnerHTMLBySelector() string {
|
||||
return setInnerHTMLBySelectorTemplate
|
||||
}
|
@ -1,34 +0,0 @@
|
||||
package templates
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"github.com/MontFerret/ferret/pkg/drivers"
|
||||
)
|
||||
|
||||
const setInnerTextTemplate = `
|
||||
(element, value) => {
|
||||
element.innerText = value;
|
||||
}
|
||||
`
|
||||
|
||||
func SetInnerText() string {
|
||||
return setInnerTextTemplate
|
||||
}
|
||||
|
||||
var setInnerTextBySelectorTemplate = fmt.Sprintf(`
|
||||
(el, selector, value) => {
|
||||
const found = el.querySelector(selector);
|
||||
|
||||
if (found == null) {
|
||||
throw new Error('%s');
|
||||
}
|
||||
|
||||
found.innerText = value;
|
||||
}
|
||||
`,
|
||||
drivers.ErrNotFound,
|
||||
)
|
||||
|
||||
func SetInnerTextBySelector() string {
|
||||
return setInnerTextBySelectorTemplate
|
||||
}
|
@ -1,12 +1,17 @@
|
||||
package templates
|
||||
|
||||
import (
|
||||
"github.com/MontFerret/ferret/pkg/drivers/cdp/eval"
|
||||
"github.com/mafredri/cdp/protocol/runtime"
|
||||
)
|
||||
|
||||
const getPreviousElementSibling = "(el) => el.previousElementSibling"
|
||||
const getNextElementSibling = "(el) => el.nextElementSibling"
|
||||
|
||||
func GetPreviousElementSibling() string {
|
||||
return getPreviousElementSibling
|
||||
func GetPreviousElementSibling(id runtime.RemoteObjectID) *eval.Function {
|
||||
return eval.F(getPreviousElementSibling).WithArgRef(id)
|
||||
}
|
||||
|
||||
func GetNextElementSibling() string {
|
||||
return getNextElementSibling
|
||||
func GetNextElementSibling(id runtime.RemoteObjectID) *eval.Function {
|
||||
return eval.F(getNextElementSibling).WithArgRef(id)
|
||||
}
|
||||
|
@ -1,95 +1,71 @@
|
||||
package templates
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"github.com/MontFerret/ferret/pkg/drivers"
|
||||
"github.com/MontFerret/ferret/pkg/drivers/cdp/eval"
|
||||
"github.com/MontFerret/ferret/pkg/runtime/values"
|
||||
"github.com/mafredri/cdp/protocol/runtime"
|
||||
)
|
||||
|
||||
var getStylesTemplate = `
|
||||
(el) => {
|
||||
const out = {};
|
||||
const styles = window.getComputedStyle(el);
|
||||
|
||||
Object.keys(styles).forEach((key) => {
|
||||
if (!isNaN(parseFloat(key))) {
|
||||
const name = styles[key];
|
||||
const value = styles.getPropertyValue(name);
|
||||
out[name] = value;
|
||||
}
|
||||
});
|
||||
const getStyles = `(el) => {
|
||||
const out = {};
|
||||
const styles = window.getComputedStyle(el);
|
||||
|
||||
return out;
|
||||
}
|
||||
`
|
||||
|
||||
func GetStyles() string {
|
||||
return getStylesTemplate
|
||||
}
|
||||
|
||||
func GetStyle(name string) string {
|
||||
return fmt.Sprintf(`
|
||||
(el) => {
|
||||
const out = {};
|
||||
const styles = window.getComputedStyle(el);
|
||||
|
||||
return styles[%s];
|
||||
}
|
||||
`, eval.ParamString(name))
|
||||
}
|
||||
|
||||
func SetStyle(name, value string) string {
|
||||
return fmt.Sprintf(`
|
||||
(el) => {
|
||||
el.style[%s] = %s;
|
||||
Object.keys(styles).forEach((key) => {
|
||||
if (!isNaN(parseFloat(key))) {
|
||||
const name = styles[key];
|
||||
const value = styles.getPropertyValue(name);
|
||||
out[name] = value;
|
||||
}
|
||||
`, eval.ParamString(name), eval.ParamString(value))
|
||||
});
|
||||
|
||||
return out;
|
||||
}`
|
||||
|
||||
func GetStyles(id runtime.RemoteObjectID) *eval.Function {
|
||||
return eval.F(getStyles).WithArgRef(id)
|
||||
}
|
||||
|
||||
func SetStyles(pairs *values.Object) string {
|
||||
return fmt.Sprintf(`
|
||||
(el) => {
|
||||
const values = %s;
|
||||
Object.keys(values).forEach((key) => {
|
||||
el.style[key] = values[key]
|
||||
});
|
||||
}
|
||||
`, eval.Param(pairs))
|
||||
const getStyle = `(el, name) => {
|
||||
const styles = window.getComputedStyle(el);
|
||||
|
||||
return styles[name];
|
||||
}`
|
||||
|
||||
func GetStyle(id runtime.RemoteObjectID, name values.String) *eval.Function {
|
||||
return eval.F(getStyle).WithArgRef(id).WithArgValue(name)
|
||||
}
|
||||
|
||||
func RemoveStyles(names []values.String) string {
|
||||
return fmt.Sprintf(`
|
||||
(el) => {
|
||||
const style = el.style;
|
||||
[%s].forEach((name) => { style[name] = "" })
|
||||
}
|
||||
`,
|
||||
eval.ParamStringList(names),
|
||||
)
|
||||
const setStyle = `(el, name, value) => {
|
||||
el.style[name] = value;
|
||||
}`
|
||||
|
||||
func SetStyle(id runtime.RemoteObjectID, name, value values.String) *eval.Function {
|
||||
return eval.F(setStyle).WithArgRef(id).WithArgValue(name).WithArgValue(value)
|
||||
}
|
||||
|
||||
func WaitForStyle(name, value string, when drivers.WaitEvent) string {
|
||||
return fmt.Sprintf(`
|
||||
(el) => {
|
||||
const styles = window.getComputedStyle(el);
|
||||
const actual = styles[%s];
|
||||
const expected = %s;
|
||||
const setStyles = `(el, values) => {
|
||||
Object.keys(values).forEach((key) => {
|
||||
el.style[key] = values[key]
|
||||
});
|
||||
}`
|
||||
|
||||
// null means we need to repeat
|
||||
return actual %s expected ? true : null ;
|
||||
}
|
||||
`, eval.ParamString(name), eval.ParamString(value), WaitEventToEqOperator(when))
|
||||
func SetStyles(id runtime.RemoteObjectID, values *values.Object) *eval.Function {
|
||||
return eval.F(setStyles).WithArgRef(id).WithArgValue(values)
|
||||
}
|
||||
|
||||
func StyleRead(name values.String) string {
|
||||
n := name.String()
|
||||
return fmt.Sprintf(`
|
||||
((function() {
|
||||
const cs = window.getComputedStyle(el);
|
||||
const currentValue = cs.getPropertyValue(%s);
|
||||
const removeStyles = `(el, names) => {
|
||||
const style = el.style;
|
||||
names.forEach((name) => { style[name] = "" })
|
||||
}`
|
||||
|
||||
return currentValue || null;
|
||||
})())
|
||||
`, eval.ParamString(n))
|
||||
func RemoveStyles(id runtime.RemoteObjectID, names []values.String) *eval.Function {
|
||||
return eval.F(removeStyles).WithArgRef(id).WithArg(names)
|
||||
}
|
||||
|
||||
const removeStylesAll = `(el) => {
|
||||
el.removeAttribute("style");
|
||||
}`
|
||||
|
||||
func RemoveStylesAll(id runtime.RemoteObjectID) *eval.Function {
|
||||
return eval.F(removeStylesAll).WithArgRef(id)
|
||||
}
|
||||
|
@ -1,7 +1,9 @@
|
||||
package templates
|
||||
|
||||
const getURL = `return window.location.toString()`
|
||||
import "github.com/MontFerret/ferret/pkg/drivers/cdp/eval"
|
||||
|
||||
func GetURL() string {
|
||||
return getURL
|
||||
const getURL = `() => window.location.toString()`
|
||||
|
||||
func GetURL() *eval.Function {
|
||||
return eval.F(getURL)
|
||||
}
|
||||
|
23
pkg/drivers/cdp/templates/value.go
Normal file
23
pkg/drivers/cdp/templates/value.go
Normal file
@ -0,0 +1,23 @@
|
||||
package templates
|
||||
|
||||
import (
|
||||
"github.com/MontFerret/ferret/pkg/drivers/cdp/eval"
|
||||
"github.com/MontFerret/ferret/pkg/runtime/core"
|
||||
"github.com/mafredri/cdp/protocol/runtime"
|
||||
)
|
||||
|
||||
const getValue = `(el) => {
|
||||
return el.value
|
||||
}`
|
||||
|
||||
func GetValue(id runtime.RemoteObjectID) *eval.Function {
|
||||
return eval.F(getValue).WithArgRef(id)
|
||||
}
|
||||
|
||||
const setValue = `(el, value) => {
|
||||
el.value = value
|
||||
}`
|
||||
|
||||
func SetValue(id runtime.RemoteObjectID, value core.Value) *eval.Function {
|
||||
return eval.F(setValue).WithArgRef(id).WithArgValue(value)
|
||||
}
|
266
pkg/drivers/cdp/templates/wait.go
Normal file
266
pkg/drivers/cdp/templates/wait.go
Normal file
@ -0,0 +1,266 @@
|
||||
package templates
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"github.com/MontFerret/ferret/pkg/drivers"
|
||||
"github.com/MontFerret/ferret/pkg/drivers/cdp/eval"
|
||||
"github.com/MontFerret/ferret/pkg/runtime/core"
|
||||
"github.com/MontFerret/ferret/pkg/runtime/values"
|
||||
"github.com/mafredri/cdp/protocol/runtime"
|
||||
)
|
||||
|
||||
const (
|
||||
waitExistenceFragment = `(el, op, ...args) => {
|
||||
const actual = %s; // check
|
||||
|
||||
// presence
|
||||
if (op === 0) {
|
||||
if (actual != null) {
|
||||
return true;
|
||||
}
|
||||
} else {
|
||||
if (actual == null) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
// null means we need to repeat
|
||||
return null;
|
||||
}`
|
||||
|
||||
waitEqualityFragment = `(el, expected, op, ...args) => {
|
||||
const actual = %s; // check
|
||||
|
||||
// presence
|
||||
if (op === 0) {
|
||||
if (actual === expected) {
|
||||
return true;
|
||||
}
|
||||
} else {
|
||||
if (actual !== expected) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
// null means we need to repeat
|
||||
return null;
|
||||
}`
|
||||
|
||||
waitExistenceBySelectorFragment = `(parent, selector, op, ...args) => {
|
||||
const el = parent.querySelector(selector); // selector
|
||||
|
||||
if (el == null) {
|
||||
return false;
|
||||
}
|
||||
|
||||
const actual = %s; // check
|
||||
|
||||
// presence
|
||||
if (op === 0) {
|
||||
if (actual != null) {
|
||||
return true;
|
||||
}
|
||||
} else {
|
||||
if (actual == null) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
// null means we need to repeat
|
||||
return null;
|
||||
}`
|
||||
|
||||
waitEqualityBySelectorFragment = `(parent, selector, expected, op, ...args) => {
|
||||
const el = parent.querySelector(selector); // selector
|
||||
|
||||
if (el == null) {
|
||||
return false;
|
||||
}
|
||||
|
||||
const actual = %s; // check
|
||||
|
||||
// presence
|
||||
if (op === 0) {
|
||||
if (actual === expected) {
|
||||
return true;
|
||||
}
|
||||
} else {
|
||||
if (actual !== expected) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
// null means we need to repeat
|
||||
return null;
|
||||
}`
|
||||
|
||||
waitExistenceBySelectorAllFragment = `(parent, selector, op, ...args) => {
|
||||
const elements = parent.querySelectorAll(selector); // selector
|
||||
|
||||
if (elements == null || elements.length === 0) {
|
||||
return false;
|
||||
}
|
||||
|
||||
let resultCount = 0;
|
||||
|
||||
elements.forEach((el) => {
|
||||
let actual = %s; // check
|
||||
|
||||
// when
|
||||
// presence
|
||||
if (op === 0) {
|
||||
if (actual != null) {
|
||||
resultCount++;
|
||||
}
|
||||
} else {
|
||||
if (actual == null) {
|
||||
resultCount++;
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
if (resultCount === elements.length) {
|
||||
return true;
|
||||
}
|
||||
|
||||
// null means we need to repeat
|
||||
return null;
|
||||
}`
|
||||
|
||||
waitEqualityBySelectorAllFragment = `(parent, selector, expected, op, ...args) => {
|
||||
const elements = parent.querySelectorAll(selector); // selector
|
||||
|
||||
if (elements == null || elements.length === 0) {
|
||||
return false;
|
||||
}
|
||||
|
||||
let resultCount = 0;
|
||||
|
||||
elements.forEach((el) => {
|
||||
let actual = %s; // check
|
||||
|
||||
// when
|
||||
// presence
|
||||
if (op === 0) {
|
||||
if (actual === expected) {
|
||||
resultCount++;
|
||||
}
|
||||
} else {
|
||||
if (actual !== expected) {
|
||||
resultCount++;
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
if (resultCount === elements.length) {
|
||||
return true;
|
||||
}
|
||||
|
||||
// null means we need to repeat
|
||||
return null;
|
||||
}`
|
||||
)
|
||||
|
||||
func partialWaitExistence(id runtime.RemoteObjectID, when drivers.WaitEvent, fragment string) *eval.Function {
|
||||
return eval.F(fmt.Sprintf(waitExistenceFragment, fragment)).
|
||||
WithArgRef(id).
|
||||
WithArg(int(when))
|
||||
}
|
||||
|
||||
func partialWaitEquality(id runtime.RemoteObjectID, expected core.Value, when drivers.WaitEvent, fragment string) *eval.Function {
|
||||
return eval.F(fmt.Sprintf(waitEqualityFragment, fragment)).
|
||||
WithArgRef(id).
|
||||
WithArgValue(expected).
|
||||
WithArg(int(when))
|
||||
}
|
||||
|
||||
func partialWaitExistenceBySelector(id runtime.RemoteObjectID, selector values.String, when drivers.WaitEvent, fragment string) *eval.Function {
|
||||
return eval.F(fmt.Sprintf(waitExistenceBySelectorFragment, fragment)).
|
||||
WithArgRef(id).
|
||||
WithArgValue(selector).
|
||||
WithArg(int(when))
|
||||
}
|
||||
|
||||
func partialWaitEqualityBySelector(id runtime.RemoteObjectID, selector values.String, expected core.Value, when drivers.WaitEvent, fragment string) *eval.Function {
|
||||
return eval.F(fmt.Sprintf(waitEqualityBySelectorFragment, fragment)).
|
||||
WithArgRef(id).
|
||||
WithArgValue(selector).
|
||||
WithArgValue(expected).
|
||||
WithArg(int(when))
|
||||
}
|
||||
|
||||
func partialWaitExistenceBySelectorAll(id runtime.RemoteObjectID, selector values.String, when drivers.WaitEvent, fragment string) *eval.Function {
|
||||
return eval.F(fmt.Sprintf(waitExistenceBySelectorAllFragment, fragment)).
|
||||
WithArgRef(id).
|
||||
WithArgValue(selector).
|
||||
WithArg(int(when))
|
||||
}
|
||||
|
||||
func partialWaitEqualityBySelectorAll(id runtime.RemoteObjectID, selector values.String, expected core.Value, when drivers.WaitEvent, fragment string) *eval.Function {
|
||||
return eval.F(fmt.Sprintf(waitEqualityBySelectorAllFragment, fragment)).
|
||||
WithArgRef(id).
|
||||
WithArgValue(selector).
|
||||
WithArgValue(expected).
|
||||
WithArg(int(when))
|
||||
}
|
||||
|
||||
const waitForElementFragment = `el.querySelector(args[0])`
|
||||
|
||||
func WaitForElement(id runtime.RemoteObjectID, selector values.String, when drivers.WaitEvent) *eval.Function {
|
||||
return partialWaitExistence(id, when, waitForElementFragment).WithArgValue(selector)
|
||||
}
|
||||
|
||||
const waitForElementAllFragment = `(function() {
|
||||
const elements = el.querySelector(args[0]);
|
||||
|
||||
return elements.length;
|
||||
})()`
|
||||
|
||||
func WaitForElementAll(id runtime.RemoteObjectID, selector values.String, when drivers.WaitEvent) *eval.Function {
|
||||
return partialWaitEquality(id, values.ZeroInt, when, waitForElementAllFragment).WithArgValue(selector)
|
||||
}
|
||||
|
||||
const waitForClassFragment = `el.className.split(' ').find(i => i === args[0]);`
|
||||
|
||||
func WaitForClass(id runtime.RemoteObjectID, class values.String, when drivers.WaitEvent) *eval.Function {
|
||||
return partialWaitExistence(id, when, waitForClassFragment).WithArgValue(class)
|
||||
}
|
||||
|
||||
func WaitForClassBySelector(id runtime.RemoteObjectID, selector, class values.String, when drivers.WaitEvent) *eval.Function {
|
||||
return partialWaitExistenceBySelector(id, selector, when, waitForClassFragment).WithArgValue(class)
|
||||
}
|
||||
|
||||
func WaitForClassBySelectorAll(id runtime.RemoteObjectID, selector, class values.String, when drivers.WaitEvent) *eval.Function {
|
||||
return partialWaitExistenceBySelectorAll(id, selector, when, waitForClassFragment).WithArgValue(class)
|
||||
}
|
||||
|
||||
const waitForAttributeFragment = `el.getAttribute(args[0])`
|
||||
|
||||
func WaitForAttribute(id runtime.RemoteObjectID, name values.String, expected core.Value, when drivers.WaitEvent) *eval.Function {
|
||||
return partialWaitEquality(id, expected, when, waitForAttributeFragment).WithArgValue(name)
|
||||
}
|
||||
|
||||
func WaitForAttributeBySelector(id runtime.RemoteObjectID, selector values.String, name core.Value, expected core.Value, when drivers.WaitEvent) *eval.Function {
|
||||
return partialWaitEqualityBySelector(id, selector, expected, when, waitForAttributeFragment).WithArgValue(name)
|
||||
}
|
||||
|
||||
func WaitForAttributeBySelectorAll(id runtime.RemoteObjectID, selector, name values.String, expected core.Value, when drivers.WaitEvent) *eval.Function {
|
||||
return partialWaitEqualityBySelectorAll(id, selector, expected, when, waitForAttributeFragment).WithArgValue(name)
|
||||
}
|
||||
|
||||
const waitForStyleFragment = `(function getStyles() {
|
||||
const styles = window.getComputedStyle(el);
|
||||
return styles[args[0]];
|
||||
})()`
|
||||
|
||||
func WaitForStyle(id runtime.RemoteObjectID, name values.String, expected core.Value, when drivers.WaitEvent) *eval.Function {
|
||||
return partialWaitEquality(id, expected, when, waitForStyleFragment).WithArgValue(name)
|
||||
}
|
||||
|
||||
func WaitForStyleBySelector(id runtime.RemoteObjectID, selector, name values.String, expected core.Value, when drivers.WaitEvent) *eval.Function {
|
||||
return partialWaitEqualityBySelector(id, selector, expected, when, waitForStyleFragment).WithArgValue(name)
|
||||
}
|
||||
|
||||
func WaitForStyleBySelectorAll(id runtime.RemoteObjectID, selector, name values.String, expected core.Value, when drivers.WaitEvent) *eval.Function {
|
||||
return partialWaitEqualityBySelectorAll(id, selector, expected, when, waitForStyleFragment).WithArgValue(name)
|
||||
}
|
@ -1,36 +0,0 @@
|
||||
package templates
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
"github.com/MontFerret/ferret/pkg/drivers"
|
||||
"github.com/MontFerret/ferret/pkg/drivers/cdp/eval"
|
||||
"github.com/MontFerret/ferret/pkg/runtime/core"
|
||||
"github.com/MontFerret/ferret/pkg/runtime/values"
|
||||
)
|
||||
|
||||
func WaitBySelector(selector values.String, when drivers.WaitEvent, value core.Value, check string) string {
|
||||
return fmt.Sprintf(
|
||||
`
|
||||
const el = document.querySelector(%s); // selector
|
||||
|
||||
if (el == null) {
|
||||
return false;
|
||||
}
|
||||
|
||||
const result = %s; // check
|
||||
|
||||
// when value
|
||||
if (result %s %s) {
|
||||
return true;
|
||||
}
|
||||
|
||||
// null means we need to repeat
|
||||
return null;
|
||||
`,
|
||||
eval.ParamString(selector.String()),
|
||||
check,
|
||||
WaitEventToEqOperator(when),
|
||||
eval.Param(value),
|
||||
)
|
||||
}
|
@ -1,43 +0,0 @@
|
||||
package templates
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
"github.com/MontFerret/ferret/pkg/drivers"
|
||||
"github.com/MontFerret/ferret/pkg/drivers/cdp/eval"
|
||||
"github.com/MontFerret/ferret/pkg/runtime/core"
|
||||
"github.com/MontFerret/ferret/pkg/runtime/values"
|
||||
)
|
||||
|
||||
func WaitBySelectorAll(selector values.String, when drivers.WaitEvent, value core.Value, check string) string {
|
||||
return fmt.Sprintf(`
|
||||
var elements = document.querySelectorAll(%s); // selector
|
||||
|
||||
if (elements == null || elements.length === 0) {
|
||||
return false;
|
||||
}
|
||||
|
||||
var resultCount = 0;
|
||||
|
||||
elements.forEach((el) => {
|
||||
var result = %s; // check
|
||||
|
||||
// when
|
||||
if (result %s %s) {
|
||||
resultCount++;
|
||||
}
|
||||
});
|
||||
|
||||
if (resultCount === elements.length) {
|
||||
return true;
|
||||
}
|
||||
|
||||
// null means we need to repeat
|
||||
return null;
|
||||
`,
|
||||
eval.ParamString(selector.String()),
|
||||
check,
|
||||
WaitEventToEqOperator(when),
|
||||
eval.Param(value),
|
||||
)
|
||||
}
|
@ -1,69 +1,74 @@
|
||||
package templates
|
||||
|
||||
const xPathTemplate = `
|
||||
(element, expression) => {
|
||||
const unwrap = (item) => {
|
||||
return item.nodeType != 2 ? item : item.nodeValue;
|
||||
};
|
||||
const out = document.evaluate(
|
||||
expression,
|
||||
element,
|
||||
null,
|
||||
XPathResult.ANY_TYPE
|
||||
);
|
||||
let result;
|
||||
|
||||
switch (out.resultType) {
|
||||
case XPathResult.UNORDERED_NODE_ITERATOR_TYPE:
|
||||
case XPathResult.ORDERED_NODE_ITERATOR_TYPE: {
|
||||
result = [];
|
||||
let item;
|
||||
|
||||
while ((item = out.iterateNext())) {
|
||||
result.push(unwrap(item));
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
case XPathResult.UNORDERED_NODE_SNAPSHOT_TYPE:
|
||||
case XPathResult.ORDERED_NODE_SNAPSHOT_TYPE: {
|
||||
result = [];
|
||||
|
||||
for (let i = 0; i < out.snapshotLength; i++) {
|
||||
const item = out.snapshotItem(i);
|
||||
|
||||
if (item != null) {
|
||||
result.push(unwrap(item));
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
case XPathResult.NUMBER_TYPE: {
|
||||
result = out.numberValue;
|
||||
break;
|
||||
}
|
||||
case XPathResult.STRING_TYPE: {
|
||||
result = out.stringValue;
|
||||
break;
|
||||
}
|
||||
case XPathResult.BOOLEAN_TYPE: {
|
||||
result = out.booleanValue;
|
||||
break;
|
||||
}
|
||||
case XPathResult.ANY_UNORDERED_NODE_TYPE:
|
||||
case XPathResult.FIRST_ORDERED_NODE_TYPE: {
|
||||
result = unwrap(out.singleNodeValue);
|
||||
break;
|
||||
}
|
||||
default: {
|
||||
break;
|
||||
import (
|
||||
"github.com/MontFerret/ferret/pkg/drivers/cdp/eval"
|
||||
"github.com/MontFerret/ferret/pkg/runtime/values"
|
||||
"github.com/mafredri/cdp/protocol/runtime"
|
||||
)
|
||||
|
||||
const xpath = `(el, expression) => {
|
||||
const unwrap = (item) => {
|
||||
return item.nodeType != 2 ? item : item.nodeValue;
|
||||
};
|
||||
const out = document.evaluate(
|
||||
expression,
|
||||
el,
|
||||
null,
|
||||
XPathResult.ANY_TYPE
|
||||
);
|
||||
let result;
|
||||
|
||||
switch (out.resultType) {
|
||||
case XPathResult.UNORDERED_NODE_ITERATOR_TYPE:
|
||||
case XPathResult.ORDERED_NODE_ITERATOR_TYPE: {
|
||||
result = [];
|
||||
let item;
|
||||
|
||||
while ((item = out.iterateNext())) {
|
||||
result.push(unwrap(item));
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
case XPathResult.UNORDERED_NODE_SNAPSHOT_TYPE:
|
||||
case XPathResult.ORDERED_NODE_SNAPSHOT_TYPE: {
|
||||
result = [];
|
||||
|
||||
for (let i = 0; i < out.snapshotLength; i++) {
|
||||
const item = out.snapshotItem(i);
|
||||
|
||||
if (item != null) {
|
||||
result.push(unwrap(item));
|
||||
}
|
||||
}
|
||||
|
||||
return result;
|
||||
break;
|
||||
}
|
||||
`
|
||||
case XPathResult.NUMBER_TYPE: {
|
||||
result = out.numberValue;
|
||||
break;
|
||||
}
|
||||
case XPathResult.STRING_TYPE: {
|
||||
result = out.stringValue;
|
||||
break;
|
||||
}
|
||||
case XPathResult.BOOLEAN_TYPE: {
|
||||
result = out.booleanValue;
|
||||
break;
|
||||
}
|
||||
case XPathResult.ANY_UNORDERED_NODE_TYPE:
|
||||
case XPathResult.FIRST_ORDERED_NODE_TYPE: {
|
||||
result = unwrap(out.singleNodeValue);
|
||||
break;
|
||||
}
|
||||
default: {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
func XPath() string {
|
||||
return xPathTemplate
|
||||
return result;
|
||||
}
|
||||
`
|
||||
|
||||
func XPath(id runtime.RemoteObjectID, expression values.String) *eval.Function {
|
||||
return eval.F(xpath).WithArgRef(id).WithArgValue(expression)
|
||||
}
|
||||
|
@ -231,9 +231,9 @@ func GetInNode(ctx context.Context, node drivers.HTMLNode, path []core.Value) (c
|
||||
|
||||
switch segment {
|
||||
case "nodeType":
|
||||
return node.GetNodeType(), nil
|
||||
return node.GetNodeType(ctx)
|
||||
case "nodeName":
|
||||
return node.GetNodeName(), nil
|
||||
return node.GetNodeName(ctx)
|
||||
case "children":
|
||||
children, err := node.GetChildNodes(ctx)
|
||||
|
||||
|
@ -142,12 +142,12 @@ func (doc *HTMLDocument) SetIn(ctx context.Context, path []core.Value, value cor
|
||||
return common.SetInDocument(ctx, doc, path, value)
|
||||
}
|
||||
|
||||
func (doc *HTMLDocument) GetNodeType() values.Int {
|
||||
return 9
|
||||
func (doc *HTMLDocument) GetNodeType(_ context.Context) (values.Int, error) {
|
||||
return 9, nil
|
||||
}
|
||||
|
||||
func (doc *HTMLDocument) GetNodeName() values.String {
|
||||
return "#document"
|
||||
func (doc *HTMLDocument) GetNodeName(_ context.Context) (values.String, error) {
|
||||
return "#document", nil
|
||||
}
|
||||
|
||||
func (doc *HTMLDocument) GetChildNodes(ctx context.Context) (*values.Array, error) {
|
||||
@ -216,7 +216,7 @@ func (doc *HTMLDocument) ScrollBySelector(_ context.Context, _ values.String, _
|
||||
return core.ErrNotSupported
|
||||
}
|
||||
|
||||
func (doc *HTMLDocument) ScrollByXY(_ context.Context, _, _ values.Float, _ drivers.ScrollOptions) error {
|
||||
func (doc *HTMLDocument) Scroll(_ context.Context, _ drivers.ScrollOptions) error {
|
||||
return core.ErrNotSupported
|
||||
}
|
||||
|
||||
@ -224,34 +224,6 @@ func (doc *HTMLDocument) MoveMouseByXY(_ context.Context, _, _ values.Float) err
|
||||
return core.ErrNotSupported
|
||||
}
|
||||
|
||||
func (doc *HTMLDocument) WaitForElement(_ context.Context, _ values.String, _ drivers.WaitEvent) error {
|
||||
return core.ErrNotSupported
|
||||
}
|
||||
|
||||
func (doc *HTMLDocument) WaitForClassBySelector(_ context.Context, _, _ values.String, _ drivers.WaitEvent) error {
|
||||
return core.ErrNotSupported
|
||||
}
|
||||
|
||||
func (doc *HTMLDocument) WaitForClassBySelectorAll(_ context.Context, _, _ values.String, _ drivers.WaitEvent) error {
|
||||
return core.ErrNotSupported
|
||||
}
|
||||
|
||||
func (doc *HTMLDocument) WaitForAttributeBySelector(_ context.Context, _, _ values.String, _ core.Value, _ drivers.WaitEvent) error {
|
||||
return core.ErrNotSupported
|
||||
}
|
||||
|
||||
func (doc *HTMLDocument) WaitForAttributeBySelectorAll(_ context.Context, _, _ values.String, _ core.Value, _ drivers.WaitEvent) error {
|
||||
return core.ErrNotSupported
|
||||
}
|
||||
|
||||
func (doc *HTMLDocument) WaitForStyleBySelector(_ context.Context, _, _ values.String, _ core.Value, _ drivers.WaitEvent) error {
|
||||
return core.ErrNotSupported
|
||||
}
|
||||
|
||||
func (doc *HTMLDocument) WaitForStyleBySelectorAll(_ context.Context, _, _ values.String, _ core.Value, _ drivers.WaitEvent) error {
|
||||
return core.ErrNotSupported
|
||||
}
|
||||
|
||||
func (doc *HTMLDocument) Close() error {
|
||||
return nil
|
||||
}
|
||||
|
@ -2,6 +2,7 @@ package http_test
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"context"
|
||||
"testing"
|
||||
|
||||
"github.com/MontFerret/ferret/pkg/drivers/http"
|
||||
@ -234,7 +235,10 @@ func TestDocument(t *testing.T) {
|
||||
|
||||
So(err, ShouldBeNil)
|
||||
|
||||
So(el.GetNodeType(), ShouldEqual, 9)
|
||||
nt, err := el.GetNodeType(context.Background())
|
||||
|
||||
So(err, ShouldBeNil)
|
||||
So(nt, ShouldEqual, 9)
|
||||
})
|
||||
})
|
||||
}
|
||||
|
@ -86,22 +86,22 @@ func (el *HTMLElement) Copy() core.Value {
|
||||
return c
|
||||
}
|
||||
|
||||
func (el *HTMLElement) GetNodeType() values.Int {
|
||||
func (el *HTMLElement) GetNodeType(_ context.Context) (values.Int, error) {
|
||||
nodes := el.selection.Nodes
|
||||
|
||||
if len(nodes) == 0 {
|
||||
return 0
|
||||
return 0, nil
|
||||
}
|
||||
|
||||
return values.NewInt(common.FromHTMLType(nodes[0].Type))
|
||||
return values.NewInt(common.FromHTMLType(nodes[0].Type)), nil
|
||||
}
|
||||
|
||||
func (el *HTMLElement) Close() error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (el *HTMLElement) GetNodeName() values.String {
|
||||
return values.NewString(goquery.NodeName(el.selection))
|
||||
func (el *HTMLElement) GetNodeName(_ context.Context) (values.String, error) {
|
||||
return values.NewString(goquery.NodeName(el.selection)), nil
|
||||
}
|
||||
|
||||
func (el *HTMLElement) Length() values.Int {
|
||||
@ -599,14 +599,46 @@ func (el *HTMLElement) WaitForClass(_ context.Context, _ values.String, _ driver
|
||||
return core.ErrNotSupported
|
||||
}
|
||||
|
||||
func (el *HTMLElement) WaitForElement(_ context.Context, _ values.String, _ drivers.WaitEvent) error {
|
||||
return core.ErrNotSupported
|
||||
}
|
||||
|
||||
func (el *HTMLElement) WaitForElementAll(_ context.Context, _ values.String, _ drivers.WaitEvent) error {
|
||||
return core.ErrNotSupported
|
||||
}
|
||||
|
||||
func (el *HTMLElement) WaitForAttribute(_ context.Context, _ values.String, _ core.Value, _ drivers.WaitEvent) error {
|
||||
return core.ErrNotSupported
|
||||
}
|
||||
|
||||
func (el *HTMLElement) WaitForAttributeBySelector(_ context.Context, _, _ values.String, _ core.Value, _ drivers.WaitEvent) error {
|
||||
return core.ErrNotSupported
|
||||
}
|
||||
|
||||
func (el *HTMLElement) WaitForAttributeBySelectorAll(_ context.Context, _, _ values.String, _ core.Value, _ drivers.WaitEvent) error {
|
||||
return core.ErrNotSupported
|
||||
}
|
||||
|
||||
func (el *HTMLElement) WaitForStyle(_ context.Context, _ values.String, _ core.Value, _ drivers.WaitEvent) error {
|
||||
return core.ErrNotSupported
|
||||
}
|
||||
|
||||
func (el *HTMLElement) WaitForStyleBySelector(_ context.Context, _, _ values.String, _ core.Value, _ drivers.WaitEvent) error {
|
||||
return core.ErrNotSupported
|
||||
}
|
||||
|
||||
func (el *HTMLElement) WaitForStyleBySelectorAll(_ context.Context, _, _ values.String, _ core.Value, _ drivers.WaitEvent) error {
|
||||
return core.ErrNotSupported
|
||||
}
|
||||
|
||||
func (el *HTMLElement) WaitForClassBySelector(_ context.Context, _, _ values.String, _ drivers.WaitEvent) error {
|
||||
return core.ErrNotSupported
|
||||
}
|
||||
|
||||
func (el *HTMLElement) WaitForClassBySelectorAll(_ context.Context, _, _ values.String, _ drivers.WaitEvent) error {
|
||||
return core.ErrNotSupported
|
||||
}
|
||||
|
||||
func (el *HTMLElement) ensureStyles(ctx context.Context) error {
|
||||
if el.styles == nil {
|
||||
styles, err := el.parseStyles(ctx)
|
||||
|
@ -257,7 +257,10 @@ func TestElement(t *testing.T) {
|
||||
|
||||
So(err, ShouldBeNil)
|
||||
|
||||
So(el.GetNodeType(), ShouldEqual, 1)
|
||||
nt, err := el.GetNodeType(context.Background())
|
||||
|
||||
So(err, ShouldBeNil)
|
||||
So(nt, ShouldEqual, 1)
|
||||
})
|
||||
|
||||
Convey(".GetNodeName", t, func() {
|
||||
@ -273,7 +276,10 @@ func TestElement(t *testing.T) {
|
||||
|
||||
So(err, ShouldBeNil)
|
||||
|
||||
So(el.GetNodeName(), ShouldEqual, "body")
|
||||
nn, err := el.GetNodeName(context.Background())
|
||||
|
||||
So(err, ShouldBeNil)
|
||||
So(nn, ShouldEqual, "body")
|
||||
})
|
||||
|
||||
Convey(".Length", t, func() {
|
||||
@ -399,10 +405,9 @@ func TestElement(t *testing.T) {
|
||||
So(err, ShouldBeNil)
|
||||
So(found, ShouldNotEqual, values.None)
|
||||
|
||||
v := found.(drivers.HTMLNode).GetNodeName()
|
||||
v, err := found.(drivers.HTMLNode).GetNodeName(context.Background())
|
||||
|
||||
So(err, ShouldBeNil)
|
||||
|
||||
So(v, ShouldEqual, "img")
|
||||
})
|
||||
|
||||
|
@ -1,6 +1,10 @@
|
||||
package drivers
|
||||
|
||||
import "strings"
|
||||
import (
|
||||
"github.com/MontFerret/ferret/pkg/runtime/values"
|
||||
"github.com/wI2L/jettison"
|
||||
"strings"
|
||||
)
|
||||
|
||||
// ScrollBehavior defines the transition animation.
|
||||
// In HTML specification, default value is auto, but in Ferret it's instant.
|
||||
@ -24,6 +28,10 @@ func NewScrollBehavior(value string) ScrollBehavior {
|
||||
}
|
||||
}
|
||||
|
||||
func (b ScrollBehavior) MarshalJSON() ([]byte, error) {
|
||||
return jettison.MarshalOpts(b.String(), jettison.NoHTMLEscaping())
|
||||
}
|
||||
|
||||
func (b ScrollBehavior) String() string {
|
||||
switch b {
|
||||
case ScrollBehaviorInstant:
|
||||
@ -62,6 +70,10 @@ func NewScrollVerticalAlignment(value string) ScrollVerticalAlignment {
|
||||
}
|
||||
}
|
||||
|
||||
func (a ScrollVerticalAlignment) MarshalJSON() ([]byte, error) {
|
||||
return jettison.MarshalOpts(a.String(), jettison.NoHTMLEscaping())
|
||||
}
|
||||
|
||||
func (a ScrollVerticalAlignment) String() string {
|
||||
switch a {
|
||||
case ScrollVerticalAlignmentCenter:
|
||||
@ -104,6 +116,10 @@ func NewScrollHorizontalAlignment(value string) ScrollHorizontalAlignment {
|
||||
}
|
||||
}
|
||||
|
||||
func (a ScrollHorizontalAlignment) MarshalJSON() ([]byte, error) {
|
||||
return jettison.MarshalOpts(a.String(), jettison.NoHTMLEscaping())
|
||||
}
|
||||
|
||||
func (a ScrollHorizontalAlignment) String() string {
|
||||
switch a {
|
||||
case ScrollHorizontalAlignmentCenter:
|
||||
@ -121,7 +137,9 @@ func (a ScrollHorizontalAlignment) String() string {
|
||||
|
||||
// ScrollOptions defines how scroll animation should be performed.
|
||||
type ScrollOptions struct {
|
||||
Behavior ScrollBehavior
|
||||
Block ScrollVerticalAlignment
|
||||
Inline ScrollHorizontalAlignment
|
||||
Top values.Float `json:"top"`
|
||||
Left values.Float `json:"left"`
|
||||
Behavior ScrollBehavior `json:"behavior"`
|
||||
Block ScrollVerticalAlignment `json:"block"`
|
||||
Inline ScrollHorizontalAlignment `json:"inline"`
|
||||
}
|
||||
|
@ -24,9 +24,9 @@ type (
|
||||
collections.Measurable
|
||||
io.Closer
|
||||
|
||||
GetNodeType() values.Int
|
||||
GetNodeType(ctx context.Context) (values.Int, error)
|
||||
|
||||
GetNodeName() values.String
|
||||
GetNodeName(ctx context.Context) (values.String, error)
|
||||
|
||||
GetChildNodes(ctx context.Context) (*values.Array, error)
|
||||
|
||||
@ -133,11 +133,27 @@ type (
|
||||
|
||||
HoverBySelector(ctx context.Context, selector values.String) error
|
||||
|
||||
WaitForElement(ctx context.Context, selector values.String, when WaitEvent) error
|
||||
|
||||
WaitForElementAll(ctx context.Context, selector values.String, when WaitEvent) error
|
||||
|
||||
WaitForAttribute(ctx context.Context, name values.String, value core.Value, when WaitEvent) error
|
||||
|
||||
WaitForAttributeBySelector(ctx context.Context, selector, name values.String, value core.Value, when WaitEvent) error
|
||||
|
||||
WaitForAttributeBySelectorAll(ctx context.Context, selector, name values.String, value core.Value, when WaitEvent) error
|
||||
|
||||
WaitForStyle(ctx context.Context, name values.String, value core.Value, when WaitEvent) error
|
||||
|
||||
WaitForStyleBySelector(ctx context.Context, selector, name values.String, value core.Value, when WaitEvent) error
|
||||
|
||||
WaitForStyleBySelectorAll(ctx context.Context, selector, name values.String, value core.Value, when WaitEvent) error
|
||||
|
||||
WaitForClass(ctx context.Context, class values.String, when WaitEvent) error
|
||||
|
||||
WaitForClassBySelector(ctx context.Context, selector, class values.String, when WaitEvent) error
|
||||
|
||||
WaitForClassBySelectorAll(ctx context.Context, selector, class values.String, when WaitEvent) error
|
||||
}
|
||||
|
||||
HTMLDocument interface {
|
||||
@ -155,29 +171,15 @@ type (
|
||||
|
||||
GetChildDocuments(ctx context.Context) (*values.Array, error)
|
||||
|
||||
Scroll(ctx context.Context, options ScrollOptions) error
|
||||
|
||||
ScrollTop(ctx context.Context, options ScrollOptions) error
|
||||
|
||||
ScrollBottom(ctx context.Context, options ScrollOptions) error
|
||||
|
||||
ScrollBySelector(ctx context.Context, selector values.String, options ScrollOptions) error
|
||||
|
||||
ScrollByXY(ctx context.Context, x, y values.Float, options ScrollOptions) error
|
||||
|
||||
MoveMouseByXY(ctx context.Context, x, y values.Float) error
|
||||
|
||||
WaitForElement(ctx context.Context, selector values.String, when WaitEvent) error
|
||||
|
||||
WaitForAttributeBySelector(ctx context.Context, selector, name values.String, value core.Value, when WaitEvent) error
|
||||
|
||||
WaitForAttributeBySelectorAll(ctx context.Context, selector, name values.String, value core.Value, when WaitEvent) error
|
||||
|
||||
WaitForStyleBySelector(ctx context.Context, selector, name values.String, value core.Value, when WaitEvent) error
|
||||
|
||||
WaitForStyleBySelectorAll(ctx context.Context, selector, name values.String, value core.Value, when WaitEvent) error
|
||||
|
||||
WaitForClassBySelector(ctx context.Context, selector, class values.String, when WaitEvent) error
|
||||
|
||||
WaitForClassBySelectorAll(ctx context.Context, selector, class values.String, when WaitEvent) error
|
||||
}
|
||||
|
||||
// HTMLPage interface represents any web page loaded in the browser
|
||||
|
@ -29,5 +29,5 @@ func InputClear(ctx context.Context, args ...core.Value) (core.Value, error) {
|
||||
return values.None, el.Clear(ctx)
|
||||
}
|
||||
|
||||
return values.None, el.ClearBySelector(ctx, values.ToString(args[1]))
|
||||
return values.True, el.ClearBySelector(ctx, values.ToString(args[1]))
|
||||
}
|
||||
|
@ -25,8 +25,8 @@ func Focus(ctx context.Context, args ...core.Value) (core.Value, error) {
|
||||
}
|
||||
|
||||
if len(args) == 1 {
|
||||
return values.None, el.Focus(ctx)
|
||||
return values.True, el.Focus(ctx)
|
||||
}
|
||||
|
||||
return values.None, el.FocusBySelector(ctx, values.ToString(args[1]))
|
||||
return values.True, el.FocusBySelector(ctx, values.ToString(args[1]))
|
||||
}
|
||||
|
@ -27,7 +27,7 @@ func Hover(ctx context.Context, args ...core.Value) (core.Value, error) {
|
||||
}
|
||||
|
||||
if len(args) == 1 {
|
||||
return values.None, el.Hover(ctx)
|
||||
return values.True, el.Hover(ctx)
|
||||
}
|
||||
|
||||
err = core.ValidateType(args[1], types.String)
|
||||
@ -36,5 +36,5 @@ func Hover(ctx context.Context, args ...core.Value) (core.Value, error) {
|
||||
return values.None, err
|
||||
}
|
||||
|
||||
return values.None, el.HoverBySelector(ctx, values.ToString(args[1]))
|
||||
return values.True, el.HoverBySelector(ctx, values.ToString(args[1]))
|
||||
}
|
||||
|
@ -49,5 +49,5 @@ func Navigate(ctx context.Context, args ...core.Value) (core.Value, error) {
|
||||
ctx, fn := waitTimeout(ctx, timeout)
|
||||
defer fn()
|
||||
|
||||
return values.None, page.Navigate(ctx, args[1].(values.String))
|
||||
return values.True, page.Navigate(ctx, args[1].(values.String))
|
||||
}
|
||||
|
@ -39,9 +39,9 @@ func Press(ctx context.Context, args ...core.Value) (core.Value, error) {
|
||||
|
||||
switch keys := keysArg.(type) {
|
||||
case values.String:
|
||||
return values.None, el.Press(ctx, []values.String{keys}, count)
|
||||
return values.True, el.Press(ctx, []values.String{keys}, count)
|
||||
case *values.Array:
|
||||
return values.None, el.Press(ctx, values.ToStrings2(keys), count)
|
||||
return values.True, el.Press(ctx, values.ToStrings2(keys), count)
|
||||
default:
|
||||
return values.None, core.TypeError(keysArg.Type(), types.String, types.Array)
|
||||
}
|
||||
|
@ -43,9 +43,9 @@ func PressSelector(ctx context.Context, args ...core.Value) (core.Value, error)
|
||||
|
||||
switch keys := keysArg.(type) {
|
||||
case values.String:
|
||||
return values.None, el.PressBySelector(ctx, selector, []values.String{keys}, count)
|
||||
return values.True, el.PressBySelector(ctx, selector, []values.String{keys}, count)
|
||||
case *values.Array:
|
||||
return values.None, el.PressBySelector(ctx, selector, values.ToStrings2(keys), count)
|
||||
return values.True, el.PressBySelector(ctx, selector, values.ToStrings2(keys), count)
|
||||
default:
|
||||
return values.None, core.TypeError(keysArg.Type(), types.String, types.Array)
|
||||
}
|
||||
|
@ -39,5 +39,5 @@ func ScrollBottom(ctx context.Context, args ...core.Value) (core.Value, error) {
|
||||
}
|
||||
}
|
||||
|
||||
return values.None, doc.ScrollBottom(ctx, opts)
|
||||
return values.True, doc.ScrollBottom(ctx, opts)
|
||||
}
|
||||
|
@ -86,14 +86,14 @@ func ScrollInto(ctx context.Context, args ...core.Value) (core.Value, error) {
|
||||
|
||||
if doc != nil {
|
||||
if selector != values.EmptyString {
|
||||
return values.None, doc.ScrollBySelector(ctx, selector, opts)
|
||||
return values.True, doc.ScrollBySelector(ctx, selector, opts)
|
||||
}
|
||||
|
||||
return values.None, doc.GetElement().ScrollIntoView(ctx, opts)
|
||||
return values.True, doc.GetElement().ScrollIntoView(ctx, opts)
|
||||
}
|
||||
|
||||
if el != nil {
|
||||
return values.None, el.ScrollIntoView(ctx, opts)
|
||||
return values.True, el.ScrollIntoView(ctx, opts)
|
||||
}
|
||||
|
||||
return values.None, core.TypeError(
|
||||
|
@ -39,5 +39,5 @@ func ScrollTop(ctx context.Context, args ...core.Value) (core.Value, error) {
|
||||
}
|
||||
}
|
||||
|
||||
return values.None, doc.ScrollTop(ctx, opts)
|
||||
return values.True, doc.ScrollTop(ctx, opts)
|
||||
}
|
||||
|
@ -46,6 +46,8 @@ func ScrollXY(ctx context.Context, args ...core.Value) (core.Value, error) {
|
||||
y := values.ToFloat(args[2])
|
||||
|
||||
var opts drivers.ScrollOptions
|
||||
opts.Top = x
|
||||
opts.Left = y
|
||||
|
||||
if len(args) > 3 {
|
||||
opts, err = toScrollOptions(args[3])
|
||||
@ -53,7 +55,10 @@ func ScrollXY(ctx context.Context, args ...core.Value) (core.Value, error) {
|
||||
if err != nil {
|
||||
return values.None, err
|
||||
}
|
||||
|
||||
opts.Top = x
|
||||
opts.Left = y
|
||||
}
|
||||
|
||||
return values.None, doc.ScrollByXY(ctx, x, y, opts)
|
||||
return values.True, doc.Scroll(ctx, opts)
|
||||
}
|
||||
|
@ -70,7 +70,7 @@ func waitAttributeWhen(ctx context.Context, args []core.Value, when drivers.Wait
|
||||
return values.None, err
|
||||
}
|
||||
|
||||
doc, err := drivers.ToDocument(arg1)
|
||||
el, err := drivers.ToElement(arg1)
|
||||
|
||||
if err != nil {
|
||||
return values.None, err
|
||||
@ -78,7 +78,7 @@ func waitAttributeWhen(ctx context.Context, args []core.Value, when drivers.Wait
|
||||
|
||||
selector := args[1].(values.String)
|
||||
name := args[2].(values.String)
|
||||
value := args[3]
|
||||
value := values.ToString(args[3])
|
||||
|
||||
if len(args) == 5 {
|
||||
err = core.ValidateType(args[4], types.Int)
|
||||
@ -93,7 +93,7 @@ func waitAttributeWhen(ctx context.Context, args []core.Value, when drivers.Wait
|
||||
ctx, fn := waitTimeout(ctx, timeout)
|
||||
defer fn()
|
||||
|
||||
return values.None, doc.WaitForAttributeBySelector(ctx, selector, name, value, when)
|
||||
return values.True, el.WaitForAttributeBySelector(ctx, selector, name, value, when)
|
||||
}
|
||||
|
||||
el := arg1.(drivers.HTMLElement)
|
||||
@ -113,5 +113,5 @@ func waitAttributeWhen(ctx context.Context, args []core.Value, when drivers.Wait
|
||||
ctx, fn := waitTimeout(ctx, timeout)
|
||||
defer fn()
|
||||
|
||||
return values.None, el.WaitForAttribute(ctx, name, value, when)
|
||||
return values.True, el.WaitForAttribute(ctx, name, value, when)
|
||||
}
|
||||
|
@ -36,7 +36,7 @@ func waitAttributeAllWhen(ctx context.Context, args []core.Value, when drivers.W
|
||||
return values.None, err
|
||||
}
|
||||
|
||||
doc, err := drivers.ToDocument(args[0])
|
||||
el, err := drivers.ToElement(args[0])
|
||||
|
||||
if err != nil {
|
||||
return values.None, err
|
||||
@ -74,5 +74,5 @@ func waitAttributeAllWhen(ctx context.Context, args []core.Value, when drivers.W
|
||||
ctx, fn := waitTimeout(ctx, timeout)
|
||||
defer fn()
|
||||
|
||||
return values.None, doc.WaitForAttributeBySelectorAll(ctx, selector, name, value, when)
|
||||
return values.True, el.WaitForAttributeBySelectorAll(ctx, selector, name, value, when)
|
||||
}
|
||||
|
@ -69,7 +69,7 @@ func waitClassWhen(ctx context.Context, args []core.Value, when drivers.WaitEven
|
||||
return values.None, err
|
||||
}
|
||||
|
||||
doc, err := drivers.ToDocument(arg1)
|
||||
el, err := drivers.ToElement(arg1)
|
||||
|
||||
if err != nil {
|
||||
return values.None, err
|
||||
@ -91,7 +91,7 @@ func waitClassWhen(ctx context.Context, args []core.Value, when drivers.WaitEven
|
||||
ctx, fn := waitTimeout(ctx, timeout)
|
||||
defer fn()
|
||||
|
||||
return values.None, doc.WaitForClassBySelector(ctx, selector, class, when)
|
||||
return values.True, el.WaitForClassBySelector(ctx, selector, class, when)
|
||||
}
|
||||
|
||||
el := arg1.(drivers.HTMLElement)
|
||||
@ -110,5 +110,5 @@ func waitClassWhen(ctx context.Context, args []core.Value, when drivers.WaitEven
|
||||
ctx, fn := waitTimeout(ctx, timeout)
|
||||
defer fn()
|
||||
|
||||
return values.None, el.WaitForClass(ctx, class, when)
|
||||
return values.True, el.WaitForClass(ctx, class, when)
|
||||
}
|
||||
|
@ -36,7 +36,7 @@ func waitClassAllWhen(ctx context.Context, args []core.Value, when drivers.WaitE
|
||||
return values.None, err
|
||||
}
|
||||
|
||||
doc, err := drivers.ToDocument(args[0])
|
||||
el, err := drivers.ToElement(args[0])
|
||||
|
||||
if err != nil {
|
||||
return values.None, err
|
||||
@ -73,5 +73,5 @@ func waitClassAllWhen(ctx context.Context, args []core.Value, when drivers.WaitE
|
||||
ctx, fn := waitTimeout(ctx, timeout)
|
||||
defer fn()
|
||||
|
||||
return values.None, doc.WaitForClassBySelectorAll(ctx, selector, class, when)
|
||||
return values.True, el.WaitForClassBySelectorAll(ctx, selector, class, when)
|
||||
}
|
||||
|
@ -34,7 +34,7 @@ func waitElementWhen(ctx context.Context, args []core.Value, when drivers.WaitEv
|
||||
return values.None, err
|
||||
}
|
||||
|
||||
doc, err := drivers.ToDocument(args[0])
|
||||
el, err := drivers.ToElement(args[0])
|
||||
|
||||
if err != nil {
|
||||
return values.None, err
|
||||
@ -56,5 +56,5 @@ func waitElementWhen(ctx context.Context, args []core.Value, when drivers.WaitEv
|
||||
ctx, fn := waitTimeout(ctx, timeout)
|
||||
defer fn()
|
||||
|
||||
return values.None, doc.WaitForElement(ctx, values.NewString(selector), when)
|
||||
return values.True, el.WaitForElement(ctx, values.NewString(selector), when)
|
||||
}
|
||||
|
@ -55,10 +55,10 @@ func WaitNavigation(ctx context.Context, args ...core.Value) (core.Value, error)
|
||||
defer fn()
|
||||
|
||||
if params.Frame == nil {
|
||||
return values.None, doc.WaitForNavigation(ctx, params.TargetURL)
|
||||
return values.True, doc.WaitForNavigation(ctx, params.TargetURL)
|
||||
}
|
||||
|
||||
return values.None, doc.WaitForFrameNavigation(ctx, params.Frame, params.TargetURL)
|
||||
return values.True, doc.WaitForFrameNavigation(ctx, params.Frame, params.TargetURL)
|
||||
}
|
||||
|
||||
func parseWaitNavigationParams(arg core.Value) (WaitNavigationParams, error) {
|
||||
|
@ -70,7 +70,7 @@ func waitStyleWhen(ctx context.Context, args []core.Value, when drivers.WaitEven
|
||||
return values.None, err
|
||||
}
|
||||
|
||||
doc, err := drivers.ToDocument(arg1)
|
||||
el, err := drivers.ToElement(arg1)
|
||||
|
||||
if err != nil {
|
||||
return values.None, err
|
||||
@ -93,7 +93,7 @@ func waitStyleWhen(ctx context.Context, args []core.Value, when drivers.WaitEven
|
||||
ctx, fn := waitTimeout(ctx, timeout)
|
||||
defer fn()
|
||||
|
||||
return values.None, doc.WaitForStyleBySelector(ctx, selector, name, value, when)
|
||||
return values.True, el.WaitForStyleBySelector(ctx, selector, name, value, when)
|
||||
}
|
||||
|
||||
el := arg1.(drivers.HTMLElement)
|
||||
@ -113,5 +113,5 @@ func waitStyleWhen(ctx context.Context, args []core.Value, when drivers.WaitEven
|
||||
ctx, fn := waitTimeout(ctx, timeout)
|
||||
defer fn()
|
||||
|
||||
return values.None, el.WaitForStyle(ctx, name, value, when)
|
||||
return values.True, el.WaitForStyle(ctx, name, value, when)
|
||||
}
|
||||
|
@ -36,7 +36,7 @@ func waitStyleAllWhen(ctx context.Context, args []core.Value, when drivers.WaitE
|
||||
return values.None, err
|
||||
}
|
||||
|
||||
doc, err := drivers.ToDocument(args[0])
|
||||
el, err := drivers.ToElement(args[0])
|
||||
|
||||
if err != nil {
|
||||
return values.None, err
|
||||
@ -74,5 +74,5 @@ func waitStyleAllWhen(ctx context.Context, args []core.Value, when drivers.WaitE
|
||||
ctx, fn := waitTimeout(ctx, timeout)
|
||||
defer fn()
|
||||
|
||||
return values.None, doc.WaitForStyleBySelectorAll(ctx, selector, name, value, when)
|
||||
return values.True, el.WaitForStyleBySelectorAll(ctx, selector, name, value, when)
|
||||
}
|
||||
|
Loading…
x
Reference in New Issue
Block a user