Repository: gavv/httpexpect
Pull request #173 (2022-11-24 18:36:21)

Add Array.ContainsAny and Array.NotContainsAny
This pull request addresses issue #157.



Patch:
diff --git a/array.go b/array.go
index 03a89f2..30c642c 100644
--- a/array.go
+++ b/array.go
@@ -532,7 +532,7 @@ func (a *Array) Elements(values ...interface{}) *Array {
 	return a
 }
 
-// NotElements is oppisite to Elements.
+// NotElements is opposite to Elements.
 //
 // Example:
 //
@@ -768,6 +768,90 @@ func (a *Array) NotContainsOnly(values ...interface{}) *Array {
 	return a
 }
 
+// ContainsAny succeeds if array contains at least one element from the given elements.
+// Before comparison, array and all elements are converted
+// to canonical form.
+//
+// Example:
+//
+//	array := NewArray(t, []interface{}{"foo", 123, 123})
+//	array.ContainsAny(123, "foo", "FOO") // success
+//	array.ContainsAny("FOO") // failure
+func (a *Array) ContainsAny(values ...interface{}) *Array {
+	a.chain.enter("ContainsAny()")
+	defer a.chain.leave()
+
+	if a.chain.failed() {
+		return a
+	}
+
+	elements, ok := canonArray(a.chain, values)
+	if !ok {
+		return a
+	}
+
+	foundAny := false
+
+	for _, expected := range elements {
+		if countElement(a.value, expected) > 0 {
+			foundAny = true
+			break
+		}
+	}
+
+	if !foundAny {
+		a.chain.fail(AssertionFailure{
+			Type:      AssertContainsElement,
+			Actual:    &AssertionValue{a.value},
+			Reference: &AssertionValue{values},
+			Errors: []error{
+				errors.New("expected:" +
+					" array contains at least one element from reference array"),
+			},
+		})
+	}
+
+	return a
+}
+
+// NotContainsAny succeeds if none of the given elements are in the array.
+//
+// Example:
+//
+//	array := NewArray(t, []interface{}{"foo", 123})
+//	array.NotContainsAny("bar", 124) // success
+//	array.NotContainsAny(123) // failure
+func (a *Array) NotContainsAny(values ...interface{}) *Array {
+	a.chain.enter("NotContainsAny()")
+	defer a.chain.leave()
+
+	if a.chain.failed() {
+		return a
+	}
+
+	elements, ok := canonArray(a.chain, values)
+	if !ok {
+		return a
+	}
+
+	for _, expected := range elements {
+		if countElement(a.value, expected) > 0 {
+			a.chain.fail(AssertionFailure{
+				Type:      AssertNotContainsElement,
+				Actual:    &AssertionValue{a.value},
+				Expected:  &AssertionValue{expected},
+				Reference: &AssertionValue{values},
+				Errors: []error{
+					errors.New("expected: array does not contain any elements from reference array"),
+				},
+			})
+			return a
+		}
+	}
+
+	return a
+}
+
 func countElement(array []interface{}, element interface{}) int {
 	count := 0
 	for _, e := range array {


Test patch:
diff --git a/array_test.go b/array_test.go
index 5db99a1..83230c6 100644
--- a/array_test.go
+++ b/array_test.go
@@ -35,6 +35,8 @@ func TestArrayFailed(t *testing.T) {
 		value.NotContains("foo")
 		value.ContainsOnly("foo")
 		value.NotContainsOnly("foo")
+		value.ContainsAny("foo")
+		value.NotContainsAny("foo")
 	}
 
 	t.Run("failed_chain", func(t *testing.T) {
@@ -564,6 +566,60 @@ func TestArrayContainsOnly(t *testing.T) {
 	})
 }
 
+func TestArrayContainsAny(t *testing.T) {
+	reporter := newMockReporter(t)
+
+	value := NewArray(reporter, []interface{}{123, "foo"})
+
+	value.ContainsAny(123)
+	value.chain.assertOK(t)
+	value.chain.reset()
+
+	value.NotContainsAny(123)
+	value.chain.assertFailed(t)
+	value.chain.reset()
+
+	value.ContainsAny("foo", 123)
+	value.chain.assertOK(t)
+	value.chain.reset()
+
+	value.NotContainsAny("foo", 123)
+	value.chain.assertFailed(t)
+	value.chain.reset()
+
+	value.ContainsAny("foo", "foo")
+	value.chain.assertOK(t)
+	value.chain.reset()
+
+	value.NotContainsAny("foo", "foo")
+	value.chain.assertFailed(t)
+	value.chain.reset()
+
+	value.ContainsAny(123, "foo", "FOO")
+	value.chain.assertOK(t)
+	value.chain.reset()
+
+	value.NotContainsAny(123, "foo", "FOO")
+	value.chain.assertFailed(t)
+	value.chain.reset()
+
+	value.ContainsAny("FOO")
+	value.chain.assertFailed(t)
+	value.chain.reset()
+
+	value.NotContainsAny("FOO")
+	value.chain.assertOK(t)
+	value.chain.reset()
+
+	value.ContainsAny([]interface{}{123, "foo"})
+	value.chain.assertFailed(t)
+	value.chain.reset()
+
+	value.NotContainsAny([]interface{}{123, "foo"})
+	value.chain.assertOK(t)
+	value.chain.reset()
+}
+
 func TestArrayConvertEqual(t *testing.T) {
 	type (
 		myArray []interface{}
@@ -644,6 +700,14 @@ func TestArrayConvertContains(t *testing.T) {
 	value.chain.assertOK(t)
 	value.chain.reset()
 
+	value.ContainsAny(myInt(123), 456.0)
+	value.chain.assertOK(t)
+	value.chain.reset()
+
+	value.NotContainsAny(myInt(123), 456.0)
+	value.chain.assertFailed(t)
+	value.chain.reset()
+
 	value.Contains("123")
 	value.chain.assertFailed(t)
 	value.chain.reset()
@@ -656,6 +720,14 @@ func TestArrayConvertContains(t *testing.T) {
 	value.chain.assertFailed(t)
 	value.chain.reset()
 
+	value.ContainsAny("123.0", "456.0")
+	value.chain.assertFailed(t)
+	value.chain.reset()
+
+	value.NotContainsAny("123.0", "456.0")
+	value.chain.assertOK(t)
+	value.chain.reset()
+
 	value.Contains(func() {})
 	value.chain.assertFailed(t)
 	value.chain.reset()
@@ -667,4 +739,12 @@ func TestArrayConvertContains(t *testing.T) {
 	value.ContainsOnly(func() {})
 	value.chain.assertFailed(t)
 	value.chain.reset()
+
+	value.ContainsAny(func() {})
+	value.chain.assertFailed(t)
+	value.chain.reset()
+
+	value.NotContainsAny(func() {})
+	value.chain.assertFailed(t)
+	value.chain.reset()
 }