Repository: gavv/httpexpect
Pull request #207 (2022-12-22 12:03:42)

Additional constructors for matcher type  (Value, Object, Array, etc)
Signed-off-by: Rohith-Raju <rohithraju488@gmail.com>
Added new constructors for the following :

1. Array
2. Boolean
3. Cookie
4. Datetime
5. Duration
6. Match
7. Number
8. Object
9. String
10. Value

Closes: #204 

coveralls: 
[![Coverage Status](https://coveralls.io/builds/55396718/badge)](https://coveralls.io/builds/55396718)

Coverage decreased (-0.4%) to 94.84% when pulling **9239cd9a881c917061aeef8eb5558d1c29b35073 on Rohith-Raju:new-constructors** into **4e927161f9445c5bd17915a2c53f53da2d5825fb on gavv:master**.

Rohith-Raju: @gavv I'll be going forward with the test cases if everything here looks fine.
gavv: Thanks, looks good.

You forgot to call `withDefaults` in some places.
gavv: I've just created #208. Please adjust formatting according to examples from that issue.
Rohith-Raju: Ok Sure !!
Rohith-Raju: @gavv For the test case, I was thinking something like this. 
```
func TestNewArrayC(t *testing.T) {
	reporter := newMockReporter(t)
	testValue := []interface{}{"foo", 123}
	arr := NewArrayC(Config{
		Reporter: reporter,
	}, testValue)
	arr.chain.assertNotFailed(t)
	arr.Equal(testValue)
}

```
Is this sufficient? Or should transfer this into a subtest?
gavv: Ideally we could add new test TestArrayConstructors with two subtests, with and without config, each one similar to what you showed above.

Same for other structs.

Patch:
diff --git a/array.go b/array.go
index 497c548..a97d58a 100644
--- a/array.go
+++ b/array.go
@@ -25,6 +25,18 @@ func NewArray(reporter Reporter, value []interface{}) *Array {
 	return newArray(newChainWithDefaults("Array()", reporter), value)
 }
 
+// NewArrayC returns a new Array instance with config
+//
+// Requirements for config are same as for WithConfig function.
+//
+// Example:
+//
+//	array := NewArrayC(config, []interface{}{"foo",123})
+func NewArrayC(config Config, value []interface{}) *Array {
+	return newArray(newChainWithConfig("Array()", config.withDefaults()), value)
+
+}
+
 func newArray(parent *chain, val []interface{}) *Array {
 	a := &Array{parent.clone(), nil}
 
diff --git a/boolean.go b/boolean.go
index cd83156..f58b02a 100644
--- a/boolean.go
+++ b/boolean.go
@@ -22,6 +22,17 @@ func NewBoolean(reporter Reporter, value bool) *Boolean {
 	return newBoolean(newChainWithDefaults("Boolean()", reporter), value)
 }
 
+// NewBooleanC returns a new Boolean instance with config
+//
+// Requirements for config are same as for WithConfig function.
+//
+// Example:
+//
+//	boolean := NewBooleanC(config, true)
+func NewBooleanC(config Config, value bool) *Boolean {
+	return newBoolean(newChainWithConfig("Boolean()", config.withDefaults()), value)
+}
+
 func newBoolean(parent *chain, val bool) *Boolean {
 	return &Boolean{parent.clone(), val}
 }
diff --git a/cookie.go b/cookie.go
index a108ffb..07178fa 100644
--- a/cookie.go
+++ b/cookie.go
@@ -26,6 +26,21 @@ func NewCookie(reporter Reporter, value *http.Cookie) *Cookie {
 	return newCookie(newChainWithDefaults("Cookie()", reporter), value)
 }
 
+// NewCookieC returns a new Cookie instance with config
+//
+// Requirements for config are same as for WithConfig function.
+//
+// Example:
+//
+//	cookie := NewCookieC(config, &http.Cookie{...})
+//
+//	cookie.Domain().Equal("example.com")
+//	cookie.Path().Equal("/")
+//	cookie.Expires().InRange(time.Now(), time.Now().Add(time.Hour * 24))
+func NewCookieC(config Config, value *http.Cookie) *Cookie {
+	return newCookie(newChainWithConfig("Cookie()", config.withDefaults()), value)
+}
+
 func newCookie(parent *chain, val *http.Cookie) *Cookie {
 	c := &Cookie{parent.clone(), nil}
 
diff --git a/datetime.go b/datetime.go
index e0e6319..7ac2c64 100644
--- a/datetime.go
+++ b/datetime.go
@@ -26,6 +26,21 @@ func NewDateTime(reporter Reporter, value time.Time) *DateTime {
 	return newDateTime(newChainWithDefaults("DateTime()", reporter), value)
 }
 
+// NewDateTimeC returns a new DateTime instance with config.
+//
+// Requirements for config are same as for WithConfig function.
+//
+// Example:
+//
+//	dt := NewDateTimeC(config, time.Now())
+//	dt.Le(time.Now())
+//
+//	time.Sleep(time.Second)
+//	dt.Lt(time.Now())
+func NewDateTimeC(config Config, value time.Time) *DateTime {
+	return newDateTime(newChainWithConfig("DateTime()", config.withDefaults()), value)
+}
+
 func newDateTime(parent *chain, val time.Time) *DateTime {
 	return &DateTime{parent.clone(), val}
 }
diff --git a/duration.go b/duration.go
index abfb5ac..30f4bbd 100644
--- a/duration.go
+++ b/duration.go
@@ -23,6 +23,18 @@ func NewDuration(reporter Reporter, value time.Duration) *Duration {
 	return newDuration(newChainWithDefaults("Duration()", reporter), &value)
 }
 
+// NewDurationC returns a new Duration instance with config.
+//
+// Requirements for config are same as for WithConfig function.
+//
+// Example:
+//
+//	d := NewDurationC(reporter, time.Second)
+//	d.Le(time.Minute)
+func NewDurationC(config Config, value time.Duration) *Duration {
+	return newDuration(newChainWithConfig("Duration()", config.withDefaults()), &value)
+}
+
 func newDuration(parent *chain, val *time.Duration) *Duration {
 	return &Duration{parent.clone(), val}
 }
diff --git a/match.go b/match.go
index 23524f0..e3fe5d5 100644
--- a/match.go
+++ b/match.go
@@ -35,9 +35,31 @@ func NewMatch(reporter Reporter, submatches []string, names []string) *Match {
 	return newMatch(newChainWithDefaults("Match()", reporter), submatches, names)
 }
 
+// NewMatchC returns a new Match instance with config.
+//
+// Requirements for config are same as for WithConfig function.
+//
+// Example:
+//
+//	s := "http://example.com/users/john"
+//	r := regexp.MustCompile(`http://(?P<host>.+)/users/(?P<user>.+)`)
+//	m := NewMatchC(reporter, r.FindStringSubmatch(s), r.SubexpNames())
+//
+//	m.NotEmpty()
+//	m.Length().Equal(3)
+//
+//	m.Index(0).Equal("http://example.com/users/john")
+//	m.Index(1).Equal("example.com")
+//	m.Index(2).Equal("john")
+//
+//	m.Name("host").Equal("example.com")
+//	m.Name("user").Equal("john")
+func NewMatchC(config Config, submatches []string, names []string) *Match {
+	return newMatch(newChainWithConfig("Match()", config.withDefaults()), submatches, names)
+}
+
 func newMatch(parent *chain, matchList []string, nameList []string) *Match {
 	m := &Match{parent.clone(), nil, nil}
-
 	if matchList != nil {
 		m.submatches = matchList
 	} else {
diff --git a/number.go b/number.go
index eb66caf..3fb7ca5 100644
--- a/number.go
+++ b/number.go
@@ -23,6 +23,17 @@ func NewNumber(reporter Reporter, value float64) *Number {
 	return newNumber(newChainWithDefaults("Number()", reporter), value)
 }
 
+// NewNumberC returns a new Number instance with config
+//
+// Requirements for config are same as for WithConfig function.
+//
+// Example:
+//
+//	number := NewNumberC(config, value)
+func NewNumberC(config Config, value float64) *Number {
+	return newNumber(newChainWithConfig("Number()", config.withDefaults()), value)
+}
+
 func newNumber(parent *chain, val float64) *Number {
 	return &Number{parent.clone(), val}
 }
diff --git a/object.go b/object.go
index 50a4778..2953776 100644
--- a/object.go
+++ b/object.go
@@ -26,6 +26,18 @@ func NewObject(reporter Reporter, value map[string]interface{}) *Object {
 	return newObject(newChainWithDefaults("Object()", reporter), value)
 }
 
+// NewObjectC returns a new Object instance with config.
+//
+// Requirements for config are same as for WithConfig function.
+//
+// Example:
+//
+//	object := NewObjectC(config, map[string]interface{}{"foo":123})
+
+func NewObjectC(config Config, value map[string]interface{}) *Object {
+	return newObject(newChainWithConfig("Object()", config.withDefaults()), value)
+}
+
 func newObject(parent *chain, val map[string]interface{}) *Object {
 	o := &Object{parent.clone(), nil}
 
diff --git a/string.go b/string.go
index f4dab35..aa68165 100644
--- a/string.go
+++ b/string.go
@@ -29,6 +29,17 @@ func NewString(reporter Reporter, value string) *String {
 	return newString(newChainWithDefaults("String()", reporter), value)
 }
 
+// NewStringC returns a new String instance with config.
+//
+// Requirements for config are same as for WithConfig function.
+//
+// Example:
+//
+//	str := NewStringC(config, value)
+func NewStringC(config Config, value string) *String {
+	return newString(newChainWithConfig("String", config.withDefaults()), value)
+}
+
 func newString(parent *chain, val string) *String {
 	return &String{parent.clone(), val}
 }
diff --git a/value.go b/value.go
index bfee01e..12dbebe 100644
--- a/value.go
+++ b/value.go
@@ -40,6 +40,33 @@ func NewValue(reporter Reporter, value interface{}) *Value {
 	return newValue(newChainWithDefaults("Value()", reporter), value)
 }
 
+// NewValueC returns a new Value instance with config.
+//
+// Requirements for config are same as for WithConfig function.
+//
+// Example:
+//
+//	value := NewValueC(t, map[string]interface{}{"foo": 123})
+//	value.Object()
+//
+//	value := NewValueC(t, []interface{}{"foo", 123})
+//	value.Array()
+//
+//	value := NewValueC(t, "foo")
+//	value.String()
+//
+//	value := NewValueC(t, 123)
+//	value.Number()
+//
+//	value := NewValueC(t, true)
+//	value.Boolean()
+//
+//	value := NewValueC(t, nil)
+//	value.Null()
+func NewValueC(config Config, value interface{}) *Value {
+	return newValue(newChainWithConfig("Value()", config.withDefaults()), value)
+}
+
 func newValue(parent *chain, val interface{}) *Value {
 	v := &Value{parent.clone(), nil}
 


Test patch:
diff --git a/array_test.go b/array_test.go
index 145fbd3..58f1e1f 100644
--- a/array_test.go
+++ b/array_test.go
@@ -122,6 +122,23 @@ func TestArrayGetters(t *testing.T) {
 	value.chain.assertNotFailed(t)
 	value.chain.clearFailed()
 }
+func TestArrayConstructors(t *testing.T) {
+	testValue := []interface{}{"Foo", 123}
+	t.Run("Constructor without config", func(t *testing.T) {
+		reporter := newMockReporter(t)
+		value := NewArray(reporter, testValue)
+		value.chain.assertNotFailed(t)
+		value.Equal(testValue)
+	})
+	t.Run("Constructor with config", func(t *testing.T) {
+		reporter := newMockReporter(t)
+		value := NewArrayC(Config{
+			Reporter: reporter,
+		}, testValue)
+		value.chain.assertNotFailed(t)
+		value.Equal(testValue)
+	})
+}
 
 func TestArrayEmpty(t *testing.T) {
 	reporter := newMockReporter(t)
diff --git a/boolean_test.go b/boolean_test.go
index 10b7e66..91ad927 100644
--- a/boolean_test.go
+++ b/boolean_test.go
@@ -21,6 +21,22 @@ func TestBooleanFailed(t *testing.T) {
 	value.False()
 }
 
+func TestBooleanConstructors(t *testing.T) {
+	t.Run("Constructor without config", func(t *testing.T) {
+		reporter := newMockReporter(t)
+		value := NewBoolean(reporter, true)
+		value.chain.assertNotFailed(t)
+		value.Equal(true)
+	})
+	t.Run("Constructor with config", func(t *testing.T) {
+		reporter := newMockReporter(t)
+		value := NewBooleanC(Config{
+			Reporter: reporter,
+		}, true)
+		value.chain.assertNotFailed(t)
+		value.Equal(true)
+	})
+}
 func TestBooleanGetters(t *testing.T) {
 	reporter := newMockReporter(t)
 
diff --git a/cookie_test.go b/cookie_test.go
index 62ce7a1..21b6da7 100644
--- a/cookie_test.go
+++ b/cookie_test.go
@@ -56,6 +56,40 @@ func TestCookieFailed(t *testing.T) {
 	})
 }
 
+func TestCookieConstructors(t *testing.T) {
+	cookie := &http.Cookie{
+		Name:    "Test",
+		Value:   "Test_val",
+		Domain:  "example.com",
+		Path:    "/path",
+		Expires: time.Unix(1234, 0),
+		MaxAge:  123,
+	}
+	t.Run("Constructor without config", func(t *testing.T) {
+		reporter := newMockReporter(t)
+		value := NewCookie(reporter, cookie)
+		value.chain.assertNotFailed(t)
+		value.Name().Equal("Test")
+		value.Value().Equal("Test_val")
+		value.Domain().Equal("example.com")
+		value.Expires().Equal(time.Unix(1234, 0))
+		value.MaxAge().Equal(123 * time.Second)
+	})
+	t.Run("Constructor with config", func(t *testing.T) {
+		reporter := newMockReporter(t)
+		value := NewCookieC(Config{
+			Reporter: reporter,
+		}, cookie)
+		value.chain.assertNotFailed(t)
+		value.Name().Equal("Test")
+		value.Value().Equal("Test_val")
+		value.Domain().Equal("example.com")
+		value.Expires().Equal(time.Unix(1234, 0))
+		value.MaxAge().Equal(123 * time.Second)
+	})
+
+}
+
 func TestCookieGetters(t *testing.T) {
 	reporter := newMockReporter(t)
 
diff --git a/datetime_test.go b/datetime_test.go
index 3f524f7..961d994 100644
--- a/datetime_test.go
+++ b/datetime_test.go
@@ -27,6 +27,24 @@ func TestDateTimeFailed(t *testing.T) {
 	value.NotInRange(tm, tm)
 }
 
+func TestDatetimeConstructors(t *testing.T) {
+	time := time.Unix(0, 1234)
+	t.Run("Constructor without config", func(t *testing.T) {
+		reporter := newMockReporter(t)
+		value := NewDateTime(reporter, time)
+		value.chain.assertNotFailed(t)
+		value.Equal(time)
+	})
+	t.Run("Constructor with config", func(t *testing.T) {
+		reporter := newMockReporter(t)
+		value := NewDateTimeC(Config{
+			Reporter: reporter,
+		}, time)
+		value.chain.assertNotFailed(t)
+		value.Equal(time)
+	})
+}
+
 func TestDateTimeEqual(t *testing.T) {
 	reporter := newMockReporter(t)
 
diff --git a/duration_test.go b/duration_test.go
index 8085999..7b6aaae 100644
--- a/duration_test.go
+++ b/duration_test.go
@@ -24,6 +24,25 @@ func TestDurationFailed(t *testing.T) {
 	value.NotInRange(tm, tm)
 }
 
+func TestDurationConstructors(t *testing.T) {
+	tm := time.Second
+	t.Run("Constructor without config", func(t *testing.T) {
+		reporter := newMockReporter(t)
+		value := NewDuration(reporter, tm)
+		value.chain.assertNotFailed(t)
+		value.Equal(tm)
+	})
+	t.Run("Constructor with config", func(t *testing.T) {
+		reporter := newMockReporter(t)
+		value := NewDurationC(Config{
+			Reporter: reporter,
+		}, tm)
+		value.chain.assertNotFailed(t)
+		value.Equal(tm)
+	})
+
+}
+
 func TestDurationSet(t *testing.T) {
 	chain := newMockChain(t)
 
diff --git a/match_test.go b/match_test.go
index 7e79d43..e4e93dd 100644
--- a/match_test.go
+++ b/match_test.go
@@ -21,7 +21,24 @@ func TestMatchFailed(t *testing.T) {
 	value.Values("")
 	value.NotValues("")
 }
-
+func TestMatchConstructors(t *testing.T) {
+	matches := []string{"m0", "m1", "m2"}
+	names := []string{"", "n1", "n2"}
+	t.Run("Constructor without config", func(t *testing.T) {
+		reporter := newMockReporter(t)
+		value := NewMatch(reporter, matches, names)
+		value.chain.assertNotFailed(t)
+		assert.Equal(t, matches, value.Raw())
+	})
+	t.Run("Constructor with config", func(t *testing.T) {
+		reporter := newMockReporter(t)
+		value := NewMatchC(Config{
+			Reporter: reporter,
+		}, matches, names)
+		value.chain.assertNotFailed(t)
+		assert.Equal(t, matches, value.Raw())
+	})
+}
 func TestMatchGetters(t *testing.T) {
 	reporter := newMockReporter(t)
 
diff --git a/number_test.go b/number_test.go
index 252f060..5489b7a 100644
--- a/number_test.go
+++ b/number_test.go
@@ -28,6 +28,22 @@ func TestNumberFailed(t *testing.T) {
 	value.NotInRange(0, 0)
 }
 
+func TestNumberConstructors(t *testing.T) {
+	t.Run("Constructor without config", func(t *testing.T) {
+		reporter := newMockReporter(t)
+		value := NewNumber(reporter, 10.3)
+		value.chain.assertNotFailed(t)
+		value.Equal(10.3)
+	})
+	t.Run("Constructor with config", func(t *testing.T) {
+		reporter := newMockReporter(t)
+		value := NewNumberC(Config{
+			Reporter: reporter,
+		}, 10.3)
+		value.chain.assertNotFailed(t)
+		value.Equal(10.3)
+	})
+}
 func TestNumberGetters(t *testing.T) {
 	reporter := newMockReporter(t)
 
diff --git a/object_test.go b/object_test.go
index e11065f..ae0cb33 100644
--- a/object_test.go
+++ b/object_test.go
@@ -69,6 +69,27 @@ func TestObjectFailed(t *testing.T) {
 	})
 }
 
+func TestObjectConstructors(t *testing.T) {
+	test := map[string]interface{}{
+		"foo": 100.0,
+	}
+	t.Run("Constructor without config", func(t *testing.T) {
+		reporter := newMockReporter(t)
+		value := NewObject(reporter, test)
+		value.chain.assertNotFailed(t)
+		value.Equal(test)
+	})
+	t.Run("Constructor with config", func(t *testing.T) {
+		reporter := newMockReporter(t)
+		value := NewObjectC(Config{
+			Reporter: reporter,
+		}, test)
+		value.chain.assertNotFailed(t)
+		value.Equal(test)
+	})
+
+}
+
 func TestObjectGetters(t *testing.T) {
 	reporter := newMockReporter(t)
 
diff --git a/string_test.go b/string_test.go
index 6bff8b5..16cc1a0 100644
--- a/string_test.go
+++ b/string_test.go
@@ -46,6 +46,23 @@ func TestStringFailed(t *testing.T) {
 	value.NotIsASCII()
 }
 
+func TestStringConstructors(t *testing.T) {
+	t.Run("Constructor without config", func(t *testing.T) {
+		reporter := newMockReporter(t)
+		value := NewString(reporter, "Hello")
+		value.chain.assertNotFailed(t)
+		value.Equal("Hello")
+	})
+	t.Run("Constructor with config", func(t *testing.T) {
+		reporter := newMockReporter(t)
+		value := NewStringC(Config{
+			Reporter: reporter,
+		}, "Hello")
+		value.chain.assertNotFailed(t)
+		value.Equal("Hello")
+	})
+}
+
 func TestStringGetters(t *testing.T) {
 	reporter := newMockReporter(t)
 
diff --git a/value_test.go b/value_test.go
index 1cfbf95..b572e2f 100644
--- a/value_test.go
+++ b/value_test.go
@@ -40,6 +40,24 @@ func TestValueFailed(t *testing.T) {
 	value.NotEqual(nil)
 }
 
+func TestValueConstructors(t *testing.T) {
+	t.Run("Constructor without config", func(t *testing.T) {
+		reporter := newMockReporter(t)
+		value := NewValue(reporter, "Test")
+		value.chain.assertNotFailed(t)
+		value.String().chain.assertNotFailed(t)
+		value.Equal("Test")
+	})
+	t.Run("Constructor with config", func(t *testing.T) {
+		reporter := newMockReporter(t)
+		value := NewValueC(Config{
+			Reporter: reporter,
+		}, "Test")
+		value.chain.assertNotFailed(t)
+		value.String().chain.assertNotFailed(t)
+		value.Equal("Test")
+	})
+}
 func TestValueCastNull(t *testing.T) {
 	reporter := newMockReporter(t)