From 20217058f8159046ca2c6094465e1a8d39dcd890 Mon Sep 17 00:00:00 2001 From: Tim Voronov Date: Mon, 17 Dec 2018 20:16:18 -0500 Subject: [PATCH] Added ELEMENT_EXISTS function --- e2e/main.go | 26 +++++++++++++++++++++++--- e2e/runner/runner.go | 12 +++++++++++- e2e/tests/doc_element_exists.fql | 10 ++++++++++ e2e/tests/doc_element_exists_d.fql | 10 ++++++++++ e2e/tests/el_element_exists.fql | 12 ++++++++++++ e2e/tests/el_element_exists_d.fql | 12 ++++++++++++ pkg/drivers/cdp/document.go | 7 +++++++ pkg/drivers/cdp/element.go | 27 +++++++++++++++++++++++++++ pkg/drivers/http/element.go | 10 ++++++++++ pkg/runtime/values/html.go | 2 ++ pkg/stdlib/html/element_exists.go | 22 ++++++++++++++++++++++ pkg/stdlib/html/lib.go | 1 + 12 files changed, 147 insertions(+), 4 deletions(-) create mode 100644 e2e/tests/doc_element_exists.fql create mode 100644 e2e/tests/doc_element_exists_d.fql create mode 100644 e2e/tests/el_element_exists.fql create mode 100644 e2e/tests/el_element_exists_d.fql create mode 100644 pkg/stdlib/html/element_exists.go diff --git a/e2e/main.go b/e2e/main.go index 8f084523..9433b798 100644 --- a/e2e/main.go +++ b/e2e/main.go @@ -3,12 +3,12 @@ package main import ( "flag" "fmt" - "os" - "path/filepath" - "github.com/MontFerret/ferret/e2e/runner" "github.com/MontFerret/ferret/e2e/server" "github.com/rs/zerolog" + "os" + "path/filepath" + "regexp" ) var ( @@ -29,6 +29,12 @@ var ( "http://0.0.0.0:9222", "address of remote Chrome instance", ) + + filter = flag.String( + "filter", + "", + "regexp expression to filter out tests", + ) ) func main() { @@ -48,6 +54,19 @@ func main() { Dir: filepath.Join(*pagesDir, "dynamic"), }) + var filterR *regexp.Regexp + + if *filter != "" { + r, err := regexp.Compile(*filter) + + if err != nil { + fmt.Println(err.Error()) + os.Exit(1) + } + + filterR = r + } + go func() { if err := static.Start(); err != nil { logger.Info().Timestamp().Msg("shutting down the static pages server") @@ -79,6 +98,7 @@ func main() { DynamicServerAddress: fmt.Sprintf("http://0.0.0.0:%d", dynamicPort), CDPAddress: *cdp, Dir: *testsDir, + Filter: filterR, }) err := r.Run() diff --git a/e2e/runner/runner.go b/e2e/runner/runner.go index c03a02da..9e8dae55 100644 --- a/e2e/runner/runner.go +++ b/e2e/runner/runner.go @@ -13,6 +13,7 @@ import ( "io/ioutil" "os" "path/filepath" + "regexp" "time" ) @@ -22,6 +23,7 @@ type ( DynamicServerAddress string CDPAddress string Dir string + Filter *regexp.Regexp } Result struct { @@ -103,7 +105,15 @@ func (r *Runner) runQueries(dir string) ([]Result, error) { // read scripts for _, f := range files { - fName := filepath.Join(dir, f.Name()) + n := f.Name() + + if r.settings.Filter != nil { + if r.settings.Filter.Match([]byte(n)) != true { + continue + } + } + + fName := filepath.Join(dir, n) b, err := ioutil.ReadFile(fName) if err != nil { diff --git a/e2e/tests/doc_element_exists.fql b/e2e/tests/doc_element_exists.fql new file mode 100644 index 00000000..13ae40d7 --- /dev/null +++ b/e2e/tests/doc_element_exists.fql @@ -0,0 +1,10 @@ +LET url = @static + '/overview.html' +LET doc = PAGE(url) + +LET expectedP = TRUE +LET actualP = ELEMENT_EXISTS(doc, '.section-nav') + +LET expectedN = FALSE +LET actualN = ELEMENT_EXISTS(doc, '.foo-bar') + +RETURN EXPECT(expectedP + expectedN, actualP + expectedN) \ No newline at end of file diff --git a/e2e/tests/doc_element_exists_d.fql b/e2e/tests/doc_element_exists_d.fql new file mode 100644 index 00000000..155e5ba8 --- /dev/null +++ b/e2e/tests/doc_element_exists_d.fql @@ -0,0 +1,10 @@ +LET url = @dynamic +LET doc = PAGE(url) + +LET expectedP = TRUE +LET actualP = ELEMENT_EXISTS(doc, '.text-center') + +LET expectedN = FALSE +LET actualN = ELEMENT_EXISTS(doc, '.foo-bar') + +RETURN EXPECT(expectedP + expectedN, actualP + expectedN) \ No newline at end of file diff --git a/e2e/tests/el_element_exists.fql b/e2e/tests/el_element_exists.fql new file mode 100644 index 00000000..66f226f6 --- /dev/null +++ b/e2e/tests/el_element_exists.fql @@ -0,0 +1,12 @@ +LET url = @static + '/value.html' +LET doc = PAGE(url) + +LET el = ELEMENT(doc, "#listings_table") + +LET expectedP = TRUE +LET actualP = ELEMENT_EXISTS(el, '.odd') + +LET expectedN = FALSE +LET actualN = ELEMENT_EXISTS(el, '.foo-bar') + +RETURN EXPECT(expectedP + expectedN, actualP + expectedN) \ No newline at end of file diff --git a/e2e/tests/el_element_exists_d.fql b/e2e/tests/el_element_exists_d.fql new file mode 100644 index 00000000..18262516 --- /dev/null +++ b/e2e/tests/el_element_exists_d.fql @@ -0,0 +1,12 @@ +LET url = @dynamic +LET doc = PAGE(url) + +LET el = ELEMENT(doc, "#root") + +LET expectedP = TRUE +LET actualP = ELEMENT_EXISTS(el, '.jumbotron') + +LET expectedN = FALSE +LET actualN = ELEMENT_EXISTS(el, '.foo-bar') + +RETURN EXPECT(expectedP + expectedN, actualP + expectedN) \ No newline at end of file diff --git a/pkg/drivers/cdp/document.go b/pkg/drivers/cdp/document.go index 38a4fac0..7fcde532 100644 --- a/pkg/drivers/cdp/document.go +++ b/pkg/drivers/cdp/document.go @@ -369,6 +369,13 @@ func (doc *HTMLDocument) CountBySelector(selector values.String) values.Int { return doc.element.CountBySelector(selector) } +func (doc *HTMLDocument) ExistsBySelector(selector values.String) values.Boolean { + doc.Lock() + defer doc.Unlock() + + return doc.element.ExistsBySelector(selector) +} + func (doc *HTMLDocument) ClickBySelector(selector values.String) (values.Boolean, error) { res, err := eval.Eval( doc.client, diff --git a/pkg/drivers/cdp/element.go b/pkg/drivers/cdp/element.go index 4736a7f2..1fa3583c 100644 --- a/pkg/drivers/cdp/element.go +++ b/pkg/drivers/cdp/element.go @@ -679,6 +679,33 @@ func (el *HTMLElement) CountBySelector(selector values.String) values.Int { return values.NewInt(len(res.NodeIDs)) } +func (el *HTMLElement) ExistsBySelector(selector values.String) values.Boolean { + if !el.IsConnected() { + return values.False + } + + ctx, cancel := contextWithTimeout() + defer cancel() + + // TODO: Can we use RemoteObjectID or BackendID instead of NodeId? + selectorArgs := dom.NewQuerySelectorArgs(el.id.nodeID, selector.String()) + res, err := el.client.DOM.QuerySelector(ctx, selectorArgs) + + if err != nil { + el.logError(err). + Str("selector", selector.String()). + Msg("failed to retrieve nodes by selector") + + return values.False + } + + if res.NodeID == 0 { + return values.False + } + + return values.True +} + func (el *HTMLElement) WaitForClass(class values.String, timeout values.Int) error { task := events.NewWaitTask( func() (core.Value, error) { diff --git a/pkg/drivers/http/element.go b/pkg/drivers/http/element.go index 417ca532..a1ea4f21 100644 --- a/pkg/drivers/http/element.go +++ b/pkg/drivers/http/element.go @@ -248,6 +248,16 @@ func (el *HTMLElement) CountBySelector(selector values.String) values.Int { return values.NewInt(selection.Size()) } +func (el *HTMLElement) ExistsBySelector(selector values.String) values.Boolean { + selection := el.selection.Closest(selector.String()) + + if selection == nil { + return values.False + } + + return values.True +} + func (el *HTMLElement) parseAttrs() *values.Object { obj := values.NewObject() diff --git a/pkg/runtime/values/html.go b/pkg/runtime/values/html.go index 2889220d..fa73d74c 100644 --- a/pkg/runtime/values/html.go +++ b/pkg/runtime/values/html.go @@ -95,6 +95,8 @@ type ( InnerTextBySelectorAll(selector String) *Array CountBySelector(selector String) Int + + ExistsBySelector(selector String) Boolean } DHTMLNode interface { diff --git a/pkg/stdlib/html/element_exists.go b/pkg/stdlib/html/element_exists.go new file mode 100644 index 00000000..62b3ebe4 --- /dev/null +++ b/pkg/stdlib/html/element_exists.go @@ -0,0 +1,22 @@ +package html + +import ( + "context" + + "github.com/MontFerret/ferret/pkg/runtime/core" + "github.com/MontFerret/ferret/pkg/runtime/values" +) + +// ElementExists returns a boolean value indicating whether there is an element matched by selector. +// @param docOrEl (HTMLDocument|HTMLElement) - Parent document or element. +// @param selector (String) - CSS selector. +// @returns (Boolean) - A boolean value indicating whether there is an element matched by selector. +func ElementExists(_ context.Context, args ...core.Value) (core.Value, error) { + el, selector, err := queryArgs(args) + + if err != nil { + return values.None, err + } + + return el.ExistsBySelector(selector), nil +} diff --git a/pkg/stdlib/html/lib.go b/pkg/stdlib/html/lib.go index 0e38590c..0b1fbaf7 100644 --- a/pkg/stdlib/html/lib.go +++ b/pkg/stdlib/html/lib.go @@ -21,6 +21,7 @@ func NewLib() map[string]core.Function { "PAGE": Page, "DOWNLOAD": Download, "ELEMENT": Element, + "ELEMENT_EXISTS": ElementExists, "ELEMENTS": Elements, "ELEMENTS_COUNT": ElementsCount, "HOVER": Hover,