1
0
mirror of https://github.com/MontFerret/ferret.git synced 2025-10-30 23:37:40 +02:00

Feature/#250 wait style (#255)

* Added support for parsed styles

* Added stdlib function.

* Added e2e tests

* Added e2e tests for STYLE_* functions
This commit is contained in:
Tim Voronov
2019-03-13 14:51:30 -04:00
committed by GitHub
parent 391836cbb2
commit 376ad77404
42 changed files with 1225 additions and 217 deletions

View File

@@ -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,

View File

@@ -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
}

View File

@@ -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,
}
}

View File

@@ -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(string(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
}
}

View File

@@ -1,14 +1,33 @@
package arrays_test
package collections_test
import (
"context"
"github.com/MontFerret/ferret/pkg/runtime/values"
"github.com/MontFerret/ferret/pkg/stdlib/arrays"
. "github.com/smartystreets/goconvey/convey"
"testing"
"github.com/MontFerret/ferret/pkg/runtime/values"
"github.com/MontFerret/ferret/pkg/stdlib/collections"
. "github.com/smartystreets/goconvey/convey"
)
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,
)

View File

@@ -0,0 +1,41 @@
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 {
name := values.NewString(n.String())
val, exists := attrs.Get(name)
if exists {
result.Set(name, val)
}
}
return result, nil
}

View File

@@ -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...)
}

View File

@@ -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)
}
}

View File

@@ -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,
@@ -40,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,

View File

@@ -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
}

View File

@@ -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...)
}

View File

@@ -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)
}
}

View File

@@ -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
}
}

View File

@@ -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
}

View File

@@ -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 {

View File

@@ -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,

View File

@@ -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
}

View File

@@ -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")
})
}