@@ -28,41 +28,59 @@ const RuleName = Object.freeze({
28
28
} )
29
29
30
30
/**
31
- * Count object properties including nested objects ones.
32
- * If a property is an object, its key is ignored.
31
+ * Acts as `Object.keys()`, but runs recursively,
32
+ * another difference is that when one of the key refers to
33
+ * a non-empty object, it's gonna be ignored.
34
+ *
35
+ * Keys for nested objects are prefixed with their parent key.
36
+ *
37
+ * Also note that this is not fully interoperable with `lodash.get`
38
+ * for example as keys themselves can contain dots or special characters.
33
39
*
34
40
* @example
35
- * Assertions.countNestedProperties ({
41
+ * Assertions.objectKeysDeep ({
36
42
* a: true,
37
43
* b: true,
38
44
* c: true,
39
45
* })
40
- * // => 3
41
- * Assertions.countNestedProperties ({
46
+ * // => ["a", "b", "c"]
47
+ * Assertions.objectKeysDeep ({
42
48
* a: true,
43
49
* b: true,
44
50
* c: {
45
- * a: true,
46
- * b: true,
51
+ * d: true,
52
+ * e: {},
53
+ * f: {
54
+ * g: true
55
+ * }
47
56
* },
48
57
* })
49
- * // => 4 (c is ignored because it's a nested object )
58
+ * // => ["a", "b", "c.d", "c.e", "c.f.g"] (c and c.f are ignored as non empty nested objects )
50
59
*
51
60
* @param {Object } object
52
- * @return {number }
61
+ * @param {Array } [keysAccumulator = []]
62
+ * @param {string } [parentPath = ""]
63
+ * @return {Array }
53
64
*/
54
- exports . countNestedProperties = ( object ) => {
55
- let propertiesCount = 0
56
- Object . keys ( object ) . forEach ( ( key ) => {
57
- if ( ! _ . isEmpty ( object [ key ] ) && typeof object [ key ] === 'object' ) {
58
- const count = exports . countNestedProperties ( object [ key ] )
59
- propertiesCount += count
60
- } else {
61
- propertiesCount ++
62
- }
63
- } )
65
+ exports . objectKeysDeep = ( object , keysAccumulator = [ ] , parentPath = '' ) => {
66
+ if ( _ . isPlainObject ( object ) || Array . isArray ( object ) ) {
67
+ Object . keys ( object ) . forEach ( ( key ) => {
68
+ if (
69
+ ! _ . isEmpty ( object [ key ] ) &&
70
+ ( _ . isPlainObject ( object [ key ] ) || Array . isArray ( object [ key ] ) )
71
+ ) {
72
+ keysAccumulator = exports . objectKeysDeep (
73
+ object [ key ] ,
74
+ keysAccumulator ,
75
+ `${ parentPath } ${ key } .`
76
+ )
77
+ } else {
78
+ keysAccumulator . push ( `${ parentPath } ${ key } ` )
79
+ }
80
+ } )
81
+ }
64
82
65
- return propertiesCount
83
+ return keysAccumulator
66
84
}
67
85
68
86
/**
@@ -106,10 +124,13 @@ exports.countNestedProperties = (object) => {
106
124
* @param {boolean } [exact=false] - if `true`, specification must match all object's properties
107
125
*/
108
126
exports . assertObjectMatchSpec = ( object , spec , exact = false ) => {
127
+ expect ( _ . isPlainObject ( object ) , 'Expected json response to be a valid object, but it is not' ) . to
128
+ . be . true
129
+ const specPath = new Set ( )
109
130
spec . forEach ( ( { field, matcher, value } ) => {
110
131
const currentValue = _ . get ( object , field )
111
132
const expectedValue = Cast . value ( value )
112
-
133
+ specPath . add ( field )
113
134
const rule = exports . getMatchingRule ( matcher )
114
135
115
136
switch ( rule . name ) {
@@ -208,11 +229,12 @@ exports.assertObjectMatchSpec = (object, spec, exact = false) => {
208
229
209
230
// We check we have exactly the same number of properties as expected
210
231
if ( exact === true ) {
211
- const propertiesCount = exports . countNestedProperties ( object )
232
+ const objectKeys = exports . objectKeysDeep ( object )
233
+ const specObjectKeys = Array . from ( specPath )
212
234
expect (
213
- propertiesCount ,
235
+ objectKeys ,
214
236
'Expected json response to fully match spec, but it does not'
215
- ) . to . be . equal ( spec . length )
237
+ ) . to . be . deep . equal ( specObjectKeys )
216
238
}
217
239
}
218
240
@@ -261,7 +283,10 @@ exports.getMatchingRule = (matcher) => {
261
283
262
284
const relativeDateGroups = relativeDateRegex . exec ( matcher )
263
285
if ( relativeDateGroups ) {
264
- return { name : RuleName . RelativeDate , isNegated : ! ! relativeDateGroups [ 1 ] }
286
+ return {
287
+ name : RuleName . RelativeDate ,
288
+ isNegated : ! ! relativeDateGroups [ 1 ] ,
289
+ }
265
290
}
266
291
267
292
expect . fail ( `Matcher "${ matcher } " did not match any supported assertions` )
0 commit comments