From 62f6af6828c457aee4a3e10249901dc70afd15bf Mon Sep 17 00:00:00 2001 From: Tim Voronov Date: Mon, 11 Mar 2019 21:27:01 -0400 Subject: [PATCH 1/6] Added support for parsed styles --- Gopkg.lock | 9 + Gopkg.toml | 4 + pkg/drivers/cdp/element.go | 144 +++++++++++++++ pkg/drivers/cdp/helpers.go | 2 +- pkg/drivers/common/getter.go | 12 ++ pkg/drivers/common/lazy.go | 6 +- pkg/drivers/common/setter.go | 2 + pkg/drivers/common/styles.go | 103 +++++++++++ pkg/drivers/common/styles_test.go | 87 +++++++++ pkg/drivers/http/element.go | 297 +++++++++++++++++++++--------- pkg/drivers/value.go | 14 ++ 11 files changed, 592 insertions(+), 88 deletions(-) create mode 100644 pkg/drivers/common/styles.go create mode 100644 pkg/drivers/common/styles_test.go diff --git a/Gopkg.lock b/Gopkg.lock index 9b01dbe2..1f3e8b44 100644 --- a/Gopkg.lock +++ b/Gopkg.lock @@ -65,6 +65,14 @@ pruneopts = "UT" revision = "d547d1d9531ed93dbdebcbff7f83e7c876a1e0ee" +[[projects]] + digest = "1:fc51ecee8f31d03436c1a0167eb1e383ad0a241d02272541853f3995374a08f1" + name = "github.com/gorilla/css" + packages = ["scanner"] + pruneopts = "UT" + revision = "398b0b046082ecb3694c01bec6b336a06a4e530a" + version = "v1.0.0" + [[projects]] digest = "1:7b5c6e2eeaa9ae5907c391a91c132abfd5c9e8a784a341b5625e750c67e6825d" name = "github.com/gorilla/websocket" @@ -293,6 +301,7 @@ input-imports = [ "github.com/PuerkitoBio/goquery", "github.com/antlr/antlr4/runtime/Go/antlr", + "github.com/aymerick/douceur/parser", "github.com/chzyer/readline", "github.com/corpix/uarand", "github.com/derekparker/trie", diff --git a/Gopkg.toml b/Gopkg.toml index 639b80f1..a7aa4183 100644 --- a/Gopkg.toml +++ b/Gopkg.toml @@ -45,6 +45,10 @@ name = "github.com/PuerkitoBio/goquery" version = "1.5.0" +[[constraint]] + name = "github.com/gorilla/css" + version = "v1.0.0" + [[constraint]] name = "github.com/gofrs/uuid" version = "3.1.2" diff --git a/pkg/drivers/cdp/element.go b/pkg/drivers/cdp/element.go index 630be544..c544d803 100644 --- a/pkg/drivers/cdp/element.go +++ b/pkg/drivers/cdp/element.go @@ -49,6 +49,7 @@ type ( value core.Value rawAttrs []string attributes *common.LazyValue + style *common.LazyValue children []*HTMLElementIdentity loadedChildren *common.LazyValue } @@ -159,6 +160,7 @@ func NewHTMLElement( el.innerText = common.NewLazyValue(el.loadInnerText) el.rawAttrs = attributes el.attributes = common.NewLazyValue(el.loadAttrs) + el.style = common.NewLazyValue(el.parseStyle) el.value = values.EmptyString el.loadedChildren = common.NewLazyValue(el.loadChildren) el.value = values.NewString(value) @@ -297,6 +299,99 @@ func (el *HTMLElement) Length() values.Int { return values.NewInt(len(el.children)) } +func (el *HTMLElement) GetStyles(ctx context.Context) (*values.Object, error) { + val, err := el.style.Read(ctx) + + if err != nil { + return values.NewObject(), err + } + + if val == values.None { + return values.NewObject(), nil + } + + styles := val.(*values.Object) + + return styles.Copy().(*values.Object), nil +} + +func (el *HTMLElement) GetStyle(ctx context.Context, name values.String) (core.Value, error) { + styles, err := el.style.Read(ctx) + + if err != nil { + return values.None, err + } + + val, found := styles.(*values.Object).Get(name) + + if !found { + return values.None, nil + } + + return val, nil +} + +func (el *HTMLElement) SetStyles(ctx context.Context, styles *values.Object) error { + if styles == nil { + return nil + } + + val, err := el.style.Read(ctx) + + if err != nil { + return err + } + + currentStyles := val.(*values.Object) + + styles.ForEach(func(value core.Value, key string) bool { + currentStyles.Set(values.NewString(key), value) + + return true + }) + + str := common.SerializeStyles(ctx, currentStyles) + + return el.SetAttribute(ctx, "style", str) +} + +func (el *HTMLElement) SetStyle(ctx context.Context, name values.String, value core.Value) error { + val, err := el.style.Read(ctx) + + if err != nil { + return err + } + + styles := val.(*values.Object) + styles.Set(name, value) + + str := common.SerializeStyles(ctx, styles) + + return el.SetAttribute(ctx, "style", str) +} + +func (el *HTMLElement) RemoveStyle(ctx context.Context, names ...values.String) error { + if len(names) == 0 { + return nil + } + + val, err := el.style.Read(ctx) + + if err != nil { + return err + } + + styles := val.(*values.Object) + + for _, name := range names { + styles.Remove(name) + } + + str := common.SerializeStyles(ctx, styles) + + return el.SetAttribute(ctx, "style", str) +} + func (el *HTMLElement) GetAttributes(ctx context.Context) *values.Object { val, err := el.attributes.Read(ctx) @@ -326,6 +421,18 @@ func (el *HTMLElement) GetAttribute(ctx context.Context, name values.String) cor return val } +func (el *HTMLElement) SetAttributes(ctx context.Context, attrs *values.Object) error { + var err error + + attrs.ForEach(func(value core.Value, key string) bool { + err = el.SetAttribute(ctx, values.NewString(key), values.NewString(value.String())) + + return err == nil + }) + + return err +} + func (el *HTMLElement) SetAttribute(ctx context.Context, name, value values.String) error { return el.client.DOM.SetAttributeValue( ctx, @@ -333,6 +440,21 @@ func (el *HTMLElement) SetAttribute(ctx context.Context, name, value values.Stri ) } +func (el *HTMLElement) RemoveAttribute(ctx context.Context, names ...values.String) error { + for _, name := range names { + err := el.client.DOM.RemoveAttribute( + ctx, + dom.NewRemoveAttributeArgs(el.id.nodeID, name.String()), + ) + + if err != nil { + return err + } + } + + return nil +} + func (el *HTMLElement) GetChildNodes(ctx context.Context) core.Value { val, err := el.loadedChildren.Read(ctx) @@ -973,6 +1095,20 @@ func (el *HTMLElement) loadChildren(ctx context.Context) (core.Value, error) { return loaded, nil } +func (el *HTMLElement) parseStyle(ctx context.Context) (core.Value, error) { + value := el.GetAttribute(ctx, "style") + + if value == values.None { + return values.NewObject(), nil + } + + if value.Type() != types.String { + return values.NewObject(), nil + } + + return common.DeserializeStyles(value.(values.String)) +} + func (el *HTMLElement) handlePageReload(_ context.Context, _ interface{}) { el.Close() } @@ -997,6 +1133,10 @@ func (el *HTMLElement) handleAttrModified(ctx context.Context, message interface return } + if reply.Name == "style" { + el.style.Reset() + } + attrs, ok := v.(*values.Object) if !ok { @@ -1033,6 +1173,10 @@ func (el *HTMLElement) handleAttrRemoved(ctx context.Context, message interface{ return } + if reply.Name == "style" { + el.style.Reset() + } + attrs, ok := v.(*values.Object) if !ok { diff --git a/pkg/drivers/cdp/helpers.go b/pkg/drivers/cdp/helpers.go index ce921eb7..d3279182 100644 --- a/pkg/drivers/cdp/helpers.go +++ b/pkg/drivers/cdp/helpers.go @@ -4,10 +4,10 @@ import ( "bytes" "context" "errors" - "github.com/MontFerret/ferret/pkg/drivers" "math" "strings" + "github.com/MontFerret/ferret/pkg/drivers" "github.com/MontFerret/ferret/pkg/drivers/cdp/eval" "github.com/MontFerret/ferret/pkg/drivers/cdp/events" "github.com/MontFerret/ferret/pkg/drivers/common" diff --git a/pkg/drivers/common/getter.go b/pkg/drivers/common/getter.go index e37d64b3..b4fd4918 100644 --- a/pkg/drivers/common/getter.go +++ b/pkg/drivers/common/getter.go @@ -59,6 +59,18 @@ func GetInElement(ctx context.Context, el drivers.HTMLElement, path []core.Value } return values.GetIn(ctx, attrs, path[1:]) + case "style": + styles, err := el.GetStyles(ctx) + + if err != nil { + return values.None, err + } + + if len(path) == 1 { + return styles, nil + } + + return values.GetIn(ctx, styles, path[1:]) default: return GetInNode(ctx, el, path) } diff --git a/pkg/drivers/common/lazy.go b/pkg/drivers/common/lazy.go index 9afd32f7..4cfc0060 100644 --- a/pkg/drivers/common/lazy.go +++ b/pkg/drivers/common/lazy.go @@ -9,18 +9,18 @@ import ( ) type ( - LazyFactory func(ctx context.Context) (core.Value, error) + LazyValueFactory func(ctx context.Context) (core.Value, error) LazyValue struct { sync.Mutex - factory LazyFactory + factory LazyValueFactory ready bool value core.Value err error } ) -func NewLazyValue(factory LazyFactory) *LazyValue { +func NewLazyValue(factory LazyValueFactory) *LazyValue { lz := new(LazyValue) lz.ready = false lz.factory = factory diff --git a/pkg/drivers/common/setter.go b/pkg/drivers/common/setter.go index 78851f8a..bd9fe073 100644 --- a/pkg/drivers/common/setter.go +++ b/pkg/drivers/common/setter.go @@ -62,6 +62,8 @@ func SetInElement(ctx context.Context, el drivers.HTMLElement, path []core.Value }) return err + case "style": + case "value": if len(path) > 1 { return core.Error(ErrInvalidPath, PathToString(path[1:])) diff --git a/pkg/drivers/common/styles.go b/pkg/drivers/common/styles.go new file mode 100644 index 00000000..afd1899a --- /dev/null +++ b/pkg/drivers/common/styles.go @@ -0,0 +1,103 @@ +package common + +import ( + "bytes" + "context" + "github.com/MontFerret/ferret/pkg/runtime/core" + "strconv" + "strings" + + "github.com/MontFerret/ferret/pkg/runtime/values" + "github.com/gorilla/css/scanner" +) + +func DeserializeStyles(input values.String) (*values.Object, error) { + styles := values.NewObject() + + if input == values.EmptyString { + return styles, nil + } + + s := scanner.New(input.String()) + + var name string + var value bytes.Buffer + var setValue = func() { + styles.Set(values.NewString(strings.TrimSpace(name)), values.NewString(strings.TrimSpace(value.String()))) + name = "" + value.Reset() + } + + for { + token := s.Next() + + if token == nil { + break + } + + if token.Type == scanner.TokenEOF { + break + } + + if name == "" && token.Type == scanner.TokenIdent { + name = token.Value + + // skip : and white spaces + for { + token = s.Next() + + if token.Value != ":" && token.Type != scanner.TokenS { + break + } + } + } + + switch token.Type { + case scanner.TokenChar: + // end of style declaration + if token.Value == ";" { + if name != "" { + setValue() + } + } else { + value.WriteString(token.Value) + } + case scanner.TokenNumber: + num, err := strconv.ParseFloat(token.Value, 64) + + if err == nil { + styles.Set(values.NewString(name), values.NewFloat(num)) + // reset prop + name = "" + value.Reset() + } + default: + value.WriteString(token.Value) + } + } + + if name != "" && value.Len() > 0 { + setValue() + } + + return styles, nil +} + +func SerializeStyles(_ context.Context, styles *values.Object) values.String { + if styles == nil { + return values.EmptyString + } + + var b bytes.Buffer + + styles.ForEach(func(value core.Value, key string) bool { + b.WriteString(key) + b.WriteString(": ") + b.WriteString(value.String()) + b.WriteString("; ") + + return true + }) + + return values.NewString(b.String()) +} diff --git a/pkg/drivers/common/styles_test.go b/pkg/drivers/common/styles_test.go new file mode 100644 index 00000000..3ed02a7b --- /dev/null +++ b/pkg/drivers/common/styles_test.go @@ -0,0 +1,87 @@ +package common_test + +import ( + "bytes" + "github.com/MontFerret/ferret/pkg/drivers/common" + "github.com/MontFerret/ferret/pkg/runtime/core" + "github.com/MontFerret/ferret/pkg/runtime/values" + "testing" + + . "github.com/smartystreets/goconvey/convey" +) + +type style struct { + raw string + name values.String + value core.Value +} + +func TestDeserializeStyles(t *testing.T) { + Convey("DeserializeStyles", t, func() { + styles := []style{ + { + raw: "min-height: 1.15", + name: "min-height", + value: values.NewFloat(1.15), + }, + { + raw: "background-color: #4A154B", + name: "background-color", + value: values.NewString("#4A154B"), + }, + { + raw: "font-size:26pt", + name: "font-size", + value: values.NewString("26pt"), + }, + { + raw: "page-break-after:avoid", + name: "page-break-after", + value: values.NewString("avoid"), + }, + { + raw: `font-family: Arial,"Helvetica Neue",Helvetica,sans-serif`, + name: "font-family", + value: values.NewString(`Arial,"Helvetica Neue",Helvetica,sans-serif`), + }, + } + + Convey("Should parse a single style", func() { + for _, s := range styles { + out, err := common.DeserializeStyles(values.NewString(s.raw)) + + So(err, ShouldBeNil) + So(out, ShouldNotBeNil) + + value, exists := out.Get(s.name) + + So(bool(exists), ShouldBeTrue) + + So(value.Compare(s.value) == 0, ShouldBeTrue) + } + }) + + Convey("Should parse multiple styles", func() { + var buff bytes.Buffer + + for _, s := range styles { + buff.WriteString(s.raw) + buff.WriteString("; ") + } + + out, err := common.DeserializeStyles(values.NewString(buff.String())) + + So(err, ShouldBeNil) + So(out, ShouldNotBeNil) + So(int(out.Length()), ShouldEqual, len(styles)) + + for _, s := range styles { + value, exists := out.Get(s.name) + + So(bool(exists), ShouldBeTrue) + + So(value.Compare(s.value) == 0, ShouldBeTrue) + } + }) + }) +} diff --git a/pkg/drivers/http/element.go b/pkg/drivers/http/element.go index 17f3fb9b..7daa7997 100644 --- a/pkg/drivers/http/element.go +++ b/pkg/drivers/http/element.go @@ -16,6 +16,7 @@ import ( type HTMLElement struct { selection *goquery.Selection attrs *values.Object + styles *values.Object children *values.Array } @@ -24,22 +25,22 @@ func NewHTMLElement(node *goquery.Selection) (drivers.HTMLElement, error) { return nil, core.Error(core.ErrMissedArgument, "element selection") } - return &HTMLElement{node, nil, nil}, nil + return &HTMLElement{node, nil, nil, nil}, nil } -func (nd *HTMLElement) MarshalJSON() ([]byte, error) { - return json.Marshal(nd.InnerText(context.Background()).String()) +func (el *HTMLElement) MarshalJSON() ([]byte, error) { + return json.Marshal(el.InnerText(context.Background()).String()) } -func (nd *HTMLElement) Type() core.Type { +func (el *HTMLElement) Type() core.Type { return drivers.HTMLElementType } -func (nd *HTMLElement) String() string { - return nd.InnerHTML(context.Background()).String() +func (el *HTMLElement) String() string { + return el.InnerHTML(context.Background()).String() } -func (nd *HTMLElement) Compare(other core.Value) int64 { +func (el *HTMLElement) Compare(other core.Value) int64 { switch other.Type() { case drivers.HTMLElementType: other := other.(drivers.HTMLElement) @@ -47,18 +48,18 @@ func (nd *HTMLElement) Compare(other core.Value) int64 { ctx, fn := drivers.WithDefaultTimeout(context.Background()) defer fn() - return nd.InnerHTML(ctx).Compare(other.InnerHTML(ctx)) + return el.InnerHTML(ctx).Compare(other.InnerHTML(ctx)) default: - return drivers.Compare(nd.Type(), other.Type()) + return drivers.Compare(el.Type(), other.Type()) } } -func (nd *HTMLElement) Unwrap() interface{} { - return nd.selection +func (el *HTMLElement) Unwrap() interface{} { + return el.selection } -func (nd *HTMLElement) Hash() uint64 { - str, err := nd.selection.Html() +func (el *HTMLElement) Hash() uint64 { + str, err := el.selection.Html() if err != nil { return 0 @@ -66,21 +67,21 @@ func (nd *HTMLElement) Hash() uint64 { h := fnv.New64a() - h.Write([]byte(nd.Type().String())) + h.Write([]byte(el.Type().String())) h.Write([]byte(":")) h.Write([]byte(str)) return h.Sum64() } -func (nd *HTMLElement) Copy() core.Value { - c, _ := NewHTMLElement(nd.selection.Clone()) +func (el *HTMLElement) Copy() core.Value { + c, _ := NewHTMLElement(el.selection.Clone()) return c } -func (nd *HTMLElement) NodeType() values.Int { - nodes := nd.selection.Nodes +func (el *HTMLElement) NodeType() values.Int { + nodes := el.selection.Nodes if len(nodes) == 0 { return 0 @@ -89,24 +90,24 @@ func (nd *HTMLElement) NodeType() values.Int { return values.NewInt(common.ToHTMLType(nodes[0].Type)) } -func (nd *HTMLElement) Close() error { +func (el *HTMLElement) Close() error { return nil } -func (nd *HTMLElement) NodeName() values.String { - return values.NewString(goquery.NodeName(nd.selection)) +func (el *HTMLElement) NodeName() values.String { + return values.NewString(goquery.NodeName(el.selection)) } -func (nd *HTMLElement) Length() values.Int { - if nd.children == nil { - nd.children = nd.parseChildren() +func (el *HTMLElement) Length() values.Int { + if el.children == nil { + el.children = el.parseChildren() } - return nd.children.Length() + return el.children.Length() } -func (nd *HTMLElement) GetValue(_ context.Context) core.Value { - val, ok := nd.selection.Attr("value") +func (el *HTMLElement) GetValue(_ context.Context) core.Value { + val, ok := el.selection.Attr("value") if ok { return values.NewString(val) @@ -115,18 +116,18 @@ func (nd *HTMLElement) GetValue(_ context.Context) core.Value { return values.EmptyString } -func (nd *HTMLElement) SetValue(_ context.Context, value core.Value) error { - nd.selection.SetAttr("value", value.String()) +func (el *HTMLElement) SetValue(_ context.Context, value core.Value) error { + el.selection.SetAttr("value", value.String()) return nil } -func (nd *HTMLElement) InnerText(_ context.Context) values.String { - return values.NewString(nd.selection.Text()) +func (el *HTMLElement) InnerText(_ context.Context) values.String { + return values.NewString(el.selection.Text()) } -func (nd *HTMLElement) InnerHTML(_ context.Context) values.String { - h, err := nd.selection.Html() +func (el *HTMLElement) InnerHTML(_ context.Context) values.String { + h, err := el.selection.Html() if err != nil { return values.EmptyString @@ -135,48 +136,140 @@ func (nd *HTMLElement) InnerHTML(_ context.Context) values.String { return values.NewString(h) } -func (nd *HTMLElement) GetAttributes(_ context.Context) *values.Object { - if nd.attrs == nil { - nd.attrs = nd.parseAttrs() +func (el *HTMLElement) GetStyles(ctx context.Context) (*values.Object, error) { + if err := el.ensureStyles(ctx); err != nil { + return values.NewObject(), err } - return nd.attrs + return el.styles.Copy().(*values.Object), nil } -func (nd *HTMLElement) GetAttribute(_ context.Context, name values.String) core.Value { - v, ok := nd.selection.Attr(name.String()) +func (el *HTMLElement) GetStyle(ctx context.Context, name values.String) (core.Value, error) { + if err := el.ensureStyles(ctx); err != nil { + return values.None, err + } - if ok { - return values.NewString(v) + return el.styles.MustGet(name), nil +} + +func (el *HTMLElement) SetStyle(ctx context.Context, name values.String, value core.Value) error { + if err := el.ensureStyles(ctx); err != nil { + return err + } + + el.styles.Set(name, value) + + str := common.SerializeStyles(ctx, el.styles) + + return el.SetAttribute(ctx, "style", str) +} + +func (el *HTMLElement) SetStyles(ctx context.Context, newStyles *values.Object) error { + if newStyles == nil { + return nil + } + + if err := el.ensureStyles(ctx); err != nil { + return err + } + + newStyles.ForEach(func(i core.Value, key string) bool { + el.styles.Set(values.NewString(key), i) + + return true + }) + + str := common.SerializeStyles(ctx, el.styles) + + return el.SetAttribute(ctx, "style", str) +} + +func (el *HTMLElement) RemoveStyle(ctx context.Context, name ...values.String) error { + if len(name) == 0 { + return nil + } + + if err := el.ensureStyles(ctx); err != nil { + return err } - return values.None + for _, s := range name { + el.styles.Remove(s) + } + + str := common.SerializeStyles(ctx, el.styles) + + return el.SetAttribute(ctx, "style", str) +} + +func (el *HTMLElement) SetAttributes(ctx context.Context, attrs *values.Object) error { + if attrs == nil { + return nil + } + + el.ensureAttrs() + + var err error + + attrs.ForEach(func(value core.Value, key string) bool { + err = el.SetAttribute(ctx, values.NewString(key), values.NewString(value.String())) + + return err == nil + }) + + return err } -func (nd *HTMLElement) SetAttribute(_ context.Context, name, value values.String) error { - nd.selection.SetAttr(string(name), string(value)) +func (el *HTMLElement) GetAttributes(_ context.Context) *values.Object { + el.ensureAttrs() + + return el.attrs.Copy().(*values.Object) +} + +func (el *HTMLElement) GetAttribute(_ context.Context, name values.String) core.Value { + el.ensureAttrs() + + return el.attrs.MustGet(name) +} + +func (el *HTMLElement) SetAttribute(_ context.Context, name, value values.String) error { + el.ensureAttrs() + + el.attrs.Set(name, value) + el.selection.SetAttr(string(name), string(value)) return nil } -func (nd *HTMLElement) GetChildNodes(_ context.Context) core.Value { - if nd.children == nil { - nd.children = nd.parseChildren() +func (el *HTMLElement) RemoveAttribute(ctx context.Context, name ...values.String) error { + el.ensureAttrs() + + for _, attr := range name { + el.attrs.Remove(attr) + el.selection.RemoveAttr(attr.String()) } - return nd.children + return nil } -func (nd *HTMLElement) GetChildNode(_ context.Context, idx values.Int) core.Value { - if nd.children == nil { - nd.children = nd.parseChildren() +func (el *HTMLElement) GetChildNodes(_ context.Context) core.Value { + if el.children == nil { + el.children = el.parseChildren() } - return nd.children.Get(idx) + return el.children } -func (nd *HTMLElement) QuerySelector(_ context.Context, selector values.String) core.Value { - selection := nd.selection.Find(selector.String()) +func (el *HTMLElement) GetChildNode(_ context.Context, idx values.Int) core.Value { + if el.children == nil { + el.children = el.parseChildren() + } + + return el.children.Get(idx) +} + +func (el *HTMLElement) QuerySelector(_ context.Context, selector values.String) core.Value { + selection := el.selection.Find(selector.String()) if selection == nil { return values.None @@ -191,8 +284,8 @@ func (nd *HTMLElement) QuerySelector(_ context.Context, selector values.String) return res } -func (nd *HTMLElement) QuerySelectorAll(_ context.Context, selector values.String) core.Value { - selection := nd.selection.Find(selector.String()) +func (el *HTMLElement) QuerySelectorAll(_ context.Context, selector values.String) core.Value { + selection := el.selection.Find(selector.String()) if selection == nil { return values.None @@ -211,8 +304,8 @@ func (nd *HTMLElement) QuerySelectorAll(_ context.Context, selector values.Strin return arr } -func (nd *HTMLElement) InnerHTMLBySelector(_ context.Context, selector values.String) values.String { - selection := nd.selection.Find(selector.String()) +func (el *HTMLElement) InnerHTMLBySelector(_ context.Context, selector values.String) values.String { + selection := el.selection.Find(selector.String()) str, err := selection.Html() @@ -224,8 +317,8 @@ func (nd *HTMLElement) InnerHTMLBySelector(_ context.Context, selector values.St return values.NewString(str) } -func (nd *HTMLElement) InnerHTMLBySelectorAll(_ context.Context, selector values.String) *values.Array { - selection := nd.selection.Find(selector.String()) +func (el *HTMLElement) InnerHTMLBySelectorAll(_ context.Context, selector values.String) *values.Array { + selection := el.selection.Find(selector.String()) arr := values.NewArray(selection.Length()) selection.Each(func(_ int, selection *goquery.Selection) { @@ -240,14 +333,14 @@ func (nd *HTMLElement) InnerHTMLBySelectorAll(_ context.Context, selector values return arr } -func (nd *HTMLElement) InnerTextBySelector(_ context.Context, selector values.String) values.String { - selection := nd.selection.Find(selector.String()) +func (el *HTMLElement) InnerTextBySelector(_ context.Context, selector values.String) values.String { + selection := el.selection.Find(selector.String()) return values.NewString(selection.Text()) } -func (nd *HTMLElement) InnerTextBySelectorAll(_ context.Context, selector values.String) *values.Array { - selection := nd.selection.Find(selector.String()) +func (el *HTMLElement) InnerTextBySelectorAll(_ context.Context, selector values.String) *values.Array { + selection := el.selection.Find(selector.String()) arr := values.NewArray(selection.Length()) selection.Each(func(_ int, selection *goquery.Selection) { @@ -257,8 +350,8 @@ func (nd *HTMLElement) InnerTextBySelectorAll(_ context.Context, selector values return arr } -func (nd *HTMLElement) CountBySelector(_ context.Context, selector values.String) values.Int { - selection := nd.selection.Find(selector.String()) +func (el *HTMLElement) CountBySelector(_ context.Context, selector values.String) values.Int { + selection := el.selection.Find(selector.String()) if selection == nil { return values.ZeroInt @@ -267,8 +360,8 @@ func (nd *HTMLElement) CountBySelector(_ context.Context, selector values.String return values.NewInt(selection.Size()) } -func (nd *HTMLElement) ExistsBySelector(_ context.Context, selector values.String) values.Boolean { - selection := nd.selection.Closest(selector.String()) +func (el *HTMLElement) ExistsBySelector(_ context.Context, selector values.String) values.Boolean { + selection := el.selection.Closest(selector.String()) if selection == nil { return values.False @@ -277,47 +370,83 @@ func (nd *HTMLElement) ExistsBySelector(_ context.Context, selector values.Strin return values.True } -func (nd *HTMLElement) GetIn(ctx context.Context, path []core.Value) (core.Value, error) { - return common.GetInElement(ctx, nd, path) +func (el *HTMLElement) GetIn(ctx context.Context, path []core.Value) (core.Value, error) { + return common.GetInElement(ctx, el, path) } -func (nd *HTMLElement) SetIn(ctx context.Context, path []core.Value, value core.Value) error { - return common.SetInElement(ctx, nd, path, value) +func (el *HTMLElement) SetIn(ctx context.Context, path []core.Value, value core.Value) error { + return common.SetInElement(ctx, el, path, value) } -func (nd *HTMLElement) Iterate(_ context.Context) (core.Iterator, error) { - return common.NewIterator(nd) +func (el *HTMLElement) Iterate(_ context.Context) (core.Iterator, error) { + return common.NewIterator(el) } -func (nd *HTMLElement) Click(_ context.Context) (values.Boolean, error) { +func (el *HTMLElement) Click(_ context.Context) (values.Boolean, error) { return false, core.ErrNotSupported } -func (nd *HTMLElement) Input(_ context.Context, _ core.Value, _ values.Int) error { +func (el *HTMLElement) Input(_ context.Context, _ core.Value, _ values.Int) error { return core.ErrNotSupported } -func (nd *HTMLElement) Select(_ context.Context, _ *values.Array) (*values.Array, error) { +func (el *HTMLElement) Select(_ context.Context, _ *values.Array) (*values.Array, error) { return nil, core.ErrNotSupported } -func (nd *HTMLElement) ScrollIntoView(_ context.Context) error { +func (el *HTMLElement) ScrollIntoView(_ context.Context) error { return core.ErrNotSupported } -func (nd *HTMLElement) Hover(_ context.Context) error { +func (el *HTMLElement) Hover(_ context.Context) error { return core.ErrNotSupported } -func (nd *HTMLElement) WaitForClass(_ context.Context, _ values.String, _ drivers.WaitEvent) error { +func (el *HTMLElement) WaitForClass(_ context.Context, _ values.String, _ drivers.WaitEvent) error { return core.ErrNotSupported } -func (nd *HTMLElement) parseAttrs() *values.Object { +func (el *HTMLElement) ensureStyles(ctx context.Context) error { + if el.styles == nil { + styles, err := el.parseStyles(ctx) + + if err != nil { + return err + } + + el.styles = styles + } + + return nil +} + +func (el *HTMLElement) parseStyles(ctx context.Context) (*values.Object, error) { + str := el.GetAttribute(ctx, "style") + + if str == values.None { + return values.NewObject(), nil + } + + styles, err := common.DeserializeStyles(values.NewString(str.String())) + + if err != nil { + return nil, err + } + + return styles, nil +} + +func (el *HTMLElement) ensureAttrs() { + if el.attrs == nil { + el.attrs = el.parseAttrs() + } +} + +func (el *HTMLElement) parseAttrs() *values.Object { obj := values.NewObject() for _, name := range common.Attributes { - val, ok := nd.selection.Attr(name) + val, ok := el.selection.Attr(name) if ok { obj.Set(values.NewString(name), values.NewString(val)) @@ -327,8 +456,8 @@ func (nd *HTMLElement) parseAttrs() *values.Object { return obj } -func (nd *HTMLElement) parseChildren() *values.Array { - children := nd.selection.Children() +func (el *HTMLElement) parseChildren() *values.Array { + children := el.selection.Children() arr := values.NewArray(10) diff --git a/pkg/drivers/value.go b/pkg/drivers/value.go index 3bd91192..7db51354 100644 --- a/pkg/drivers/value.go +++ b/pkg/drivers/value.go @@ -53,12 +53,26 @@ type ( SetValue(ctx context.Context, value core.Value) error + GetStyles(ctx context.Context) (*values.Object, error) + + GetStyle(ctx context.Context, name values.String) (core.Value, error) + + SetStyles(ctx context.Context, values *values.Object) error + + SetStyle(ctx context.Context, name values.String, value core.Value) error + + RemoveStyle(ctx context.Context, name ...values.String) error + GetAttributes(ctx context.Context) *values.Object GetAttribute(ctx context.Context, name values.String) core.Value + SetAttributes(ctx context.Context, values *values.Object) error + SetAttribute(ctx context.Context, name, value values.String) error + RemoveAttribute(ctx context.Context, name ...values.String) error + InnerHTMLBySelector(ctx context.Context, selector values.String) values.String InnerHTMLBySelectorAll(ctx context.Context, selector values.String) *values.Array From 5f4ccc4377e7bfe7ec5d2bfdab475a720b0e228d Mon Sep 17 00:00:00 2001 From: Tim Voronov Date: Tue, 12 Mar 2019 21:16:55 -0400 Subject: [PATCH 2/6] Added stdlib function. --- pkg/drivers/http/element.go | 2 +- pkg/stdlib/arrays/lib.go | 1 - pkg/stdlib/arrays/reverse.go | 36 ------------- pkg/stdlib/collections/lib.go | 3 +- pkg/stdlib/collections/reverse.go | 51 +++++++++++++++++++ .../{arrays => collections}/reverse_test.go | 29 +++++++++-- pkg/stdlib/html/attr_get.go | 40 +++++++++++++++ pkg/stdlib/html/attr_remove.go | 41 +++++++++++++++ pkg/stdlib/html/attr_set.go | 50 ++++++++++++++++++ pkg/stdlib/html/lib.go | 3 ++ pkg/stdlib/lib.go | 5 ++ pkg/stdlib/strings/lib.go | 1 - pkg/stdlib/strings/reverse.go | 30 ----------- pkg/stdlib/strings/reverse_test.go | 29 ----------- 14 files changed, 217 insertions(+), 104 deletions(-) delete mode 100644 pkg/stdlib/arrays/reverse.go create mode 100644 pkg/stdlib/collections/reverse.go rename pkg/stdlib/{arrays => collections}/reverse_test.go (54%) create mode 100644 pkg/stdlib/html/attr_get.go create mode 100644 pkg/stdlib/html/attr_remove.go create mode 100644 pkg/stdlib/html/attr_set.go delete mode 100644 pkg/stdlib/strings/reverse.go delete mode 100644 pkg/stdlib/strings/reverse_test.go diff --git a/pkg/drivers/http/element.go b/pkg/drivers/http/element.go index 7daa7997..1469e525 100644 --- a/pkg/drivers/http/element.go +++ b/pkg/drivers/http/element.go @@ -241,7 +241,7 @@ func (el *HTMLElement) SetAttribute(_ context.Context, name, value values.String return nil } -func (el *HTMLElement) RemoveAttribute(ctx context.Context, name ...values.String) error { +func (el *HTMLElement) RemoveAttribute(_ context.Context, name ...values.String) error { el.ensureAttrs() for _, attr := range name { diff --git a/pkg/stdlib/arrays/lib.go b/pkg/stdlib/arrays/lib.go index 4242940f..959749c0 100644 --- a/pkg/stdlib/arrays/lib.go +++ b/pkg/stdlib/arrays/lib.go @@ -21,7 +21,6 @@ func NewLib() map[string]core.Function { "REMOVE_NTH": RemoveNth, "REMOVE_VALUE": RemoveValue, "REMOVE_VALUES": RemoveValues, - "REVERSE": Reverse, "SHIFT": Shift, "SLICE": Slice, "SORTED": Sorted, diff --git a/pkg/stdlib/arrays/reverse.go b/pkg/stdlib/arrays/reverse.go deleted file mode 100644 index db078cf2..00000000 --- a/pkg/stdlib/arrays/reverse.go +++ /dev/null @@ -1,36 +0,0 @@ -package arrays - -import ( - "context" - - "github.com/MontFerret/ferret/pkg/runtime/core" - "github.com/MontFerret/ferret/pkg/runtime/values" - "github.com/MontFerret/ferret/pkg/runtime/values/types" -) - -// Reverse return a new array with its elements reversed. -// @param array (Array) - Target array. -// @returns (Array) - A new array with its elements reversed. -func Reverse(_ context.Context, args ...core.Value) (core.Value, error) { - err := core.ValidateArgs(args, 1, 1) - - if err != nil { - return values.None, err - } - - err = core.ValidateType(args[0], types.Array) - - if err != nil { - return values.None, err - } - - arr := args[0].(*values.Array) - size := int(arr.Length()) - result := values.NewArray(size) - - for i := size - 1; i >= 0; i-- { - result.Push(arr.Get(values.NewInt(i))) - } - - return result, nil -} diff --git a/pkg/stdlib/collections/lib.go b/pkg/stdlib/collections/lib.go index c9a57c62..857ac43c 100644 --- a/pkg/stdlib/collections/lib.go +++ b/pkg/stdlib/collections/lib.go @@ -4,6 +4,7 @@ import "github.com/MontFerret/ferret/pkg/runtime/core" func NewLib() map[string]core.Function { return map[string]core.Function{ - "LENGTH": Length, + "LENGTH": Length, + "REVERSE": Reverse, } } diff --git a/pkg/stdlib/collections/reverse.go b/pkg/stdlib/collections/reverse.go new file mode 100644 index 00000000..2a4f9c7d --- /dev/null +++ b/pkg/stdlib/collections/reverse.go @@ -0,0 +1,51 @@ +package collections + +import ( + "context" + "github.com/MontFerret/ferret/pkg/runtime/values/types" + + "github.com/MontFerret/ferret/pkg/runtime/core" + "github.com/MontFerret/ferret/pkg/runtime/values" +) + +// Reverse returns the reverse of a given string or array value. +// @param text (String|Array) - The string or array to reverse. +// @returns (String|Array) - Returns a reversed version of a given value. +func Reverse(_ context.Context, args ...core.Value) (core.Value, error) { + err := core.ValidateArgs(args, 1, 1) + + if err != nil { + return values.EmptyString, err + } + + err = core.ValidateType(args[0], types.Array, types.String) + + if err != nil { + return values.None, err + } + + switch col := args[0].(type) { + case values.String: + runes := []rune(col) + size := len(runes) + + // Reverse + for i := 0; i < size/2; i++ { + runes[i], runes[size-1-i] = runes[size-1-i], runes[i] + } + + return values.NewString(string(runes)), nil + case *values.Array: + size := int(col.Length()) + result := values.NewArray(size) + + for i := size - 1; i >= 0; i-- { + result.Push(col.Get(values.NewInt(i))) + } + + return result, nil + + default: + return values.None, nil + } +} diff --git a/pkg/stdlib/arrays/reverse_test.go b/pkg/stdlib/collections/reverse_test.go similarity index 54% rename from pkg/stdlib/arrays/reverse_test.go rename to pkg/stdlib/collections/reverse_test.go index 2fbdf1b4..50b72f5a 100644 --- a/pkg/stdlib/arrays/reverse_test.go +++ b/pkg/stdlib/collections/reverse_test.go @@ -1,14 +1,33 @@ -package arrays_test +package collections_test import ( "context" + "testing" + "github.com/MontFerret/ferret/pkg/runtime/values" - "github.com/MontFerret/ferret/pkg/stdlib/arrays" + "github.com/MontFerret/ferret/pkg/stdlib/collections" . "github.com/smartystreets/goconvey/convey" - "testing" ) func TestReverse(t *testing.T) { + Convey("When args are not passed", t, func() { + Convey("It should return an error", func() { + var err error + _, err = collections.Reverse(context.Background()) + + So(err, ShouldBeError) + }) + }) + + Convey("Should reverse a text with right encoding", t, func() { + out, _ := collections.Reverse( + context.Background(), + values.NewString("The quick brown 狐 jumped over the lazy 犬"), + ) + + So(out, ShouldEqual, "犬 yzal eht revo depmuj 狐 nworb kciuq ehT") + }) + Convey("Should return a copy of an array with reversed elements", t, func() { arr := values.NewArrayWith( values.NewInt(1), @@ -19,7 +38,7 @@ func TestReverse(t *testing.T) { values.NewInt(6), ) - out, err := arrays.Reverse( + out, err := collections.Reverse( context.Background(), arr, ) @@ -31,7 +50,7 @@ func TestReverse(t *testing.T) { Convey("Should return an empty array when there no elements in a source one", t, func() { arr := values.NewArray(0) - out, err := arrays.Reverse( + out, err := collections.Reverse( context.Background(), arr, ) diff --git a/pkg/stdlib/html/attr_get.go b/pkg/stdlib/html/attr_get.go new file mode 100644 index 00000000..2619b19d --- /dev/null +++ b/pkg/stdlib/html/attr_get.go @@ -0,0 +1,40 @@ +package html + +import ( + "context" + + "github.com/MontFerret/ferret/pkg/runtime/core" + "github.com/MontFerret/ferret/pkg/runtime/values" +) + +// AttributeGet gets single or more attribute(s) of a given element. +// @param el (HTMLElement) - Target element. +// @param names (...String) - Attribute name(s). +// @returns Object - Key-value pairs of attribute values. +func AttributeGet(ctx context.Context, args ...core.Value) (core.Value, error) { + err := core.ValidateArgs(args, 2, core.MaxArgs) + + if err != nil { + return values.None, err + } + + el, err := resolveElement(args[0]) + + if err != nil { + return values.None, err + } + + names := args[1:] + result := values.NewObject() + attrs := el.GetAttributes(ctx) + + for _, n := range names { + val, exists := attrs.Get(values.NewString(n.String())) + + if exists { + result.Set(values.NewString(n.String()), val) + } + } + + return result, nil +} diff --git a/pkg/stdlib/html/attr_remove.go b/pkg/stdlib/html/attr_remove.go new file mode 100644 index 00000000..0e37dc3f --- /dev/null +++ b/pkg/stdlib/html/attr_remove.go @@ -0,0 +1,41 @@ +package html + +import ( + "context" + + "github.com/MontFerret/ferret/pkg/runtime/core" + "github.com/MontFerret/ferret/pkg/runtime/values" + "github.com/MontFerret/ferret/pkg/runtime/values/types" +) + +// AttributeRemove removes single or more attribute(s) of a given element. +// @param el (HTMLElement) - Target element. +// @param names (...String) - Attribute name(s). +func AttributeRemove(ctx context.Context, args ...core.Value) (core.Value, error) { + err := core.ValidateArgs(args, 2, core.MaxArgs) + + if err != nil { + return values.None, err + } + + el, err := resolveElement(args[0]) + + if err != nil { + return values.None, err + } + + attrs := args[1:] + attrsStr := make([]values.String, 0, len(attrs)) + + for _, attr := range attrs { + str, ok := attr.(values.String) + + if !ok { + return values.None, core.TypeError(attr.Type(), types.String) + } + + attrsStr = append(attrsStr, str) + } + + return values.None, el.RemoveAttribute(ctx, attrsStr...) +} diff --git a/pkg/stdlib/html/attr_set.go b/pkg/stdlib/html/attr_set.go new file mode 100644 index 00000000..3df3fef4 --- /dev/null +++ b/pkg/stdlib/html/attr_set.go @@ -0,0 +1,50 @@ +package html + +import ( + "context" + + "github.com/MontFerret/ferret/pkg/runtime/core" + "github.com/MontFerret/ferret/pkg/runtime/values" + "github.com/MontFerret/ferret/pkg/runtime/values/types" +) + +// AttributeSet sets or updates a single or more attribute(s) of a given element. +// @param el (HTMLElement) - Target element. +// @param nameOrObj (String | Object) - Attribute name or an object representing a key-value pair of attributes. +// @param value (String) - If a second parameter is a string value, this parameter represent an attribute value. +func AttributeSet(ctx context.Context, args ...core.Value) (core.Value, error) { + err := core.ValidateArgs(args, 2, core.MaxArgs) + + if err != nil { + return values.None, err + } + + el, err := resolveElement(args[0]) + + if err != nil { + return values.None, err + } + + switch arg1 := args[1].(type) { + case values.String: + // ATTR_SET(el, name, value) + err = core.ValidateArgs(args, 3, 3) + + if err != nil { + return values.None, nil + } + + arg2, ok := args[2].(values.String) + + if !ok { + return values.None, core.TypeError(arg1.Type(), types.String, types.Object) + } + + return values.None, el.SetAttribute(ctx, arg1, arg2) + case *values.Object: + // ATTR_SET(el, values) + return values.None, el.SetAttributes(ctx, arg1) + default: + return values.None, core.TypeError(arg1.Type(), types.String, types.Object) + } +} diff --git a/pkg/stdlib/html/lib.go b/pkg/stdlib/html/lib.go index 2a936dd0..293ec295 100644 --- a/pkg/stdlib/html/lib.go +++ b/pkg/stdlib/html/lib.go @@ -14,6 +14,9 @@ const defaultTimeout = 5000 func NewLib() map[string]core.Function { return map[string]core.Function{ + "ATTR_GET": AttributeGet, + "ATTR_REMOVE": AttributeRemove, + "ATTR_SET": AttributeSet, "CLICK": Click, "CLICK_ALL": ClickAll, "DOCUMENT": Document, diff --git a/pkg/stdlib/lib.go b/pkg/stdlib/lib.go index 3555836d..851c50de 100644 --- a/pkg/stdlib/lib.go +++ b/pkg/stdlib/lib.go @@ -1,6 +1,7 @@ package stdlib import ( + "fmt" "github.com/MontFerret/ferret/pkg/runtime/core" "github.com/MontFerret/ferret/pkg/stdlib/arrays" "github.com/MontFerret/ferret/pkg/stdlib/collections" @@ -17,6 +18,10 @@ func NewLib() map[string]core.Function { add := func(l map[string]core.Function) { for name, fn := range l { + if _, exists := lib[name]; exists { + panic(fmt.Sprintf("%s function already exists", name)) + } + lib[name] = fn } } diff --git a/pkg/stdlib/strings/lib.go b/pkg/stdlib/strings/lib.go index b43a881b..8761e8e1 100644 --- a/pkg/stdlib/strings/lib.go +++ b/pkg/stdlib/strings/lib.go @@ -22,7 +22,6 @@ func NewLib() map[string]core.Function { "REGEXP_SPLIT": RegexSplit, "REGEXP_TEST": RegexTest, "REGEXP_REPLACE": RegexReplace, - "REVERSE": Reverse, "RIGHT": Right, "RTRIM": RTrim, "SHA1": Sha1, diff --git a/pkg/stdlib/strings/reverse.go b/pkg/stdlib/strings/reverse.go deleted file mode 100644 index 78509bc7..00000000 --- a/pkg/stdlib/strings/reverse.go +++ /dev/null @@ -1,30 +0,0 @@ -package strings - -import ( - "context" - - "github.com/MontFerret/ferret/pkg/runtime/core" - "github.com/MontFerret/ferret/pkg/runtime/values" -) - -// Reverse returns the reverse of the string value. -// @param text (String) - The string to revers -// @returns (String) - Returns a reversed version of the string. -func Reverse(_ context.Context, args ...core.Value) (core.Value, error) { - err := core.ValidateArgs(args, 1, 1) - - if err != nil { - return values.EmptyString, err - } - - text := args[0].String() - runes := []rune(text) - size := len(runes) - - // Reverse - for i := 0; i < size/2; i++ { - runes[i], runes[size-1-i] = runes[size-1-i], runes[i] - } - - return values.NewString(string(runes)), nil -} diff --git a/pkg/stdlib/strings/reverse_test.go b/pkg/stdlib/strings/reverse_test.go deleted file mode 100644 index ffbdd8ed..00000000 --- a/pkg/stdlib/strings/reverse_test.go +++ /dev/null @@ -1,29 +0,0 @@ -package strings_test - -import ( - "context" - "github.com/MontFerret/ferret/pkg/runtime/values" - "github.com/MontFerret/ferret/pkg/stdlib/strings" - . "github.com/smartystreets/goconvey/convey" - "testing" -) - -func TestReverse(t *testing.T) { - Convey("When args are not passed", t, func() { - Convey("It should return an error", func() { - var err error - _, err = strings.Reverse(context.Background()) - - So(err, ShouldBeError) - }) - }) - - Convey("Should reverse a text with right encoding", t, func() { - out, _ := strings.Reverse( - context.Background(), - values.NewString("The quick brown 狐 jumped over the lazy 犬"), - ) - - So(out, ShouldEqual, "犬 yzal eht revo depmuj 狐 nworb kciuq ehT") - }) -} From 18247363fe2015e0f8fe38d121503ff1a0a96a9b Mon Sep 17 00:00:00 2001 From: Tim Voronov Date: Wed, 13 Mar 2019 11:11:30 -0400 Subject: [PATCH 3/6] Added e2e tests --- Gopkg.lock | 2 +- Makefile | 2 +- .../components/pages/events/appearable.js | 37 +++++++++++++++++-- .../dynamic/components/pages/events/index.js | 20 +++++++++- e2e/tests/el_attrs_get.d.fql | 11 ++++++ e2e/tests/el_attrs_remove.d.fql | 17 +++++++++ e2e/tests/el_attrs_set.d.fql | 17 +++++++++ e2e/tests/el_attrs_set_bulk.d.fql | 15 ++++++++ pkg/drivers/cdp/element.go | 20 +++++++--- pkg/drivers/common/styles_test.go | 15 ++++++++ 10 files changed, 143 insertions(+), 13 deletions(-) create mode 100644 e2e/tests/el_attrs_get.d.fql create mode 100644 e2e/tests/el_attrs_remove.d.fql create mode 100644 e2e/tests/el_attrs_set.d.fql create mode 100644 e2e/tests/el_attrs_set_bulk.d.fql diff --git a/Gopkg.lock b/Gopkg.lock index 1f3e8b44..55cd9c96 100644 --- a/Gopkg.lock +++ b/Gopkg.lock @@ -301,11 +301,11 @@ input-imports = [ "github.com/PuerkitoBio/goquery", "github.com/antlr/antlr4/runtime/Go/antlr", - "github.com/aymerick/douceur/parser", "github.com/chzyer/readline", "github.com/corpix/uarand", "github.com/derekparker/trie", "github.com/gofrs/uuid", + "github.com/gorilla/css/scanner", "github.com/labstack/echo", "github.com/mafredri/cdp", "github.com/mafredri/cdp/devtool", diff --git a/Makefile b/Makefile index ffba0024..27f7dc61 100644 --- a/Makefile +++ b/Makefile @@ -29,7 +29,7 @@ cover: curl -s https://codecov.io/bash | bash e2e: - go run ${DIR_E2E}/main.go --tests ${DIR_E2E}/tests --pages ${DIR_E2E}/pages + go run ${DIR_E2E}/main.go --tests ${DIR_E2E}/tests --pages ${DIR_E2E}/pages --filter el_attrs_set* bench: go test -run=XXX -bench=. ${DIR_PKG}/... diff --git a/e2e/pages/dynamic/components/pages/events/appearable.js b/e2e/pages/dynamic/components/pages/events/appearable.js index 09820f0f..160fd0ed 100644 --- a/e2e/pages/dynamic/components/pages/events/appearable.js +++ b/e2e/pages/dynamic/components/pages/events/appearable.js @@ -2,23 +2,52 @@ import random from "../../../utils/random.js"; const e = React.createElement; -function render(id) { - return e("span", { id: `${id}-content` }, ["Hello world"]); +function render(id, props = {}) { + return e("span", { id: `${id}-content`, ...props }, ["Hello world"]); } export default class AppearableComponent extends React.PureComponent { constructor(props) { super(props); + let element = null; + + if (props.appear) { + if (props.useStyle) { + element = render(props.id, { style: {display: "none"}}) + } + } else { + if (props.useStyle) { + element = render(props.id, { style: {display: "block" }}) + } else { + element = render(props.id) + } + } + this.state = { - element: props.appear === true ? null : render(props.id) + element }; } handleClick() { setTimeout(() => { + const props = this.props; + let element = null; + + if (props.appear) { + if (props.useStyle) { + element = render(props.id, { style: {display: "block" }}) + } else { + element = render(props.id) + } + } else { + if (props.useStyle) { + element = render(props.id, { style: {display: "none"}}) + } + } + this.setState({ - element: this.props.appear === true ? render(this.props.id) : null + element, }) }, random()) } diff --git a/e2e/pages/dynamic/components/pages/events/index.js b/e2e/pages/dynamic/components/pages/events/index.js index 8735891f..c05dbe8a 100644 --- a/e2e/pages/dynamic/components/pages/events/index.js +++ b/e2e/pages/dynamic/components/pages/events/index.js @@ -48,13 +48,31 @@ export default class EventsPage extends React.Component { title: "Appearable" }) ]), + ]), + e("div", { className: "row" }, [ e("div", { className: "col-lg-4"}, [ e(Appearable, { id: "wait-no-element", appear: false, title: "Disappearable" }) - ]) + ]), + e("div", { className: "col-lg-4"}, [ + e(Appearable, { + id: "wait-style", + appear: true, + title: "Appearable with style", + useStyle: true, + }) + ]), + e("div", { className: "col-lg-4"}, [ + e(Appearable, { + id: "wait-no-style", + appear: false, + title: "Disappearable", + useStyle: true, + }) + ]), ]) ]) } diff --git a/e2e/tests/el_attrs_get.d.fql b/e2e/tests/el_attrs_get.d.fql new file mode 100644 index 00000000..cc04d98a --- /dev/null +++ b/e2e/tests/el_attrs_get.d.fql @@ -0,0 +1,11 @@ +LET url = @dynamic + "?redirect=/events" +LET doc = DOCUMENT(url, true) +LET pageSelector = "#page-events" +LET elemSelector = "#wait-no-style-content" + +WAIT_ELEMENT(doc, pageSelector) + +LET el = ELEMENT(doc, elemSelector) +LET attrs = ATTR_GET(el, "style") + +RETURN EXPECT("display: block;", attrs.style) \ No newline at end of file diff --git a/e2e/tests/el_attrs_remove.d.fql b/e2e/tests/el_attrs_remove.d.fql new file mode 100644 index 00000000..30b905d4 --- /dev/null +++ b/e2e/tests/el_attrs_remove.d.fql @@ -0,0 +1,17 @@ +LET url = @dynamic + "?redirect=/events" +LET doc = DOCUMENT(url, true) +LET pageSelector = "#page-events" +LET elemSelector = "#wait-no-style-content" + +WAIT_ELEMENT(doc, pageSelector) +LET el = ELEMENT(doc, elemSelector) + +LET prev = el.attributes.style + +ATTR_REMOVE(el, "style") + +WAIT(1000) + +LET curr = el.attributes.style + +RETURN prev == "display: block;" && curr == NONE ? "" : "expected attribute style be removed" \ No newline at end of file diff --git a/e2e/tests/el_attrs_set.d.fql b/e2e/tests/el_attrs_set.d.fql new file mode 100644 index 00000000..766f1cdf --- /dev/null +++ b/e2e/tests/el_attrs_set.d.fql @@ -0,0 +1,17 @@ +LET url = @dynamic + "?redirect=/events" +LET doc = DOCUMENT(url, true) +LET pageSelector = "#page-events" +LET elemSelector = "#wait-no-style-content" + +WAIT_ELEMENT(doc, pageSelector) + +LET el = ELEMENT(doc, elemSelector) +LET prev = el.style + +ATTR_SET(el, "style", "color: black;") + +WAIT(1000) + +LET curr = el.style + +RETURN curr.color == "black" ? "" : "styles should be updated" \ No newline at end of file diff --git a/e2e/tests/el_attrs_set_bulk.d.fql b/e2e/tests/el_attrs_set_bulk.d.fql new file mode 100644 index 00000000..6b532a93 --- /dev/null +++ b/e2e/tests/el_attrs_set_bulk.d.fql @@ -0,0 +1,15 @@ +LET url = @dynamic + "?redirect=/events" +LET doc = DOCUMENT(url, true) +LET pageSelector = "#page-events" +LET elemSelector = "#wait-no-style-content" + +WAIT_ELEMENT(doc, pageSelector) + +LET el = ELEMENT(doc, elemSelector) +LET prev = el.style + +ATTR_SET(el, { style: "color: black;", "data-ferret-x": "test" }) + +WAIT(1000) + +RETURN el.style.color == "black" && el.attributes["data-ferret-x"] == "test" ? "" : "styles should be updated" \ No newline at end of file diff --git a/pkg/drivers/cdp/element.go b/pkg/drivers/cdp/element.go index c544d803..fd299076 100644 --- a/pkg/drivers/cdp/element.go +++ b/pkg/drivers/cdp/element.go @@ -47,7 +47,6 @@ type ( innerHTML values.String innerText *common.LazyValue value core.Value - rawAttrs []string attributes *common.LazyValue style *common.LazyValue children []*HTMLElementIdentity @@ -129,7 +128,6 @@ func LoadElement( id, node.Node.NodeType, node.Node.NodeName, - node.Node.Attributes, val, innerHTML, createChildrenArray(node.Node.Children), @@ -143,7 +141,6 @@ func NewHTMLElement( id *HTMLElementIdentity, nodeType int, nodeName string, - attributes []string, value string, innerHTML values.String, children []*HTMLElementIdentity, @@ -158,7 +155,6 @@ func NewHTMLElement( el.nodeName = values.NewString(nodeName) el.innerHTML = innerHTML el.innerText = common.NewLazyValue(el.loadInnerText) - el.rawAttrs = attributes el.attributes = common.NewLazyValue(el.loadAttrs) el.style = common.NewLazyValue(el.parseStyle) el.value = values.EmptyString @@ -1062,8 +1058,14 @@ func (el *HTMLElement) loadInnerText(ctx context.Context) (core.Value, error) { return parsed, nil } -func (el *HTMLElement) loadAttrs(_ context.Context) (core.Value, error) { - return parseAttrs(el.rawAttrs), nil +func (el *HTMLElement) loadAttrs(ctx context.Context) (core.Value, error) { + repl, err := el.client.DOM.GetAttributes(ctx, dom.NewGetAttributesArgs(el.id.nodeID)) + + if err != nil { + return values.None, err + } + + return parseAttrs(repl.Attributes), nil } func (el *HTMLElement) loadChildren(ctx context.Context) (core.Value, error) { @@ -1126,6 +1128,12 @@ func (el *HTMLElement) handleAttrModified(ctx context.Context, message interface return } + // they are not event loaded + // just ignore the event + if !el.attributes.Ready() { + return + } + el.attributes.Write(ctx, func(v core.Value, err error) { if err != nil { el.logError(err).Msg("failed to update element") diff --git a/pkg/drivers/common/styles_test.go b/pkg/drivers/common/styles_test.go index 3ed02a7b..a994ce0a 100644 --- a/pkg/drivers/common/styles_test.go +++ b/pkg/drivers/common/styles_test.go @@ -44,6 +44,21 @@ func TestDeserializeStyles(t *testing.T) { name: "font-family", value: values.NewString(`Arial,"Helvetica Neue",Helvetica,sans-serif`), }, + { + raw: "color: black", + name: "color", + value: values.NewString("black"), + }, + { + raw: "display: inline-block", + name: "display", + value: values.NewString("inline-block"), + }, + { + raw: "min-width: 50", + name: "min-width", + value: values.NewFloat(50), + }, } Convey("Should parse a single style", func() { From 5b31c72939b4fc924aada0213f28f948e6ac6145 Mon Sep 17 00:00:00 2001 From: Tim Voronov Date: Wed, 13 Mar 2019 11:30:39 -0400 Subject: [PATCH 4/6] Added e2e tests for STYLE_* functions --- Makefile | 2 +- e2e/tests/el_attrs_remove.d.fql | 2 +- e2e/tests/el_attrs_set_bulk.d.fql | 1 - e2e/tests/el_style_get.d.fql | 11 ++++++++ e2e/tests/el_style_remove.d.fql | 17 ++++++++++++ e2e/tests/el_style_set.d.fql | 17 ++++++++++++ e2e/tests/el_style_set_bulk.d.fql | 17 ++++++++++++ pkg/stdlib/collections/reverse.go | 2 +- pkg/stdlib/html/attr_get.go | 5 ++-- pkg/stdlib/html/lib.go | 3 +++ pkg/stdlib/html/style_get.go | 44 +++++++++++++++++++++++++++++++ pkg/stdlib/html/style_remove.go | 41 ++++++++++++++++++++++++++++ pkg/stdlib/html/style_set.go | 44 +++++++++++++++++++++++++++++++ 13 files changed, 200 insertions(+), 6 deletions(-) create mode 100644 e2e/tests/el_style_get.d.fql create mode 100644 e2e/tests/el_style_remove.d.fql create mode 100644 e2e/tests/el_style_set.d.fql create mode 100644 e2e/tests/el_style_set_bulk.d.fql create mode 100644 pkg/stdlib/html/style_get.go create mode 100644 pkg/stdlib/html/style_remove.go create mode 100644 pkg/stdlib/html/style_set.go diff --git a/Makefile b/Makefile index 27f7dc61..ffba0024 100644 --- a/Makefile +++ b/Makefile @@ -29,7 +29,7 @@ cover: curl -s https://codecov.io/bash | bash e2e: - go run ${DIR_E2E}/main.go --tests ${DIR_E2E}/tests --pages ${DIR_E2E}/pages --filter el_attrs_set* + go run ${DIR_E2E}/main.go --tests ${DIR_E2E}/tests --pages ${DIR_E2E}/pages bench: go test -run=XXX -bench=. ${DIR_PKG}/... diff --git a/e2e/tests/el_attrs_remove.d.fql b/e2e/tests/el_attrs_remove.d.fql index 30b905d4..b81e92a9 100644 --- a/e2e/tests/el_attrs_remove.d.fql +++ b/e2e/tests/el_attrs_remove.d.fql @@ -14,4 +14,4 @@ WAIT(1000) LET curr = el.attributes.style -RETURN prev == "display: block;" && curr == NONE ? "" : "expected attribute style be removed" \ No newline at end of file +RETURN prev == "display: block;" && curr == NONE ? "" : "expected attribute to be removed" \ No newline at end of file diff --git a/e2e/tests/el_attrs_set_bulk.d.fql b/e2e/tests/el_attrs_set_bulk.d.fql index 6b532a93..b4cab363 100644 --- a/e2e/tests/el_attrs_set_bulk.d.fql +++ b/e2e/tests/el_attrs_set_bulk.d.fql @@ -6,7 +6,6 @@ LET elemSelector = "#wait-no-style-content" WAIT_ELEMENT(doc, pageSelector) LET el = ELEMENT(doc, elemSelector) -LET prev = el.style ATTR_SET(el, { style: "color: black;", "data-ferret-x": "test" }) diff --git a/e2e/tests/el_style_get.d.fql b/e2e/tests/el_style_get.d.fql new file mode 100644 index 00000000..77d00cbd --- /dev/null +++ b/e2e/tests/el_style_get.d.fql @@ -0,0 +1,11 @@ +LET url = @dynamic + "?redirect=/events" +LET doc = DOCUMENT(url, true) +LET pageSelector = "#page-events" +LET elemSelector = "#wait-no-style-content" + +WAIT_ELEMENT(doc, pageSelector) + +LET el = ELEMENT(doc, elemSelector) +LET val = STYLE_GET(el, "display") + +RETURN val.display == "block" ? "" : "could not get style values" \ No newline at end of file diff --git a/e2e/tests/el_style_remove.d.fql b/e2e/tests/el_style_remove.d.fql new file mode 100644 index 00000000..2ffc66af --- /dev/null +++ b/e2e/tests/el_style_remove.d.fql @@ -0,0 +1,17 @@ +LET url = @dynamic + "?redirect=/events" +LET doc = DOCUMENT(url, true) +LET pageSelector = "#page-events" +LET elemSelector = "#wait-no-style-content" + +WAIT_ELEMENT(doc, pageSelector) +LET el = ELEMENT(doc, elemSelector) + +LET prev = el.style + +STYLE_REMOVE(el, "display") + +WAIT(1000) + +LET curr = el.style + +RETURN prev.display == "block" && curr.display == NONE ? "" : "expected style to be removed" \ No newline at end of file diff --git a/e2e/tests/el_style_set.d.fql b/e2e/tests/el_style_set.d.fql new file mode 100644 index 00000000..e7770b51 --- /dev/null +++ b/e2e/tests/el_style_set.d.fql @@ -0,0 +1,17 @@ +LET url = @dynamic + "?redirect=/events" +LET doc = DOCUMENT(url, true) +LET pageSelector = "#page-events" +LET elemSelector = "#wait-no-style-content" + +WAIT_ELEMENT(doc, pageSelector) + +LET el = ELEMENT(doc, elemSelector) +LET prev = el.style + +STYLE_SET(el, "color", "black") + +WAIT(1000) + +LET curr = el.style + +RETURN curr.color == "black" ? "" : "styles should be updated" \ No newline at end of file diff --git a/e2e/tests/el_style_set_bulk.d.fql b/e2e/tests/el_style_set_bulk.d.fql new file mode 100644 index 00000000..5b876553 --- /dev/null +++ b/e2e/tests/el_style_set_bulk.d.fql @@ -0,0 +1,17 @@ +LET url = @dynamic + "?redirect=/events" +LET doc = DOCUMENT(url, true) +LET pageSelector = "#page-events" +LET elemSelector = "#wait-no-style-content" + +WAIT_ELEMENT(doc, pageSelector) + +LET el = ELEMENT(doc, elemSelector) +LET prev = el.style + +STYLE_SET(el, { color: "black", "min-width": "100px", "background-color": "#11111" }) + +WAIT(1000) + +LET curr = el.style + +RETURN curr.color == "black" && curr["min-width"] == "100px" && curr["background-color"] == "#11111" ? "" : "styles should be updated" \ No newline at end of file diff --git a/pkg/stdlib/collections/reverse.go b/pkg/stdlib/collections/reverse.go index 2a4f9c7d..a07af10f 100644 --- a/pkg/stdlib/collections/reverse.go +++ b/pkg/stdlib/collections/reverse.go @@ -26,7 +26,7 @@ func Reverse(_ context.Context, args ...core.Value) (core.Value, error) { switch col := args[0].(type) { case values.String: - runes := []rune(col) + runes := []rune(string(col)) size := len(runes) // Reverse diff --git a/pkg/stdlib/html/attr_get.go b/pkg/stdlib/html/attr_get.go index 2619b19d..519ec837 100644 --- a/pkg/stdlib/html/attr_get.go +++ b/pkg/stdlib/html/attr_get.go @@ -29,10 +29,11 @@ func AttributeGet(ctx context.Context, args ...core.Value) (core.Value, error) { attrs := el.GetAttributes(ctx) for _, n := range names { - val, exists := attrs.Get(values.NewString(n.String())) + name := values.NewString(n.String()) + val, exists := attrs.Get(name) if exists { - result.Set(values.NewString(n.String()), val) + result.Set(name, val) } } diff --git a/pkg/stdlib/html/lib.go b/pkg/stdlib/html/lib.go index 293ec295..70edd1ee 100644 --- a/pkg/stdlib/html/lib.go +++ b/pkg/stdlib/html/lib.go @@ -43,6 +43,9 @@ func NewLib() map[string]core.Function { "SCROLL_ELEMENT": ScrollInto, "SCROLL_TOP": ScrollTop, "SELECT": Select, + "STYLE_GET": StyleGet, + "STYLE_REMOVE": StyleRemove, + "STYLE_SET": StyleSet, "WAIT_ELEMENT": WaitElement, "WAIT_NO_ELEMENT": WaitNoElement, "WAIT_CLASS": WaitClass, diff --git a/pkg/stdlib/html/style_get.go b/pkg/stdlib/html/style_get.go new file mode 100644 index 00000000..1bce3df1 --- /dev/null +++ b/pkg/stdlib/html/style_get.go @@ -0,0 +1,44 @@ +package html + +import ( + "context" + + "github.com/MontFerret/ferret/pkg/runtime/core" + "github.com/MontFerret/ferret/pkg/runtime/values" +) + +// StyleGet gets single or more style attribute value(s) of a given element. +// @param el (HTMLElement) - Target element. +// @param names (...String) - Style name(s). +// @returns Object - Key-value pairs of style values. +func StyleGet(ctx context.Context, args ...core.Value) (core.Value, error) { + err := core.ValidateArgs(args, 2, core.MaxArgs) + + if err != nil { + return values.None, err + } + + el, err := resolveElement(args[0]) + + if err != nil { + return values.None, err + } + + names := args[1:] + result := values.NewObject() + + for _, n := range names { + name := values.NewString(n.String()) + val, err := el.GetStyle(ctx, name) + + if err != nil { + return values.None, err + } + + if val != values.None { + result.Set(name, val) + } + } + + return result, nil +} diff --git a/pkg/stdlib/html/style_remove.go b/pkg/stdlib/html/style_remove.go new file mode 100644 index 00000000..c6b11013 --- /dev/null +++ b/pkg/stdlib/html/style_remove.go @@ -0,0 +1,41 @@ +package html + +import ( + "context" + + "github.com/MontFerret/ferret/pkg/runtime/core" + "github.com/MontFerret/ferret/pkg/runtime/values" + "github.com/MontFerret/ferret/pkg/runtime/values/types" +) + +// StyleRemove removes single or more style attribute value(s) of a given element. +// @param el (HTMLElement) - Target element. +// @param names (...String) - Style name(s). +func StyleRemove(ctx context.Context, args ...core.Value) (core.Value, error) { + err := core.ValidateArgs(args, 2, core.MaxArgs) + + if err != nil { + return values.None, err + } + + el, err := resolveElement(args[0]) + + if err != nil { + return values.None, err + } + + attrs := args[1:] + attrsStr := make([]values.String, 0, len(attrs)) + + for _, attr := range attrs { + str, ok := attr.(values.String) + + if !ok { + return values.None, core.TypeError(attr.Type(), types.String) + } + + attrsStr = append(attrsStr, str) + } + + return values.None, el.RemoveStyle(ctx, attrsStr...) +} diff --git a/pkg/stdlib/html/style_set.go b/pkg/stdlib/html/style_set.go new file mode 100644 index 00000000..ee5af6a4 --- /dev/null +++ b/pkg/stdlib/html/style_set.go @@ -0,0 +1,44 @@ +package html + +import ( + "context" + + "github.com/MontFerret/ferret/pkg/runtime/core" + "github.com/MontFerret/ferret/pkg/runtime/values" + "github.com/MontFerret/ferret/pkg/runtime/values/types" +) + +// StyleSet sets or updates a single or more style attribute value of a given element. +// @param el (HTMLElement) - Target element. +// @param nameOrObj (String | Object) - Style name or an object representing a key-value pair of attributes. +// @param value (String) - If a second parameter is a string value, this parameter represent a style value. +func StyleSet(ctx context.Context, args ...core.Value) (core.Value, error) { + err := core.ValidateArgs(args, 2, core.MaxArgs) + + if err != nil { + return values.None, err + } + + el, err := resolveElement(args[0]) + + if err != nil { + return values.None, err + } + + switch arg1 := args[1].(type) { + case values.String: + // STYLE_SET(el, name, value) + err = core.ValidateArgs(args, 3, 3) + + if err != nil { + return values.None, nil + } + + return values.None, el.SetStyle(ctx, arg1, args[2]) + case *values.Object: + // STYLE_SET(el, values) + return values.None, el.SetStyles(ctx, arg1) + default: + return values.None, core.TypeError(arg1.Type(), types.String, types.Object) + } +} From 826fad56783c0952158e6fae33f8df7fd80dd300 Mon Sep 17 00:00:00 2001 From: Tim Voronov Date: Wed, 13 Mar 2019 11:47:11 -0400 Subject: [PATCH 5/6] Updated setter --- pkg/drivers/common/setter.go | 36 ++++++++++++++++++++++++++++++++++++ pkg/runtime/values/object.go | 33 ++++++++++++++++++++++++++++----- pkg/stdlib/objects/keys.go | 10 +++++++++- 3 files changed, 73 insertions(+), 6 deletions(-) diff --git a/pkg/drivers/common/setter.go b/pkg/drivers/common/setter.go index bd9fe073..2bc43812 100644 --- a/pkg/drivers/common/setter.go +++ b/pkg/drivers/common/setter.go @@ -54,6 +54,15 @@ func SetInElement(ctx context.Context, el drivers.HTMLElement, path []core.Value return err } + curr := el.GetAttributes(ctx) + + // remove all previous attributes + err = el.RemoveAttribute(ctx, curr.Keys()...) + + if err != nil { + return err + } + obj := value.(*values.Object) obj.ForEach(func(value core.Value, key string) bool { err = el.SetAttribute(ctx, values.NewString(key), values.NewString(value.String())) @@ -63,7 +72,34 @@ func SetInElement(ctx context.Context, el drivers.HTMLElement, path []core.Value return err case "style": + if len(path) > 1 { + attrName := path[1] + return el.SetStyle(ctx, values.NewString(attrName.String()), value) + } + + err := core.ValidateType(value, types.Object) + + if err != nil { + return err + } + + styles, err := el.GetStyles(ctx) + + if err != nil { + return err + } + + err = el.RemoveStyle(ctx, styles.Keys()...) + + obj := value.(*values.Object) + obj.ForEach(func(value core.Value, key string) bool { + err = el.SetStyle(ctx, values.NewString(key), value) + + return err == nil + }) + + return err case "value": if len(path) > 1 { return core.Error(ErrInvalidPath, PathToString(path[1:])) diff --git a/pkg/runtime/values/object.go b/pkg/runtime/values/object.go index dae45350..94111c12 100644 --- a/pkg/runtime/values/object.go +++ b/pkg/runtime/values/object.go @@ -80,10 +80,23 @@ func (t *Object) Compare(other core.Value) int64 { var res int64 - sortedT := sort.StringSlice(t.Keys()) + tKeys := make([]string, 0, len(t.value)) + + for k := range t.value { + tKeys = append(tKeys, k) + } + + sortedT := sort.StringSlice(tKeys) sortedT.Sort() - sortedOther := sort.StringSlice(other.Keys()) + otherKeys := make([]string, 0, other.Length()) + + other.ForEach(func(value core.Value, k string) bool { + tKeys = append(otherKeys, k) + return true + }) + + sortedOther := sort.StringSlice(otherKeys) sortedOther.Sort() var tVal, otherVal core.Value @@ -178,11 +191,21 @@ func (t *Object) Length() Int { return Int(len(t.value)) } -func (t *Object) Keys() []string { - keys := make([]string, 0, len(t.value)) +func (t *Object) Keys() []String { + keys := make([]String, 0, len(t.value)) for k := range t.value { - keys = append(keys, k) + keys = append(keys, NewString(k)) + } + + return keys +} + +func (t *Object) Values() []core.Value { + keys := make([]core.Value, 0, len(t.value)) + + for _, v := range t.value { + keys = append(keys, v) } return keys diff --git a/pkg/stdlib/objects/keys.go b/pkg/stdlib/objects/keys.go index b0c4f3f8..65cd1531 100644 --- a/pkg/stdlib/objects/keys.go +++ b/pkg/stdlib/objects/keys.go @@ -36,7 +36,15 @@ func Keys(_ context.Context, args ...core.Value) (core.Value, error) { needSort = bool(args[1].(values.Boolean)) } - keys := sort.StringSlice(obj.Keys()) + oKeys := make([]string, 0, obj.Length()) + + obj.ForEach(func(value core.Value, key string) bool { + oKeys = append(oKeys, key) + + return true + }) + + keys := sort.StringSlice(oKeys) keysArray := values.NewArray(len(keys)) if needSort { From 60de5d2643cc9926b9c8ad2bd2aaf8e8cf772758 Mon Sep 17 00:00:00 2001 From: Tim Voronov Date: Wed, 13 Mar 2019 12:18:32 -0400 Subject: [PATCH 6/6] Fixed issues --- pkg/runtime/collections/collection.go | 2 +- pkg/runtime/collections/keyed.go | 4 ++-- pkg/runtime/values/object.go | 2 +- pkg/stdlib/objects/keep_keys_test.go | 8 ++++---- 4 files changed, 8 insertions(+), 8 deletions(-) diff --git a/pkg/runtime/collections/collection.go b/pkg/runtime/collections/collection.go index 84e9d6b0..add9a739 100644 --- a/pkg/runtime/collections/collection.go +++ b/pkg/runtime/collections/collection.go @@ -22,7 +22,7 @@ type ( KeyedCollection interface { core.Value Measurable - Keys() []string + Keys() []values.String Get(key values.String) (core.Value, values.Boolean) Set(key values.String, value core.Value) } diff --git a/pkg/runtime/collections/keyed.go b/pkg/runtime/collections/keyed.go index cd156bb5..3284afc8 100644 --- a/pkg/runtime/collections/keyed.go +++ b/pkg/runtime/collections/keyed.go @@ -10,7 +10,7 @@ type KeyedIterator struct { valVar string keyVar string values KeyedCollection - keys []string + keys []values.String pos int } @@ -40,7 +40,7 @@ func (iterator *KeyedIterator) Next(_ context.Context, scope *core.Scope) (*core } if len(iterator.keys) > iterator.pos { - key := values.NewString(iterator.keys[iterator.pos]) + key := iterator.keys[iterator.pos] val, _ := iterator.values.Get(key) iterator.pos++ diff --git a/pkg/runtime/values/object.go b/pkg/runtime/values/object.go index 94111c12..f6d957a4 100644 --- a/pkg/runtime/values/object.go +++ b/pkg/runtime/values/object.go @@ -92,7 +92,7 @@ func (t *Object) Compare(other core.Value) int64 { otherKeys := make([]string, 0, other.Length()) other.ForEach(func(value core.Value, k string) bool { - tKeys = append(otherKeys, k) + otherKeys = append(otherKeys, k) return true }) diff --git a/pkg/stdlib/objects/keep_keys_test.go b/pkg/stdlib/objects/keep_keys_test.go index d3829ae7..4bd8a592 100644 --- a/pkg/stdlib/objects/keep_keys_test.go +++ b/pkg/stdlib/objects/keep_keys_test.go @@ -185,15 +185,15 @@ func isEqualObjects(obj1 *values.Object, obj2 *values.Object) bool { var val2 core.Value for _, key := range obj1.Keys() { - val1, _ = obj1.Get(values.NewString(key)) - val2, _ = obj2.Get(values.NewString(key)) + val1, _ = obj1.Get(key) + val2, _ = obj2.Get(key) if val1.Compare(val2) != 0 { return false } } for _, key := range obj2.Keys() { - val1, _ = obj1.Get(values.NewString(key)) - val2, _ = obj2.Get(values.NewString(key)) + val1, _ = obj1.Get(key) + val2, _ = obj2.Get(key) if val2.Compare(val1) != 0 { return false }