1 |
|
2 | 'use strict';
|
3 |
|
4 | if (typeof require === 'function') {
|
5 | var expect = require('expect.js'),
|
6 | util = require('util'),
|
7 | DeepDiff = require('..');
|
8 | }
|
9 | var deep = DeepDiff,
|
10 | executingInBrowser = 'undefined' !== typeof window;
|
11 |
|
12 | describe('deep-diff', function() {
|
13 | var empty = {};
|
14 |
|
15 | describe('A target that has no properties', function() {
|
16 |
|
17 | it('shows no differences when compared to another empty object', function() {
|
18 | expect(deep.diff(empty, {})).to.be.an('undefined');
|
19 | });
|
20 |
|
21 | describe('when compared to a different type of keyless object', function() {
|
22 | var ctor = function() {
|
23 | this.foo = 'bar';
|
24 | };
|
25 | var comparandTuples = [
|
26 | ['an array', {
|
27 | key: []
|
28 | }],
|
29 | ['an object', {
|
30 | key: {}
|
31 | }],
|
32 | ['a date', {
|
33 | key: new Date()
|
34 | }],
|
35 | ['a null', {
|
36 | key: null
|
37 | }],
|
38 | ['a regexp literal', {
|
39 | key: /a/
|
40 | }],
|
41 | ['Math', {
|
42 | key: Math
|
43 | }]
|
44 | ];
|
45 |
|
46 | comparandTuples.forEach(function(lhsTuple) {
|
47 | comparandTuples.forEach(function(rhsTuple) {
|
48 | if (lhsTuple[0] === rhsTuple[0]) {
|
49 | return;
|
50 | }
|
51 | it('shows differences when comparing ' + lhsTuple[0] + ' to ' + rhsTuple[0], function() {
|
52 | var diff = deep.diff(lhsTuple[1], rhsTuple[1]);
|
53 | expect(diff).to.be.ok();
|
54 | expect(diff.length).to.be(1);
|
55 | expect(diff[0]).to.have.property('kind');
|
56 | expect(diff[0].kind).to.be('E');
|
57 | });
|
58 | });
|
59 | });
|
60 | });
|
61 |
|
62 | describe('when compared with an object having other properties', function() {
|
63 | var comparand = {
|
64 | other: 'property',
|
65 | another: 13.13
|
66 | };
|
67 | var diff = deep.diff(empty, comparand);
|
68 |
|
69 | it('the differences are reported', function() {
|
70 | expect(diff).to.be.ok();
|
71 | expect(diff.length).to.be(2);
|
72 |
|
73 | expect(diff[0]).to.have.property('kind');
|
74 | expect(diff[0].kind).to.be('N');
|
75 | expect(diff[0]).to.have.property('path');
|
76 | expect(diff[0].path).to.be.an(Array);
|
77 | expect(diff[0].path[0]).to.eql('other');
|
78 | expect(diff[0]).to.have.property('rhs');
|
79 | expect(diff[0].rhs).to.be('property');
|
80 |
|
81 | expect(diff[1]).to.have.property('kind');
|
82 | expect(diff[1].kind).to.be('N');
|
83 | expect(diff[1]).to.have.property('path');
|
84 | expect(diff[1].path).to.be.an(Array);
|
85 | expect(diff[1].path[0]).to.eql('another');
|
86 | expect(diff[1]).to.have.property('rhs');
|
87 | expect(diff[1].rhs).to.be(13.13);
|
88 | });
|
89 |
|
90 | });
|
91 |
|
92 | });
|
93 |
|
94 | describe('A target that has one property', function() {
|
95 | var lhs = {
|
96 | one: 'property'
|
97 | };
|
98 |
|
99 | it('shows no differences when compared to itself', function() {
|
100 | expect(deep.diff(lhs, lhs)).to.be.an('undefined');
|
101 | });
|
102 |
|
103 | it('shows the property as removed when compared to an empty object', function() {
|
104 | var diff = deep.diff(lhs, empty);
|
105 | expect(diff).to.be.ok();
|
106 | expect(diff.length).to.be(1);
|
107 | expect(diff[0]).to.have.property('kind');
|
108 | expect(diff[0].kind).to.be('D');
|
109 | });
|
110 |
|
111 | it('shows the property as edited when compared to an object with null', function() {
|
112 | var diff = deep.diff(lhs, {
|
113 | one: null
|
114 | });
|
115 | expect(diff).to.be.ok();
|
116 | expect(diff.length).to.be(1);
|
117 | expect(diff[0]).to.have.property('kind');
|
118 | expect(diff[0].kind).to.be('E');
|
119 | });
|
120 |
|
121 | it('shows the property as edited when compared to an array', function() {
|
122 | var diff = deep.diff(lhs, ['one']);
|
123 | expect(diff).to.be.ok();
|
124 | expect(diff.length).to.be(1);
|
125 | expect(diff[0]).to.have.property('kind');
|
126 | expect(diff[0].kind).to.be('E');
|
127 | });
|
128 |
|
129 | });
|
130 |
|
131 | describe('A target that has null value', function() {
|
132 | var lhs = {
|
133 | key: null
|
134 | };
|
135 |
|
136 | it('shows no differences when compared to itself', function() {
|
137 | expect(deep.diff(lhs, lhs)).to.be.an('undefined');
|
138 | });
|
139 |
|
140 | it('shows the property as removed when compared to an empty object', function() {
|
141 | var diff = deep.diff(lhs, empty);
|
142 | expect(diff).to.be.ok();
|
143 | expect(diff.length).to.be(1);
|
144 | expect(diff[0]).to.have.property('kind');
|
145 | expect(diff[0].kind).to.be('D');
|
146 | });
|
147 |
|
148 | it('shows the property is changed when compared to an object that has value', function() {
|
149 | var diff = deep.diff(lhs, {
|
150 | key: 'value'
|
151 | });
|
152 | expect(diff).to.be.ok();
|
153 | expect(diff.length).to.be(1);
|
154 | expect(diff[0]).to.have.property('kind');
|
155 | expect(diff[0].kind).to.be('E');
|
156 | });
|
157 |
|
158 | it('shows that an object property is changed when it is set to null', function() {
|
159 | lhs.key = {
|
160 | nested: 'value'
|
161 | };
|
162 | var diff = deep.diff(lhs, {
|
163 | key: null
|
164 | });
|
165 | expect(diff).to.be.ok();
|
166 | expect(diff.length).to.be(1);
|
167 | expect(diff[0]).to.have.property('kind');
|
168 | expect(diff[0].kind).to.be('E');
|
169 | });
|
170 |
|
171 | });
|
172 |
|
173 |
|
174 | describe('A target that has a date value', function() {
|
175 | var lhs = {
|
176 | key: new Date(555555555555)
|
177 | };
|
178 |
|
179 | it('shows the property is changed with a new date value', function() {
|
180 | var diff = deep.diff(lhs, {
|
181 | key: new Date(777777777777)
|
182 | });
|
183 | expect(diff).to.be.ok();
|
184 | expect(diff.length).to.be(1);
|
185 | expect(diff[0]).to.have.property('kind');
|
186 | expect(diff[0].kind).to.be('E');
|
187 | });
|
188 |
|
189 | });
|
190 |
|
191 |
|
192 | describe('A target that has a NaN', function() {
|
193 | var lhs = {
|
194 | key: NaN
|
195 | };
|
196 |
|
197 | it('shows the property is changed when compared to another number', function() {
|
198 | var diff = deep.diff(lhs, {
|
199 | key: 0
|
200 | });
|
201 | expect(diff).to.be.ok();
|
202 | expect(diff.length).to.be(1);
|
203 | expect(diff[0]).to.have.property('kind');
|
204 | expect(diff[0].kind).to.be('E');
|
205 | });
|
206 |
|
207 | it('shows no differences when compared to another NaN', function() {
|
208 | var diff = deep.diff(lhs, {
|
209 | key: NaN
|
210 | });
|
211 | expect(diff).to.be.an('undefined');
|
212 | });
|
213 |
|
214 | });
|
215 |
|
216 |
|
217 | describe('When executing in a browser (otherwise these tests are benign)', function() {
|
218 |
|
219 | it('#isConflict reports conflict in the global namespace for `DeepDiff`', function() {
|
220 |
|
221 | if (executingInBrowser) {
|
222 | expect(DeepDiff.isConflict()).to.be.ok();
|
223 | }
|
224 | });
|
225 |
|
226 | it('#noConflict restores prior definition for the global `DeepDiff`', function() {
|
227 |
|
228 | if (executingInBrowser) {
|
229 | expect(DeepDiff.isConflict()).to.be.ok();
|
230 | var another = DeepDiff.noConflict();
|
231 | expect(another).to.be(deep);
|
232 | expect(DeepDiff).to.be(DeepDiffConflict);
|
233 | }
|
234 | });
|
235 |
|
236 | });
|
237 |
|
238 |
|
239 |
|
240 | describe('When filtering keys', function() {
|
241 | var lhs = {
|
242 | enhancement: 'Filter/Ignore Keys?',
|
243 | numero: 11,
|
244 | submitted_by: 'ericclemmons',
|
245 | supported_by: ['ericclemmons'],
|
246 | status: 'open'
|
247 | };
|
248 | var rhs = {
|
249 | enhancement: 'Filter/Ignore Keys?',
|
250 | numero: 11,
|
251 | submitted_by: 'ericclemmons',
|
252 | supported_by: [
|
253 | 'ericclemmons',
|
254 | 'TylerGarlick',
|
255 | 'flitbit',
|
256 | 'ergdev'
|
257 | ],
|
258 | status: 'closed',
|
259 | fixed_by: 'flitbit'
|
260 | };
|
261 |
|
262 | describe('if the filtered property is an array', function() {
|
263 |
|
264 | it('changes to the array do not appear as a difference', function() {
|
265 | var prefilter = function(path, key) {
|
266 | return key === 'supported_by';
|
267 | };
|
268 | var diff = deep(lhs, rhs, prefilter);
|
269 | expect(diff).to.be.ok();
|
270 | expect(diff.length).to.be(2);
|
271 | expect(diff[0]).to.have.property('kind');
|
272 | expect(diff[0].kind).to.be('E');
|
273 | expect(diff[1]).to.have.property('kind');
|
274 | expect(diff[1].kind).to.be('N');
|
275 | });
|
276 |
|
277 | });
|
278 |
|
279 | describe('if the filtered property is not an array', function() {
|
280 |
|
281 | it('changes do not appear as a difference', function() {
|
282 | var prefilter = function(path, key) {
|
283 | return key === 'fixed_by';
|
284 | };
|
285 | var diff = deep(lhs, rhs, prefilter);
|
286 | expect(diff).to.be.ok();
|
287 | expect(diff.length).to.be(4);
|
288 | expect(diff[0]).to.have.property('kind');
|
289 | expect(diff[0].kind).to.be('A');
|
290 | expect(diff[1]).to.have.property('kind');
|
291 | expect(diff[1].kind).to.be('A');
|
292 | expect(diff[2]).to.have.property('kind');
|
293 | expect(diff[2].kind).to.be('A');
|
294 | expect(diff[3]).to.have.property('kind');
|
295 | expect(diff[3].kind).to.be('E');
|
296 | });
|
297 |
|
298 | });
|
299 | });
|
300 |
|
301 | describe('A target that has nested values', function() {
|
302 | var nestedOne = {
|
303 | noChange: 'same',
|
304 | levelOne: {
|
305 | levelTwo: 'value'
|
306 | }
|
307 | };
|
308 | var nestedTwo = {
|
309 | noChange: 'same',
|
310 | levelOne: {
|
311 | levelTwo: 'another value'
|
312 | }
|
313 | };
|
314 |
|
315 | it('shows no differences when compared to itself', function() {
|
316 | expect(deep.diff(nestedOne, nestedOne)).to.be.an('undefined');
|
317 | });
|
318 |
|
319 | it('shows the property as removed when compared to an empty object', function() {
|
320 | var diff = deep(nestedOne, empty);
|
321 | expect(diff).to.be.ok();
|
322 | expect(diff.length).to.be(2);
|
323 | expect(diff[0]).to.have.property('kind');
|
324 | expect(diff[0].kind).to.be('D');
|
325 | expect(diff[1]).to.have.property('kind');
|
326 | expect(diff[1].kind).to.be('D');
|
327 | });
|
328 |
|
329 | it('shows the property is changed when compared to an object that has value', function() {
|
330 | var diff = deep.diff(nestedOne, nestedTwo);
|
331 | expect(diff).to.be.ok();
|
332 | expect(diff.length).to.be(1);
|
333 | expect(diff[0]).to.have.property('kind');
|
334 | expect(diff[0].kind).to.be('E');
|
335 | });
|
336 |
|
337 | it('shows the property as added when compared to an empty object on left', function() {
|
338 | var diff = deep.diff(empty, nestedOne);
|
339 | expect(diff).to.be.ok();
|
340 | expect(diff.length).to.be(2);
|
341 | expect(diff[0]).to.have.property('kind');
|
342 | expect(diff[0].kind).to.be('N');
|
343 | });
|
344 |
|
345 | describe('when diff is applied to a different empty object', function() {
|
346 | var diff = deep.diff(nestedOne, nestedTwo);
|
347 | var result = {};
|
348 |
|
349 | it('has result with nested values', function() {
|
350 | deep.applyChange(result, nestedTwo, diff[0]);
|
351 | expect(result.levelOne).to.be.ok();
|
352 | expect(result.levelOne).to.be.an('object');
|
353 | expect(result.levelOne.levelTwo).to.be.ok();
|
354 | expect(result.levelOne.levelTwo).to.eql('another value');
|
355 | });
|
356 |
|
357 | });
|
358 |
|
359 | });
|
360 |
|
361 | describe('regression test for bug #10, ', function() {
|
362 | var lhs = {
|
363 | "id": "Release",
|
364 | "phases": [{
|
365 | "id": "Phase1",
|
366 | "tasks": [{
|
367 | "id": "Task1"
|
368 | }, {
|
369 | "id": "Task2"
|
370 | }]
|
371 | }, {
|
372 | "id": "Phase2",
|
373 | "tasks": [{
|
374 | "id": "Task3"
|
375 | }]
|
376 | }]
|
377 | };
|
378 | var rhs = {
|
379 | "id": "Release",
|
380 | "phases": [{
|
381 |
|
382 | "id": "Phase2",
|
383 | "tasks": [{
|
384 | "id": "Task3"
|
385 | }]
|
386 | }, {
|
387 | "id": "Phase1",
|
388 | "tasks": [{
|
389 | "id": "Task1"
|
390 | }, {
|
391 | "id": "Task2"
|
392 | }]
|
393 | }]
|
394 | };
|
395 |
|
396 | describe('differences in nested arrays are detected', function() {
|
397 | var diff = deep.diff(lhs, rhs);
|
398 |
|
399 |
|
400 | expect(diff).to.be.ok();
|
401 | expect(diff.length).to.be(6);
|
402 |
|
403 |
|
404 |
|
405 | expect(diff[0].kind).to.be('E');
|
406 | expect(diff[0].path).to.be.an('array');
|
407 | expect(diff[0].path).to.have.length(3);
|
408 | expect(diff[0].path[0]).to.be('phases');
|
409 | expect(diff[0].path[1]).to.be(0);
|
410 | expect(diff[0].path[2]).to.be('id');
|
411 | expect(diff[0].lhs).to.be('Phase1');
|
412 | expect(diff[0].rhs).to.be('Phase2');
|
413 |
|
414 |
|
415 |
|
416 | expect(diff[1].kind).to.be('E');
|
417 | expect(diff[1].path).to.be.an('array');
|
418 | expect(diff[1].path).to.have.length(5);
|
419 | expect(diff[1].path[0]).to.be('phases');
|
420 | expect(diff[1].path[1]).to.be(0);
|
421 | expect(diff[1].path[2]).to.be('tasks');
|
422 | expect(diff[1].path[3]).to.be(0);
|
423 | expect(diff[1].path[4]).to.be('id');
|
424 | expect(diff[1].lhs).to.be('Task1');
|
425 | expect(diff[1].rhs).to.be('Task3');
|
426 |
|
427 |
|
428 |
|
429 | expect(diff[2].kind).to.be('A');
|
430 | expect(diff[2].path).to.be.an('array');
|
431 | expect(diff[2].path).to.have.length(3);
|
432 | expect(diff[2].path[0]).to.be('phases');
|
433 | expect(diff[2].path[1]).to.be(0);
|
434 | expect(diff[2].path[2]).to.be('tasks');
|
435 | expect(diff[2].index).to.be(1);
|
436 | expect(diff[2].item.kind).to.be('D');
|
437 |
|
438 |
|
439 |
|
440 | expect(diff[3].kind).to.be('E');
|
441 | expect(diff[3].path).to.be.an('array');
|
442 | expect(diff[3].path).to.have.length(3);
|
443 | expect(diff[3].path[0]).to.be('phases');
|
444 | expect(diff[3].path[1]).to.be(1);
|
445 | expect(diff[3].path[2]).to.be('id');
|
446 | expect(diff[3].lhs).to.be('Phase2');
|
447 | expect(diff[3].rhs).to.be('Phase1');
|
448 |
|
449 |
|
450 |
|
451 | expect(diff[4].kind).to.be('E');
|
452 | expect(diff[4].path).to.be.an('array');
|
453 | expect(diff[4].path).to.have.length(5);
|
454 | expect(diff[4].path[0]).to.be('phases');
|
455 | expect(diff[4].path[1]).to.be(1);
|
456 | expect(diff[4].path[2]).to.be('tasks');
|
457 | expect(diff[4].path[3]).to.be(0);
|
458 | expect(diff[4].path[4]).to.be('id');
|
459 | expect(diff[4].lhs).to.be('Task3');
|
460 | expect(diff[4].rhs).to.be('Task1');
|
461 |
|
462 |
|
463 |
|
464 | expect(diff[5].kind).to.be('A');
|
465 | expect(diff[5].path).to.be.an('array');
|
466 | expect(diff[5].path).to.have.length(3);
|
467 | expect(diff[5].path[0]).to.be('phases');
|
468 | expect(diff[5].path[1]).to.be(1);
|
469 | expect(diff[5].path[2]).to.be('tasks');
|
470 | expect(diff[5].index).to.be(1);
|
471 | expect(diff[5].item.kind).to.be('N');
|
472 |
|
473 | it('differences can be applied', function() {
|
474 | var applied = deep.applyDiff(lhs, rhs);
|
475 |
|
476 | it('and the result equals the rhs', function() {
|
477 |
|
478 | });
|
479 |
|
480 | });
|
481 | });
|
482 |
|
483 | });
|
484 |
|
485 | });
|