Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add InList and NotInList #256

Merged
merged 38 commits into from
Feb 1, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
38 commits
Select commit Hold shift + click to select a range
531eea3
Add InList and NotInList for String
fahimbagar Jan 30, 2023
3fedfb2
Add InList and NotInList for Object
fahimbagar Jan 30, 2023
7df2ded
Add InList and NotInList for Number
fahimbagar Jan 30, 2023
c64c0cf
Add InList and NotInList for Duration
fahimbagar Jan 30, 2023
0dcf755
Add InList and NotInList for DateTime
fahimbagar Jan 30, 2023
ac54f6f
Add InList and NotInList for Boolean
fahimbagar Jan 30, 2023
ae61ab3
Add InList and NotInList for Value
fahimbagar Jan 30, 2023
31ba96d
Add InList and NotInList for Array
fahimbagar Jan 30, 2023
649ee8c
Safeguard InList and NotInList for Array
fahimbagar Jan 30, 2023
b06cca5
Fix linter issues
fahimbagar Jan 30, 2023
40c6e50
Remove single slice checker from AssertionList
fahimbagar Jan 30, 2023
1a3dd7a
Revert treating single array as param with IsEqual/NotEqual
fahimbagar Jan 30, 2023
60b72ad
Zero length for Array InList NotInList
fahimbagar Jan 31, 2023
d34acb1
Zero length for Boolean InList NotInList
fahimbagar Jan 31, 2023
bfd5e5f
Zero length for Duration InList NotInList
fahimbagar Jan 31, 2023
9734268
Zero length for DateTime InList NotInList
fahimbagar Jan 31, 2023
b885435
Zero length for Number InList NotInList
fahimbagar Jan 31, 2023
b7fc213
Zero length for Object InList NotInList
fahimbagar Jan 31, 2023
36a20f9
Zero length for String InList NotInList
fahimbagar Jan 31, 2023
f0cc4aa
Zero length for Value InList NotInList
fahimbagar Jan 31, 2023
4d9d21b
Remove unneeded comment in Array
fahimbagar Jan 31, 2023
d5f93d2
Fix comment and error message in Array
fahimbagar Feb 1, 2023
826d0cd
Early return for failed case in Array
fahimbagar Feb 1, 2023
9830403
Fix comment and error message in Boolean
fahimbagar Feb 1, 2023
a36fc2d
Early return for failed case in Boolean
fahimbagar Feb 1, 2023
2994100
Fix comment and error message in DateTime
fahimbagar Feb 1, 2023
8925811
Early return for failed case in DateTime
fahimbagar Feb 1, 2023
96ac9d5
Fix comment and error message in Duration
fahimbagar Feb 1, 2023
3533984
Early return for failed case in Duration
fahimbagar Feb 1, 2023
fe31cbf
Fix comment and error message in Number
fahimbagar Feb 1, 2023
00b9337
Early return for failed case in Number
fahimbagar Feb 1, 2023
a8d07c2
Fix comment and error message in Object
fahimbagar Feb 1, 2023
76e9645
Early return for failed case in Object
fahimbagar Feb 1, 2023
79bb6f0
Fix comment and error message in String
fahimbagar Feb 1, 2023
3f85b49
Early return for failed case in String
fahimbagar Feb 1, 2023
be043ad
Fix comment and error message in Value
fahimbagar Feb 1, 2023
16b39f8
Early return for failed case in Value
fahimbagar Feb 1, 2023
0d5cba1
Moved unexposed to end of file
fahimbagar Feb 1, 2023
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
109 changes: 109 additions & 0 deletions array.go
Original file line number Diff line number Diff line change
Expand Up @@ -746,6 +746,115 @@ func (a *Array) Equal(value interface{}) *Array {
return a.IsEqual(value)
}

