| 87 | } |
| 88 | |
| 89 | function listPropertyDifferences( |
| 90 | diffs: string[], |
| 91 | path: string, |
| 92 | actual: any, |
| 93 | expected: any, |
| 94 | depth: number, |
| 95 | pp: (value: any) => string, |
| 96 | ) { |
| 97 | if (actual === expected) return; |
| 98 | if (typeof actual !== typeof expected) { |
| 99 | diffs.push(`${path}: Expected ${pp(actual)} to be ${pp(expected)}`); |
| 100 | } else if (depth && Array.isArray(expected)) { |
| 101 | if (!Array.isArray(actual)) { |
| 102 | diffs.push(`${path}: Expected ${pp(expected)} but was ${pp(actual)}`); |
| 103 | } else { |
| 104 | const maxLength = Math.max(actual.length, expected.length); |
| 105 | listPropertyDifferences( |
| 106 | diffs, |
| 107 | path + '.length', |
| 108 | expected.length, |
| 109 | actual.length, |
| 110 | depth - 1, |
| 111 | pp, |
| 112 | ); |
| 113 | for (let i = 0; i < maxLength; i++) { |
| 114 | const actualItem = actual[i]; |
| 115 | const expectedItem = expected[i]; |
| 116 | listPropertyDifferences( |
| 117 | diffs, |
| 118 | path + '[' + i + ']', |
| 119 | actualItem, |
| 120 | expectedItem, |
| 121 | depth - 1, |
| 122 | pp, |
| 123 | ); |
| 124 | } |
| 125 | } |
| 126 | } else if ( |
| 127 | depth && |
| 128 | expected && |
| 129 | typeof expected === 'object' && |
| 130 | actual && |
| 131 | typeof actual === 'object' |
| 132 | ) { |
| 133 | new Set(Object.keys(expected).concat(Object.keys(actual))).forEach((key) => { |
| 134 | const actualItem = actual[key]; |
| 135 | const expectedItem = expected[key]; |
| 136 | listPropertyDifferences(diffs, path + '.' + key, actualItem, expectedItem, depth - 1, pp); |
| 137 | }); |
| 138 | } else { |
| 139 | diffs.push(`${path}: Expected ${pp(actual)} to be ${pp(expected)}`); |
| 140 | } |
| 141 | } |