package httpexpect import ( "reflect" ) // Array provides methods to inspect attached []interface{} object // (Go representation of JSON array). type Array struct { chain chain value []interface{} } // NewArray returns a new Array given a reporter used to report failures // and value to be inspected. // // Both reporter and value should not be nil. If value is nil, failure is // reported. // // Example: // array := NewArray(t, []interface{}{"foo", 123}) func NewArray(reporter Reporter, value []interface{}) *Array { chain := makeChain(reporter) if value == nil { chain.fail("expected non-nil array value") } else { value, _ = canonArray(&chain, value) } return &Array{chain, value} } // Raw returns underlying value attached to Array. // This is the value originally passed to NewArray, converted to canonical form. // // Example: // array := NewArray(t, []interface{}{"foo", 123}) // assert.Equal(t, []interface{}{"foo", 123.0}, array.Raw()) func (a *Array) Raw() []interface{} { return a.value } // Path is similar to Value.Path. func (a *Array) Path(path string) *Value { return getPath(&a.chain, a.value, path) } // Schema is similar to Value.Schema. func (a *Array) Schema(schema interface{}) *Array { checkSchema(&a.chain, a.value, schema) return a } // Length returns a new Number object that may be used to inspect array length. // // Example: // array := NewArray(t, []interface{}{1, 2, 3}) // array.Length().Equal(3) func (a *Array) Length() *Number { return &Number{a.chain, float64(len(a.value))} } // Element returns a new Value object that may be used to inspect array element // for given index. // // If index is out of array bounds, Element reports failure and returns empty // (but non-nil) value. // // Example: // array := NewArray(t, []interface{}{"foo", 123}) // array.Element(0).String().Equal("foo") // array.Element(1).Number().Equal(123) func (a *Array) Element(index int) *Value { if index < 0 || index >= len(a.value) { a.chain.fail( "\narray index out of bounds:\n index %d\n\n bounds [%d; %d)", index, 0, len(a.value)) return &Value{a.chain, nil} } return &Value{a.chain, a.value[index]} } // First returns a new Value object that may be used to inspect first element // of given array. // // If given array is empty, First reports failure and returns empty // (but non-nil) value. // // Example: // array := NewArray(t, []interface{}{"foo", 123}) // array.First().String().Equal("foo") func (a *Array) First() *Value { if len(a.value) < 1 { a.chain.fail("\narray is empty") return &Value{a.chain, nil} } return &Value{a.chain, a.value[0]} } // Last returns a new Value object that may be used to inspect last element // of given array. // // If given array is empty, Last reports failure and returns empty // (but non-nil) value. // // Example: // array := NewArray(t, []interface{}{"foo", 123}) // array.Last().Number().Equal(123) func (a *Array) Last() *Value { if len(a.value) < 1 { a.chain.fail("\narray is empty") return &Value{a.chain, nil} } return &Value{a.chain, a.value[len(a.value)-1]} } // Iter returns a new slice of Values attached to array elements. // // Example: // strings := []interface{}{"foo", "bar"} // array := NewArray(t, strings) // // for n, val := range array.Iter() { // val.String().Equal(strings[n]) // } func (a *Array) Iter() []Value { if a.chain.failed() { return []Value{} } ret := []Value{} for n := range a.value { ret = append(ret, Value{a.chain, a.value[n]}) } return ret } // Empty succeeds if array is empty. // // Example: // array := NewArray(t, []interface{}{}) // array.Empty() func (a *Array) Empty() *Array { return a.Equal([]interface{}{}) } // NotEmpty succeeds if array is non-empty. // // Example: // array := NewArray(t, []interface{}{"foo", 123}) // array.NotEmpty() func (a *Array) NotEmpty() *Array { return a.NotEqual([]interface{}{}) } // Equal succeeds if array is equal to given Go slice. // Before comparison, both array and value are converted to canonical form. // // value should be a slice of any type. // // Example: // array := NewArray(t, []interface{}{"foo", 123}) // array.Equal([]interface{}{"foo", 123}) // // array := NewArray(t, []interface{}{"foo", "bar"}) // array.Equal([]string{}{"foo", "bar"}) // // array := NewArray(t, []interface{}{123, 456}) // array.Equal([]int{}{123, 456}) func (a *Array) Equal(value interface{}) *Array { expected, ok := canonArray(&a.chain, value) if !ok { return a } if !reflect.DeepEqual(expected, a.value) { a.chain.fail("\nexpected array equal to:\n%s\n\nbut got:\n%s\n\ndiff:\n%s", dumpValue(expected), dumpValue(a.value), diffValues(expected, a.value)) } return a } // NotEqual succeeds if array is not equal to given Go slice. // Before comparison, both array and value are converted to canonical form. // // value should be a slice of any type. // // Example: // array := NewArray(t, []interface{}{"foo", 123}) // array.NotEqual([]interface{}{123, "foo"}) func (a *Array) NotEqual(value interface{}) *Array { expected, ok := canonArray(&a.chain, value) if !ok { return a } if reflect.DeepEqual(expected, a.value) { a.chain.fail("\nexpected array not equal to:\n%s", dumpValue(expected)) } return a } // Elements succeeds if array contains all given elements, in given order, and only // them. Before comparison, array and all elements are converted to canonical form. // // For partial or unordered comparison, see Contains and ContainsOnly. // // Example: // array := NewArray(t, []interface{}{"foo", 123}) // array.Elements("foo", 123) // // This calls are equivalent: // array.Elelems("a", "b") // array.Equal([]interface{}{"a", "b"}) func (a *Array) Elements(values ...interface{}) *Array { return a.Equal(values) } // Contains succeeds if array contains all given elements (in any order). // Before comparison, array and all elements are converted to canonical form. // // Example: // array := NewArray(t, []interface{}{"foo", 123}) // array.Contains(123, "foo") func (a *Array) Contains(values ...interface{}) *Array { elements, ok := canonArray(&a.chain, values) if !ok { return a } for _, e := range elements { if !a.containsElement(e) { a.chain.fail("\nexpected array containing element:\n%s\n\nbut got:\n%s", dumpValue(e), dumpValue(a.value)) } } return a } // NotContains succeeds if array contains none of given elements. // Before comparison, array and all elements are converted to canonical form. // // Example: // array := NewArray(t, []interface{}{"foo", 123}) // array.NotContains("bar") // success // array.NotContains("bar", "foo") // failure (array contains "foo") func (a *Array) NotContains(values ...interface{}) *Array { elements, ok := canonArray(&a.chain, values) if !ok { return a } for _, e := range elements { if a.containsElement(e) { a.chain.fail("\nexpected array not containing element:\n%s\n\nbut got:\n%s", dumpValue(e), dumpValue(a.value)) } } return a } // ContainsOnly succeeds if array contains all given elements, in any order, and only // them. Before comparison, array and all elements are converted to canonical form. // // Example: // array := NewArray(t, []interface{}{"foo", 123}) // array.ContainsOnly(123, "foo") // // This calls are equivalent: // array.ContainsOnly("a", "b") // array.ContainsOnly("b", "a") func (a *Array) ContainsOnly(values ...interface{}) *Array { elements, ok := canonArray(&a.chain, values) if !ok { return a } if len(elements) != len(a.value) { a.chain.fail("\nexpected array of length == %d:\n%s\n\n"+ "but got array of length %d:\n%s", len(elements), dumpValue(elements), len(a.value), dumpValue(a.value)) return a } for _, e := range elements { if !a.containsElement(e) { a.chain.fail("\nexpected array containing element:\n%s\n\nbut got:\n%s", dumpValue(e), dumpValue(a.value)) } } return a } func (a *Array) containsElement(expected interface{}) bool { for _, e := range a.value { if reflect.DeepEqual(expected, e) { return true } } return false }