// InList succeeds if whole array is equal to one of the elements from given
// list of arrays.
// Before comparison, both array and each value are converted to canonical
// form.
//
// Each value should be a slice of any type.
//
// Example:
//
// array := NewArray(t, []interface{}{"foo", 123})
// array.InList([]interface{}{"foo", 123}, []interface{}{"bar", "456"})
func (a *Array) InList(values ...interface{}) *Array {
opChain := a.chain.enter("InList()")
defer opChain.leave()

if opChain.failed() {
return a
}

if len(values) == 0 {
opChain.fail(AssertionFailure{
Type: AssertUsage,
Errors: []error{
errors.New("unexpected empty list argument"),
},
})

return a
}

var isListed bool
for _, v := range values {
expected, ok := canonArray(opChain, v)
if !ok {
return a
}

if reflect.DeepEqual(expected, a.value) {
isListed = true
break
}
}

if !isListed {
opChain.fail(AssertionFailure{
Type: AssertBelongs,
Actual: &AssertionValue{a.value},
Expected: &AssertionValue{AssertionList(values)},
Errors: []error{
errors.New("expected: arrays is equal to one of the values"),
},
})
}

return a
}

// NotInList succeeds if whole array is not equal to any of the elements
// from given list of arrays.
// Before comparison, both array and each value are converted to canonical
// form.
//
// Each value should be a slice of any type.
//
// Example:
//
// array := NewArray(t, []interface{}{"foo", 123})
// array.NotInList([]interface{}{"bar", 456}, []interface{}{"baz", "foo"})
func (a *Array) NotInList(values ...interface{}) *Array {
opChain := a.chain.enter("NotInList()")
defer opChain.leave()

if opChain.failed() {
return a
}

if len(values) == 0 {
opChain.fail(AssertionFailure{
Type: AssertUsage,
Errors: []error{
errors.New("unexpected empty list argument"),
},
})

return a
}

for _, v := range values {
expected, ok := canonArray(opChain, v)
if !ok {
return a
}

if reflect.DeepEqual(expected, a.value) {
opChain.fail(AssertionFailure{
Type: AssertNotBelongs,
Actual: &AssertionValue{a.value},
Expected: &AssertionValue{AssertionList(values)},
Errors: []error{
errors.New("expected: arrays is not equal to any of the values"),
},
})
break
}
}

return a
}

// IsEqualUnordered succeeds if array is equal to another array, ignoring element
// order. Before comparison, both arrays are converted to canonical form.
//
Expand Down
58 changes: 58 additions & 0 deletions array_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,8 @@ func TestArray_Failed(t *testing.T) {
value.NotEmpty()
value.IsEqual([]interface{}{})
value.NotEqual([]interface{}{})
value.InList([]interface{}{})
value.NotInList([]interface{}{})
value.IsEqualUnordered([]interface{}{})
value.NotEqualUnordered([]interface{}{})
value.ConsistsOf("foo")
Expand Down Expand Up @@ -377,6 +379,62 @@ func TestArray_EqualNotEmpty(t *testing.T) {
value.chain.clearFailed()
}

func TestArray_InList(t *testing.T) {
reporter := newMockReporter(t)

value := NewArray(reporter, []interface{}{"foo", "bar"})

assert.Equal(t, []interface{}{"foo", "bar"}, value.Raw())

value.InList()
value.chain.assertFailed(t)
value.chain.clearFailed()

value.NotInList()
value.chain.assertFailed(t)
value.chain.clearFailed()

value.InList("foo", "bar")
value.chain.assertFailed(t)
value.chain.clearFailed()

value.NotInList("foo", "bar")
value.chain.assertFailed(t)
value.chain.clearFailed()

value.InList([]interface{}{})
value.chain.assertFailed(t)
value.chain.clearFailed()

value.NotInList([]interface{}{})
value.chain.assertNotFailed(t)
value.chain.clearFailed()

value.InList([]interface{}{"bar", "foo"})
value.chain.assertFailed(t)
value.chain.clearFailed()

value.NotInList([]interface{}{"bar", "foo"})
value.chain.assertNotFailed(t)
value.chain.clearFailed()

value.InList([]interface{}{"bar", "foo"}, []interface{}{"foo", "bar"})
value.chain.assertNotFailed(t)
value.chain.clearFailed()

value.NotInList([]interface{}{"bar", "foo"}, []interface{}{"foo", "bar"})
value.chain.assertFailed(t)
value.chain.clearFailed()

value.InList([]interface{}{"bar", "foo"}, []interface{}{"FOO", "BAR"})
value.chain.assertFailed(t)
value.chain.clearFailed()

value.NotInList([]interface{}{"bar", "foo"}, []interface{}{"FOO", "BAR"})
value.chain.assertNotFailed(t)
value.chain.clearFailed()
}

func TestArray_EqualTypes(t *testing.T) {
reporter := newMockReporter(t)

Expand Down
24 changes: 0 additions & 24 deletions assertion_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -336,18 +336,6 @@ func TestAssertion_ValidateTraits(t *testing.T) {
List: fieldRequired,
},
},
{
testName: "AssertionList should not be slice of single element",
errorContainsText: "AssertionList",
failure: AssertionFailure{
Expected: &AssertionValue{
Value: AssertionList{[]int{1}},
},
},
traits: fieldTraits{
List: fieldRequired,
},
},
}

