diff --git a/array.go b/array.go index c1314d2f9..a99faa97f 100644 --- a/array.go +++ b/array.go @@ -150,6 +150,20 @@ func (a *Array) Length() *Number { return newNumber(opChain, float64(len(a.value))) } +// rawIndex returns array index for given index. Supports negative index. issue #461 +// If index is out of array bounds, remain unchanged +// index must be between [-len(a.value), len(a.value)-1] +func (a *Array) rawIndex(index int) int { + maxIdx := len(a.value) + // support negative index + if index < 0 { + // negative index, calculate actual index + index += maxIdx + } + + return index +} + // Value returns a new Value instance with array element for given index. // // If index is out of array bounds, Value reports failure and returns empty @@ -168,12 +182,15 @@ func (a *Array) Value(index int) *Value { return newValue(opChain, nil) } + srcIndex := index + index = a.rawIndex(index) + if index < 0 || index >= len(a.value) { opChain.fail(AssertionFailure{ Type: AssertInRange, - Actual: &AssertionValue{index}, + Actual: &AssertionValue{srcIndex}, Expected: &AssertionValue{AssertionRange{ - Min: 0, + Min: -len(a.value), Max: len(a.value) - 1, }}, Errors: []error{ @@ -208,12 +225,15 @@ func (a *Array) HasValue(index int, value interface{}) *Array { return a } + srcIndex := index + index = a.rawIndex(index) + if index < 0 || index >= len(a.value) { opChain.fail(AssertionFailure{ Type: AssertInRange, - Actual: &AssertionValue{index}, + Actual: &AssertionValue{srcIndex}, Expected: &AssertionValue{AssertionRange{ - Min: 0, + Min: -len(a.value), Max: len(a.value) - 1, }}, Errors: []error{ @@ -262,12 +282,14 @@ func (a *Array) NotHasValue(index int, value interface{}) *Array { return a } + srcIndex := index + index = a.rawIndex(index) if index < 0 || index >= len(a.value) { opChain.fail(AssertionFailure{ Type: AssertInRange, - Actual: &AssertionValue{index}, + Actual: &AssertionValue{srcIndex}, Expected: &AssertionValue{AssertionRange{ - Min: 0, + Min: -len(a.value), Max: len(a.value) - 1, }}, Errors: []error{ diff --git a/array_test.go b/array_test.go index cad656e95..9ca54cc15 100644 --- a/array_test.go +++ b/array_test.go @@ -2568,3 +2568,60 @@ func TestArray_ComparatorErrors(t *testing.T) { chain.assert(t, success) }) } + +func TestArray_NegativeIndex(t *testing.T) { + waitTestArr := []interface{}{"1", "2", "3", "4", "5", "6"} + testArgs := []struct { + index int + value string + }{{-1, "6"}, {-2, "5"}, {-3, "4"}, {-4, "3"}, {-5, "2"}, {-6, "1"}} + + newTestArr := func() *Array { + reporter := newMockReporter(t) + return NewArray(reporter, waitTestArr) + } + + t.Run("negative index Value", func(t *testing.T) { + + for _, arg := range testArgs { + value := newTestArr().Value(arg.index) + value.chain.assert(t, success) + assert.Equal(t, value.Raw(), arg.value) + } + + failedArgs := []int{-7, -9999999} + for _, arg := range failedArgs { + value := newTestArr().Value(arg) + value.chain.assert(t, failure) + + } + + }) + + t.Run("negative index HasValue", func(t *testing.T) { + + for _, arg := range testArgs { + newTestArr().HasValue(arg.index, arg.value). + chain.assert(t, success) + } + }) + + t.Run("negative index NotHasValue_failure", func(t *testing.T) { + + for _, arg := range testArgs { + newTestArr().NotHasValue(arg.index, arg.value). + chain.assert(t, failure) + } + + }) + + t.Run("negative index NotHasValue_success", func(t *testing.T) { + + for _, arg := range testArgs { + newTestArr().NotHasValue(arg.index, arg.value+"1"). + chain.assert(t, success) + } + + }) + +}