From 62e9093c4a74b3b7992e87dcfd815b2fefa0aaaa Mon Sep 17 00:00:00 2001 From: Tim Voronov Date: Fri, 13 Nov 2020 21:10:11 -0500 Subject: [PATCH 1/3] Added support of Element.nextElementSibling and Element.previousElementSibling --- .../dynamic/components/pages/lists/index.js | 4 +- e2e/pages/static/list.html | 11 +++++ e2e/tests/dynamic/element/siblings/next.fql | 12 +++++ e2e/tests/dynamic/element/siblings/prev.fql | 12 +++++ e2e/tests/static/element/siblings/next.fql | 13 +++++ e2e/tests/static/element/siblings/prev.fql | 13 +++++ pkg/compiler/compiler_member_test.go | 20 ++++++++ pkg/drivers/cdp/dom/element.go | 47 +++++++++++++++++++ pkg/drivers/cdp/templates/siblings.go | 12 +++++ pkg/drivers/common/getter.go | 4 ++ pkg/drivers/http/element.go | 32 +++++++++++-- pkg/drivers/value.go | 4 ++ 12 files changed, 177 insertions(+), 7 deletions(-) create mode 100644 e2e/pages/static/list.html create mode 100644 e2e/tests/dynamic/element/siblings/next.fql create mode 100644 e2e/tests/dynamic/element/siblings/prev.fql create mode 100644 e2e/tests/static/element/siblings/next.fql create mode 100644 e2e/tests/static/element/siblings/prev.fql create mode 100644 pkg/drivers/cdp/templates/siblings.go diff --git a/e2e/pages/dynamic/components/pages/lists/index.js b/e2e/pages/dynamic/components/pages/lists/index.js index 9284eec9..c3937175 100644 --- a/e2e/pages/dynamic/components/pages/lists/index.js +++ b/e2e/pages/dynamic/components/pages/lists/index.js @@ -4,8 +4,8 @@ const ITEMS = [{"artist":"Lil Tecca","track":"Ransom"},{"artist":"NLE Choppa","t export default class ListsPage extends React.Component { render() { - const items = ITEMS.map((i) => { - return e("li", { className: "list-group-item track"}, [ + const items = ITEMS.map((i, idx) => { + return e("li", { className: "list-group-item track", 'data-index': idx }, [ e("div", { className: "track-details"}, [ e("h5", { className: "track-artist"}, i.artist), e("small", { className: "track-name"}, i.track) diff --git a/e2e/pages/static/list.html b/e2e/pages/static/list.html new file mode 100644 index 00000000..2ca092c3 --- /dev/null +++ b/e2e/pages/static/list.html @@ -0,0 +1,11 @@ + + + + Ferret E2E SPA + + + + + +
  • Lil Tecca
    Ransom
  • NLE Choppa
    Shotta Flow (Feat. Blueface) [Remix]
  • Baby Jesus (DaBaby)
    Suge
  • NLE Choppa
    Shotta Flow 3
  • Lil Tecca
    Lil Tecca - Did It Again
  • NLE Choppa
    Shotta Flow
  • Ynw Melly
    Dangerously In Love (772 Love Pt. 2)
  • POLO G
    Polo G feat. Lil TJay - Pop Out
  • MUSTARD
    Ballin' (feat. Roddy Ricch)
  • Lil Nas X
    Panini
  • Juice WRLD
    Juice Wrld - RUN
  • Shordie Shordie
    Betchua (Bitchuary)
  • Post Malone
    Goodbyes (feat. Young Thug)
  • LIL UZI VERT
    Sanguine Paradise
  • Calboy
    Envy Me
  • Ambjaay
    Uno
  • Lil Tecca
    Lil Tecca - Bossanova
  • Lil Baby
    Baby
  • Lil Tjay
    Lil Tjay - Brothers (Prod by JDONTHATRACK & Protegebeatz)
  • YK Osiris
    Worth It
+ \ No newline at end of file diff --git a/e2e/tests/dynamic/element/siblings/next.fql b/e2e/tests/dynamic/element/siblings/next.fql new file mode 100644 index 00000000..450c41bb --- /dev/null +++ b/e2e/tests/dynamic/element/siblings/next.fql @@ -0,0 +1,12 @@ +LET doc = DOCUMENT(@lab.cdn.dynamic + "/#/lists", { driver:"cdp" }) + +LET current = ELEMENT(doc, ".track") +T::NOT::NONE(current) +LET next = current.nextElementSibling +T::NOT::NONE(next) + +LET currentIdx = TO_INT(current.attributes['data-index']) +LET nextIdx = TO_INT(next.attributes['data-index']) +T::GT(nextIdx, currentIdx) + +RETURN NONE \ No newline at end of file diff --git a/e2e/tests/dynamic/element/siblings/prev.fql b/e2e/tests/dynamic/element/siblings/prev.fql new file mode 100644 index 00000000..aeb7f77c --- /dev/null +++ b/e2e/tests/dynamic/element/siblings/prev.fql @@ -0,0 +1,12 @@ +LET doc = DOCUMENT(@lab.cdn.dynamic + "/#/lists", { driver:"cdp" }) + +LET current = ELEMENT(doc, '[data-index="1"]') +T::NOT::NONE(current) +LET prev = current.previousElementSibling +T::NOT::NONE(prev) + +LET currentIdx = TO_INT(current.attributes['data-index']) +LET prevIdx = TO_INT(prev.attributes['data-index']) +T::LT(prevIdx, currentIdx) + +RETURN NONE \ No newline at end of file diff --git a/e2e/tests/static/element/siblings/next.fql b/e2e/tests/static/element/siblings/next.fql new file mode 100644 index 00000000..da48803e --- /dev/null +++ b/e2e/tests/static/element/siblings/next.fql @@ -0,0 +1,13 @@ +LET url = @lab.cdn.static + '/list.html' +LET doc = DOCUMENT(url) + +LET current = ELEMENT(doc, ".track") +T::NOT::NONE(current) +LET next = current.nextElementSibling.nextElementSibling +T::NOT::NONE(next) + +LET currentIdx = TO_INT(current.attributes['data-index']) +LET nextIdx = TO_INT(next.attributes['data-index']) +T::GT(nextIdx, currentIdx) + +RETURN NONE \ No newline at end of file diff --git a/e2e/tests/static/element/siblings/prev.fql b/e2e/tests/static/element/siblings/prev.fql new file mode 100644 index 00000000..cab52d09 --- /dev/null +++ b/e2e/tests/static/element/siblings/prev.fql @@ -0,0 +1,13 @@ +LET url = @lab.cdn.static + '/list.html' +LET doc = DOCUMENT(url) + +LET current = ELEMENT(doc, '[data-index="1"]') +T::NOT::NONE(current) +LET prev = current.previousElementSibling +T::NOT::NONE(prev) + +LET currentIdx = TO_INT(current.attributes['data-index']) +LET prevIdx = TO_INT(prev.attributes['data-index']) +T::LT(prevIdx, currentIdx) + +RETURN NONE \ No newline at end of file diff --git a/pkg/compiler/compiler_member_test.go b/pkg/compiler/compiler_member_test.go index ebafbcc5..ce6df841 100644 --- a/pkg/compiler/compiler_member_test.go +++ b/pkg/compiler/compiler_member_test.go @@ -215,6 +215,26 @@ func TestMember(t *testing.T) { So(string(out), ShouldEqual, `"Bob"`) }) + + Convey("Computed property with quotes", func() { + c := compiler.New() + + p := c.MustCompile(` + LET obj = { + attributes: { + 'data-index': 1 + } + } + + RETURN obj.attributes['data-index'] + `) + + out, err := p.Run(context.Background()) + + So(err, ShouldBeNil) + + So(string(out), ShouldEqual, "1") + }) }) } diff --git a/pkg/drivers/cdp/dom/element.go b/pkg/drivers/cdp/dom/element.go index 9d03bd31..a599e50c 100644 --- a/pkg/drivers/cdp/dom/element.go +++ b/pkg/drivers/cdp/dom/element.go @@ -527,6 +527,53 @@ func (el *HTMLElement) GetChildNode(ctx context.Context, idx values.Int) (core.V ) } +func (el *HTMLElement) GetPreviousElementSibling(ctx context.Context) (core.Value, error) { + return el.getSibling(ctx, templates.GetPreviousElementSibling()) +} + +func (el *HTMLElement) GetNextElementSibling(ctx context.Context) (core.Value, error) { + return el.getSibling(ctx, templates.GetNextElementSibling()) +} + +func (el *HTMLElement) getSibling(ctx context.Context, expr string) (core.Value, error) { + if el.IsDetached() { + return values.None, drivers.ErrDetached + } + + obj, err := el.exec.EvalWithArgumentsAndReturnReference(ctx, expr, runtime.CallArgument{ + ObjectID: &el.id.ObjectID, + }) + + if err != nil { + return values.None, err + } + + el.logger.Debug().Interface("obj.ObjectID", obj.ObjectID).Msg("output") + + if obj.Type != "object" || obj.ObjectID == nil { + return values.None, nil + } + + repl, err := el.client.DOM.RequestNode(ctx, dom.NewRequestNodeArgs(*obj.ObjectID)) + + if err != nil { + return values.None, err + } + + return LoadHTMLElementWithID( + ctx, + el.logger, + el.client, + el.dom, + el.input, + el.exec, + HTMLElementIdentity{ + NodeID: repl.NodeID, + ObjectID: *obj.ObjectID, + }, + ) +} + func (el *HTMLElement) QuerySelector(ctx context.Context, selector values.String) (core.Value, error) { if el.IsDetached() { return values.None, drivers.ErrDetached diff --git a/pkg/drivers/cdp/templates/siblings.go b/pkg/drivers/cdp/templates/siblings.go new file mode 100644 index 00000000..20152363 --- /dev/null +++ b/pkg/drivers/cdp/templates/siblings.go @@ -0,0 +1,12 @@ +package templates + +const getPreviousElementSibling = "(el) => el.previousElementSibling" +const getNextElementSibling = "(el) => el.nextElementSibling" + +func GetPreviousElementSibling() string { + return getPreviousElementSibling +} + +func GetNextElementSibling() string { + return getNextElementSibling +} diff --git a/pkg/drivers/common/getter.go b/pkg/drivers/common/getter.go index 3625fe75..063ad735 100644 --- a/pkg/drivers/common/getter.go +++ b/pkg/drivers/common/getter.go @@ -194,6 +194,10 @@ func GetInElement(ctx context.Context, el drivers.HTMLElement, path []core.Value } return values.GetIn(ctx, styles, path[1:]) + case "previousElementSibling": + return el.GetPreviousElementSibling(ctx) + case "nextElementSibling": + return el.GetNextElementSibling(ctx) default: return GetInNode(ctx, el, path) } diff --git a/pkg/drivers/http/element.go b/pkg/drivers/http/element.go index ffee3b91..b3dac5aa 100644 --- a/pkg/drivers/http/element.go +++ b/pkg/drivers/http/element.go @@ -489,6 +489,26 @@ func (el *HTMLElement) Iterate(_ context.Context) (core.Iterator, error) { return common.NewIterator(el) } +func (el *HTMLElement) GetPreviousElementSibling(ctx context.Context) (core.Value, error) { + sibling := el.selection.Prev() + + if sibling == nil { + return values.None, nil + } + + return NewHTMLElement(sibling) +} + +func (el *HTMLElement) GetNextElementSibling(ctx context.Context) (core.Value, error) { + sibling := el.selection.Next() + + if sibling == nil { + return values.None, nil + } + + return NewHTMLElement(sibling) +} + func (el *HTMLElement) Click(_ context.Context, _ values.Int) error { return core.ErrNotSupported } @@ -608,12 +628,14 @@ func (el *HTMLElement) ensureAttrs() { func (el *HTMLElement) parseAttrs() *values.Object { obj := values.NewObject() - for _, name := range common.Attributes { - val, ok := el.selection.Attr(name) + if len(el.selection.Nodes) == 0 { + return obj + } - if ok { - obj.Set(values.NewString(name), values.NewString(val)) - } + node := el.selection.Nodes[0] + + for _, attr := range node.Attr { + obj.Set(values.NewString(attr.Key), values.NewString(attr.Val)) } return obj diff --git a/pkg/drivers/value.go b/pkg/drivers/value.go index 33ac3a92..5ecc47c3 100644 --- a/pkg/drivers/value.go +++ b/pkg/drivers/value.go @@ -93,6 +93,10 @@ type ( GetInnerTextBySelectorAll(ctx context.Context, selector values.String) (*values.Array, error) + GetPreviousElementSibling(ctx context.Context) (core.Value, error) + + GetNextElementSibling(ctx context.Context) (core.Value, error) + Click(ctx context.Context, count values.Int) error ClickBySelector(ctx context.Context, selector values.String, count values.Int) error From 5715166a99948d30e9aeda708c2ff96fc8fff355 Mon Sep 17 00:00:00 2001 From: Tim Voronov Date: Fri, 13 Nov 2020 21:12:59 -0500 Subject: [PATCH 2/3] Fixed linting issues --- pkg/drivers/http/element.go | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/pkg/drivers/http/element.go b/pkg/drivers/http/element.go index b3dac5aa..045a470c 100644 --- a/pkg/drivers/http/element.go +++ b/pkg/drivers/http/element.go @@ -489,7 +489,7 @@ func (el *HTMLElement) Iterate(_ context.Context) (core.Iterator, error) { return common.NewIterator(el) } -func (el *HTMLElement) GetPreviousElementSibling(ctx context.Context) (core.Value, error) { +func (el *HTMLElement) GetPreviousElementSibling(_ context.Context) (core.Value, error) { sibling := el.selection.Prev() if sibling == nil { @@ -499,7 +499,7 @@ func (el *HTMLElement) GetPreviousElementSibling(ctx context.Context) (core.Valu return NewHTMLElement(sibling) } -func (el *HTMLElement) GetNextElementSibling(ctx context.Context) (core.Value, error) { +func (el *HTMLElement) GetNextElementSibling(_ context.Context) (core.Value, error) { sibling := el.selection.Next() if sibling == nil { From 6b1efcaa7fbfae414c404e2e8d9df052e6a2b845 Mon Sep 17 00:00:00 2001 From: Tim Voronov Date: Fri, 13 Nov 2020 21:22:30 -0500 Subject: [PATCH 3/3] Removed reedundant logger use --- pkg/drivers/cdp/dom/element.go | 2 -- 1 file changed, 2 deletions(-) diff --git a/pkg/drivers/cdp/dom/element.go b/pkg/drivers/cdp/dom/element.go index a599e50c..f1c0fe44 100644 --- a/pkg/drivers/cdp/dom/element.go +++ b/pkg/drivers/cdp/dom/element.go @@ -548,8 +548,6 @@ func (el *HTMLElement) getSibling(ctx context.Context, expr string) (core.Value, return values.None, err } - el.logger.Debug().Interface("obj.ObjectID", obj.ObjectID).Msg("output") - if obj.Type != "object" || obj.ObjectID == nil { return values.None, nil }