for _, test := range tests {
Expand Down Expand Up @@ -627,18 +615,6 @@ func TestAssertion_ValidateAssertion(t *testing.T) {
Expected: &AssertionValue{AssertionList{}},
},
},
{
testName: "List has one element and it's list",
errorContainsText: "AssertionList",
input: AssertionFailure{
Type: AssertBelongs,
Errors: []error{
errors.New("test"),
},
Actual: &AssertionValue{},
Expected: &AssertionValue{AssertionList{[]string{"test"}}},
},
},
}

for _, test := range tests {
Expand Down
7 changes: 0 additions & 7 deletions assertion_validation.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,6 @@ package httpexpect
import (
"errors"
"fmt"
"reflect"
)

func validateAssertion(failure *AssertionFailure) error {
Expand Down Expand Up @@ -182,12 +181,6 @@ func validateTraits(failure *AssertionFailure, traits fieldTraits) error {
if len(lst) == 0 {
return errors.New("AssertionList should be non-empty")
}

if len(lst) == 1 && reflect.ValueOf(lst[0]).Kind() == reflect.Slice {
return errors.New(
"AssertionList should contain a list of values," +
" but it contains a single element which itself is a list")
}
}
}
}
Expand Down
100 changes: 100 additions & 0 deletions boolean.go
Original file line number Diff line number Diff line change
Expand Up @@ -162,6 +162,97 @@ func (b *Boolean) Equal(value bool) *Boolean {
return b.IsEqual(value)
}

// InList succeeds if boolean is equal to one of the elements from given
// list of booleans.
//
// Example:
//
// boolean := NewBoolean(t, true)
// boolean.InList(true, false)
func (b *Boolean) InList(values ...bool) *Boolean {
opChain := b.chain.enter("InList()")
defer opChain.leave()

if opChain.failed() {
return b
}

if len(values) == 0 {
opChain.fail(AssertionFailure{
Type: AssertUsage,
Errors: []error{
errors.New("unexpected empty list argument"),
},
})

return b
}

var isListed bool
for _, v := range values {
if b.value == v {
isListed = true
break
}
}

if !isListed {
opChain.fail(AssertionFailure{
Type: AssertBelongs,
Actual: &AssertionValue{b.value},
Expected: &AssertionValue{AssertionList(boolList(values))},
Errors: []error{
errors.New("expected: boolean is equal to one of the values"),
},
})
}

return b
}

// NotInList succeeds if boolean is not equal to any of the elements from
// given list of booleans.
//
// Example:
//
// boolean := NewBoolean(t, true)
// boolean.NotInList(true, false)
func (b *Boolean) NotInList(values ...bool) *Boolean {
opChain := b.chain.enter("NotInList()")
defer opChain.leave()

if opChain.failed() {
return b
}

if len(values) == 0 {
opChain.fail(AssertionFailure{
Type: AssertUsage,
Errors: []error{
errors.New("unexpected empty list argument"),
},
})

return b
}

for _, v := range values {
if b.value == v {
opChain.fail(AssertionFailure{
Type: AssertNotBelongs,
Actual: &AssertionValue{b.value},
Expected: &AssertionValue{AssertionList(boolList(values))},
Errors: []error{
errors.New("expected: boolean is not equal to any of the values"),
},
})
break
}
}

return b
}

// True succeeds if boolean is true.
//
// Example:
Expand Down Expand Up @@ -217,3 +308,12 @@ func (b *Boolean) False() *Boolean {

return b
}

func boolList(values []bool) []interface{} {
l := make([]interface{}, 0, len(values))
for _, v := range values {
l = append(l, v)
}

return l
}
Loading