| 1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120 |  
 
1x
 
47x
 
47x
47x
 
47x
15x
15x
28x
 
15x
15x
27x
 
 
47x
 
 
 
 
 
 
 
47x
4x
 
 
43x
1x
 
 
42x
8x
 
 
34x
73x
73x
73x
73x
14x
 
 
 
20x
 
 
 
 
 
 
 
 
 
 
135x
 
475x
168x
 
 
307x
307x
50x
 
 
257x
3x
 
 
254x
 
254x
254x
3x
 
 
251x
251x
21x
 
 
108x
 
 
 
254x
 
 
 
 
332x
2x
 
330x
135x
 
195x
 
 
 
 
79x
115x
24x
 
24x
 
 
91x
 
 
 
 
 
  | import {isEqual} from 'lodash';
 
export const toDeepEqualMatcher = {
  toDeepEqual: (util) => {
    return {
        compare: function(actuals, expecteds) {
          const test = compareArrays(actuals, expecteds);
          const result = { pass: (test === 'pass'), message: `compareArrays say: ${test}` + '\n\n' };
 
          if (!result.pass && Array.isArray(actuals) && Array.isArray(expecteds)) {
            result.message += 'Actual \n';
            actuals.forEach(function(x) {
              result.message += stringify(x) + '\n';
            });
            result.message += '\nExpected \n';
            expecteds.forEach(function(x) {
              result.message += stringify(x) + '\n';
            });
          }
          return result;
        }
    };
  }
};
 
function compareArrays(actuals, expecteds): any {
 
  if ((actuals !== null && expecteds === null) || (actuals === null && expecteds !== null)) {
    return 'Failed: Non-null does not compare to null';
  }
 
  if (actuals.length !== expecteds.length) {
    return 'Failed: Arrays not the same length';
  }
 
  if (!Array.isArray(actuals) || !Array.isArray(expecteds)) {
    return compareValues(expecteds, actuals, -1);
  }
 
  for (let idx = 0; idx < expecteds.length; idx++ ) {
    const expected = expecteds[idx];
    const actual = actuals[idx];
    const result = compareValues(actual, expected, idx);
    if (result !== 'pass') {
      return result;
    }
  };
 
  return 'pass';
}
 
/*
  Compares objects irrespective of the property ordering
  and only compares those properties supplied in 'expected'
  Saves a bit of refactoring in tests when 'actual' property order changes,
  or new properties are added that are irrelevent to the test
*/
function compareProperties(actual, expected, idx): any {
 
  for (const key in expected) {
 
    if (typeof expected[key] === 'function') {
      continue;  // ignore function properties
    }
 
    const value = expected[key];
    if (value === undefined) {
      continue;
    }
 
    if (!actual.hasOwnProperty(key)) {
      return `Index: ${idx}, Actual has no property '${key}'`;
    }
 
    const otherValue = actual[key];
 
    const types = compareTypes(value, otherValue, key, idx);
    if (types !== 'pass') {
      return types;
    }
 
    const values = compareValues(value, otherValue, idx);
    if (values !== 'pass') {
      return values;
    }
  }
  return 'pass';
}
 
function compareTypes(value, otherValue, key, idx): any {
  return typeof value === typeof otherValue ? 'pass'
    : `Index: ${idx}, Key: '${key}', Types differ ('${typeof value}' vs '${typeof otherValue}')`;
}
 
function compareValues(value, otherValue, idx): any {
  if ((value !== null && otherValue === null) || (value === null && otherValue !== null)) {
    return 'Failed: Non-null does not compare to null';
  }
  if (typeof value === 'object') {
    return compareProperties(value, otherValue, idx);
  }
  return value === otherValue ? 'pass'
    : `Index: ${idx}, Not objects, values differ (${value} vs ${otherValue})`;
}
 
function stringify(x) {
  return JSON.stringify(x, function(key, value) {
    if (Array.isArray(value)) {
      return '[' + value
        .map(function(i) {
          return '\n\t' + stringify(i);
        }) + '\n]';
    }
    return value;
  })
    .replace(/\\"/g, '"')
    .replace(/\\t/g, '\t')
    .replace(/\\n/g, '\n');
}
  |