tests.js 22 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759
  1. (function (root, factory) {
  2. if (typeof define === 'function' && define.amd) { // eslint-disable-line no-undef
  3. define(['deep-diff', 'expect.js'], factory);// eslint-disable-line no-undef
  4. } else if (typeof module === 'object' && module.exports) {
  5. module.exports = factory(require('../'), require('expect.js'));
  6. } else {
  7. root.returnExports = factory(root.DeepDiff, root.expect);
  8. }
  9. // eslint-disable-next-line no-undef
  10. }(typeof self !== 'undefined' ? self : this, function (deep, expect) {
  11. describe('deep-diff', function () {
  12. var empty = {};
  13. describe('A target that has no properties', function () {
  14. it('shows no differences when compared to another empty object', function () {
  15. expect(deep.diff(empty, {})).to.be.an('undefined');
  16. });
  17. describe('when compared to a different type of keyless object', function () {
  18. var comparandTuples = [
  19. ['an array', {
  20. key: []
  21. }],
  22. ['an object', {
  23. key: {}
  24. }],
  25. ['a date', {
  26. key: new Date()
  27. }],
  28. ['a null', {
  29. key: null
  30. }],
  31. ['a regexp literal', {
  32. key: /a/
  33. }],
  34. ['Math', {
  35. key: Math
  36. }]
  37. ];
  38. comparandTuples.forEach(function (lhsTuple) {
  39. comparandTuples.forEach(function (rhsTuple) {
  40. if (lhsTuple[0] === rhsTuple[0]) {
  41. return;
  42. }
  43. it('shows differences when comparing ' + lhsTuple[0] + ' to ' + rhsTuple[0], function () {
  44. var diff = deep.diff(lhsTuple[1], rhsTuple[1]);
  45. expect(diff).to.be.ok();
  46. expect(diff.length).to.be(1);
  47. expect(diff[0]).to.have.property('kind');
  48. expect(diff[0].kind).to.be('E');
  49. });
  50. });
  51. });
  52. });
  53. describe('when compared with an object having other properties', function () {
  54. var comparand = {
  55. other: 'property',
  56. another: 13.13
  57. };
  58. var diff = deep.diff(empty, comparand);
  59. it('the differences are reported', function () {
  60. expect(diff).to.be.ok();
  61. expect(diff.length).to.be(2);
  62. expect(diff[0]).to.have.property('kind');
  63. expect(diff[0].kind).to.be('N');
  64. expect(diff[0]).to.have.property('path');
  65. expect(diff[0].path).to.be.an(Array);
  66. expect(diff[0].path[0]).to.eql('other');
  67. expect(diff[0]).to.have.property('rhs');
  68. expect(diff[0].rhs).to.be('property');
  69. expect(diff[1]).to.have.property('kind');
  70. expect(diff[1].kind).to.be('N');
  71. expect(diff[1]).to.have.property('path');
  72. expect(diff[1].path).to.be.an(Array);
  73. expect(diff[1].path[0]).to.eql('another');
  74. expect(diff[1]).to.have.property('rhs');
  75. expect(diff[1].rhs).to.be(13.13);
  76. });
  77. });
  78. });
  79. describe('A target that has one property', function () {
  80. var lhs = {
  81. one: 'property'
  82. };
  83. it('shows no differences when compared to itself', function () {
  84. expect(deep.diff(lhs, lhs)).to.be.an('undefined');
  85. });
  86. it('shows the property as removed when compared to an empty object', function () {
  87. var diff = deep.diff(lhs, empty);
  88. expect(diff).to.be.ok();
  89. expect(diff.length).to.be(1);
  90. expect(diff[0]).to.have.property('kind');
  91. expect(diff[0].kind).to.be('D');
  92. });
  93. it('shows the property as edited when compared to an object with null', function () {
  94. var diff = deep.diff(lhs, {
  95. one: null
  96. });
  97. expect(diff).to.be.ok();
  98. expect(diff.length).to.be(1);
  99. expect(diff[0]).to.have.property('kind');
  100. expect(diff[0].kind).to.be('E');
  101. });
  102. it('shows the property as edited when compared to an array', function () {
  103. var diff = deep.diff(lhs, ['one']);
  104. expect(diff).to.be.ok();
  105. expect(diff.length).to.be(1);
  106. expect(diff[0]).to.have.property('kind');
  107. expect(diff[0].kind).to.be('E');
  108. });
  109. });
  110. describe('A target that has null value', function () {
  111. var lhs = {
  112. key: null
  113. };
  114. it('shows no differences when compared to itself', function () {
  115. expect(deep.diff(lhs, lhs)).to.be.an('undefined');
  116. });
  117. it('shows the property as removed when compared to an empty object', function () {
  118. var diff = deep.diff(lhs, empty);
  119. expect(diff).to.be.ok();
  120. expect(diff.length).to.be(1);
  121. expect(diff[0]).to.have.property('kind');
  122. expect(diff[0].kind).to.be('D');
  123. });
  124. it('shows the property is changed when compared to an object that has value', function () {
  125. var diff = deep.diff(lhs, {
  126. key: 'value'
  127. });
  128. expect(diff).to.be.ok();
  129. expect(diff.length).to.be(1);
  130. expect(diff[0]).to.have.property('kind');
  131. expect(diff[0].kind).to.be('E');
  132. });
  133. it('shows that an object property is changed when it is set to null', function () {
  134. lhs.key = {
  135. nested: 'value'
  136. };
  137. var diff = deep.diff(lhs, {
  138. key: null
  139. });
  140. expect(diff).to.be.ok();
  141. expect(diff.length).to.be(1);
  142. expect(diff[0]).to.have.property('kind');
  143. expect(diff[0].kind).to.be('E');
  144. });
  145. });
  146. describe('A target that has a date value', function () {
  147. var lhs = {
  148. key: new Date(555555555555)
  149. };
  150. it('shows the property is changed with a new date value', function () {
  151. var diff = deep.diff(lhs, {
  152. key: new Date(777777777777)
  153. });
  154. expect(diff).to.be.ok();
  155. expect(diff.length).to.be(1);
  156. expect(diff[0]).to.have.property('kind');
  157. expect(diff[0].kind).to.be('E');
  158. });
  159. });
  160. describe('A target that has a NaN', function () {
  161. var lhs = {
  162. key: NaN
  163. };
  164. it('shows the property is changed when compared to another number', function () {
  165. var diff = deep.diff(lhs, {
  166. key: 0
  167. });
  168. expect(diff).to.be.ok();
  169. expect(diff.length).to.be(1);
  170. expect(diff[0]).to.have.property('kind');
  171. expect(diff[0].kind).to.be('E');
  172. });
  173. it('shows no differences when compared to another NaN', function () {
  174. var diff = deep.diff(lhs, {
  175. key: NaN
  176. });
  177. expect(diff).to.be.an('undefined');
  178. });
  179. });
  180. describe('can revert namespace using noConflict', function () {
  181. if (deep.noConflict) {
  182. deep = deep.noConflict();
  183. it('conflict is restored (when applicable)', function () {
  184. // In node there is no global conflict.
  185. if (typeof globalConflict !== 'undefined') {
  186. expect(DeepDiff).to.be(deep); // eslint-disable-line no-undef
  187. }
  188. });
  189. it('DeepDiff functionality available through result of noConflict()', function () {
  190. expect(deep.applyDiff).to.be.a('function');
  191. });
  192. }
  193. });
  194. describe('When filtering keys', function () {
  195. var lhs = {
  196. enhancement: 'Filter/Ignore Keys?',
  197. numero: 11,
  198. submittedBy: 'ericclemmons',
  199. supportedBy: ['ericclemmons'],
  200. status: 'open'
  201. };
  202. var rhs = {
  203. enhancement: 'Filter/Ignore Keys?',
  204. numero: 11,
  205. submittedBy: 'ericclemmons',
  206. supportedBy: [
  207. 'ericclemmons',
  208. 'TylerGarlick',
  209. 'flitbit',
  210. 'ergdev'
  211. ],
  212. status: 'closed',
  213. fixedBy: 'flitbit'
  214. };
  215. describe('if the filtered property is an array', function () {
  216. it('changes to the array do not appear as a difference', function () {
  217. var prefilter = function (path, key) {
  218. return key === 'supportedBy';
  219. };
  220. var diff = deep(lhs, rhs, prefilter);
  221. expect(diff).to.be.ok();
  222. expect(diff.length).to.be(2);
  223. expect(diff[0]).to.have.property('kind');
  224. expect(diff[0].kind).to.be('E');
  225. expect(diff[1]).to.have.property('kind');
  226. expect(diff[1].kind).to.be('N');
  227. });
  228. });
  229. describe('if the filtered property is not an array', function () {
  230. it('changes do not appear as a difference', function () {
  231. var prefilter = function (path, key) {
  232. return key === 'fixedBy';
  233. };
  234. var diff = deep(lhs, rhs, prefilter);
  235. expect(diff).to.be.ok();
  236. expect(diff.length).to.be(4);
  237. expect(diff[0]).to.have.property('kind');
  238. expect(diff[0].kind).to.be('A');
  239. expect(diff[1]).to.have.property('kind');
  240. expect(diff[1].kind).to.be('A');
  241. expect(diff[2]).to.have.property('kind');
  242. expect(diff[2].kind).to.be('A');
  243. expect(diff[3]).to.have.property('kind');
  244. expect(diff[3].kind).to.be('E');
  245. });
  246. });
  247. });
  248. describe('A target that has nested values', function () {
  249. var nestedOne = {
  250. noChange: 'same',
  251. levelOne: {
  252. levelTwo: 'value'
  253. },
  254. arrayOne: [{
  255. objValue: 'value'
  256. }]
  257. };
  258. var nestedTwo = {
  259. noChange: 'same',
  260. levelOne: {
  261. levelTwo: 'another value'
  262. },
  263. arrayOne: [{
  264. objValue: 'new value'
  265. }, {
  266. objValue: 'more value'
  267. }]
  268. };
  269. it('shows no differences when compared to itself', function () {
  270. expect(deep.diff(nestedOne, nestedOne)).to.be.an('undefined');
  271. });
  272. it('shows the property as removed when compared to an empty object', function () {
  273. var diff = deep(nestedOne, empty);
  274. expect(diff).to.be.ok();
  275. expect(diff.length).to.be(3);
  276. expect(diff[0]).to.have.property('kind');
  277. expect(diff[0].kind).to.be('D');
  278. expect(diff[1]).to.have.property('kind');
  279. expect(diff[1].kind).to.be('D');
  280. });
  281. it('shows the property is changed when compared to an object that has value', function () {
  282. var diff = deep.diff(nestedOne, nestedTwo);
  283. expect(diff).to.be.ok();
  284. expect(diff.length).to.be(3);
  285. });
  286. it('shows the property as added when compared to an empty object on left', function () {
  287. var diff = deep.diff(empty, nestedOne);
  288. expect(diff).to.be.ok();
  289. expect(diff.length).to.be(3);
  290. expect(diff[0]).to.have.property('kind');
  291. expect(diff[0].kind).to.be('N');
  292. });
  293. describe('when diff is applied to a different empty object', function () {
  294. var diff = deep.diff(nestedOne, nestedTwo);
  295. it('has result with nested values', function () {
  296. var result = {};
  297. deep.applyChange(result, nestedTwo, diff[0]);
  298. expect(result.levelOne).to.be.ok();
  299. expect(result.levelOne).to.be.an('object');
  300. expect(result.levelOne.levelTwo).to.be.ok();
  301. expect(result.levelOne.levelTwo).to.eql('another value');
  302. });
  303. it('has result with array object values', function () {
  304. var result = {};
  305. deep.applyChange(result, nestedTwo, diff[2]);
  306. expect(result.arrayOne).to.be.ok();
  307. expect(result.arrayOne).to.be.an('array');
  308. expect(result.arrayOne[0]).to.be.ok();
  309. expect(result.arrayOne[0].objValue).to.be.ok();
  310. expect(result.arrayOne[0].objValue).to.equal('new value');
  311. });
  312. it('has result with added array objects', function () {
  313. var result = {};
  314. deep.applyChange(result, nestedTwo, diff[1]);
  315. expect(result.arrayOne).to.be.ok();
  316. expect(result.arrayOne).to.be.an('array');
  317. expect(result.arrayOne[1]).to.be.ok();
  318. expect(result.arrayOne[1].objValue).to.be.ok();
  319. expect(result.arrayOne[1].objValue).to.equal('more value');
  320. });
  321. });
  322. });
  323. describe('regression test for bug #10, ', function () {
  324. var lhs = {
  325. id: 'Release',
  326. phases: [{
  327. id: 'Phase1',
  328. tasks: [{
  329. id: 'Task1'
  330. }, {
  331. id: 'Task2'
  332. }]
  333. }, {
  334. id: 'Phase2',
  335. tasks: [{
  336. id: 'Task3'
  337. }]
  338. }]
  339. };
  340. var rhs = {
  341. id: 'Release',
  342. phases: [{
  343. // E: Phase1 -> Phase2
  344. id: 'Phase2',
  345. tasks: [{
  346. id: 'Task3'
  347. }]
  348. }, {
  349. id: 'Phase1',
  350. tasks: [{
  351. id: 'Task1'
  352. }, {
  353. id: 'Task2'
  354. }]
  355. }]
  356. };
  357. describe('differences in nested arrays are detected', function () {
  358. var diff = deep.diff(lhs, rhs);
  359. // there should be differences
  360. expect(diff).to.be.ok();
  361. expect(diff.length).to.be(6);
  362. it('differences can be applied', function () {
  363. var applied = deep.applyDiff(lhs, rhs);
  364. it('and the result equals the rhs', function () {
  365. expect(applied).to.eql(rhs);
  366. });
  367. });
  368. });
  369. });
  370. describe('regression test for bug #35', function () {
  371. var lhs = ['a', 'a', 'a'];
  372. var rhs = ['a'];
  373. it('can apply diffs between two top level arrays', function () {
  374. var differences = deep.diff(lhs, rhs);
  375. differences.forEach(function (it) {
  376. deep.applyChange(lhs, true, it);
  377. });
  378. expect(lhs).to.eql(['a']);
  379. });
  380. });
  381. describe('Objects from different frames', function () {
  382. if (typeof globalConflict === 'undefined') { return; }
  383. // eslint-disable-next-line no-undef
  384. var frame = document.createElement('iframe');
  385. // eslint-disable-next-line no-undef
  386. document.body.appendChild(frame);
  387. var lhs = new frame.contentWindow.Date(2010, 1, 1);
  388. var rhs = new frame.contentWindow.Date(2010, 1, 1);
  389. it('can compare date instances from a different frame', function () {
  390. var differences = deep.diff(lhs, rhs);
  391. expect(differences).to.be(undefined);
  392. });
  393. });
  394. describe('Comparing regexes should work', function () {
  395. var lhs = /foo/;
  396. var rhs = /foo/i;
  397. it('can compare regex instances', function () {
  398. var diff = deep.diff(lhs, rhs);
  399. expect(diff.length).to.be(1);
  400. expect(diff[0].kind).to.be('E');
  401. expect(diff[0].path).to.not.be.ok();
  402. expect(diff[0].lhs).to.be('/foo/');
  403. expect(diff[0].rhs).to.be('/foo/i');
  404. });
  405. });
  406. describe('subject.toString is not a function', function () {
  407. var lhs = {
  408. left: 'yes',
  409. right: 'no',
  410. };
  411. var rhs = {
  412. left: {
  413. toString: true,
  414. },
  415. right: 'no',
  416. };
  417. it('should not throw a TypeError', function () {
  418. var diff = deep.diff(lhs, rhs);
  419. expect(diff.length).to.be(1);
  420. });
  421. });
  422. describe('regression test for issue #83', function () {
  423. var lhs = {
  424. date: null
  425. };
  426. var rhs = {
  427. date: null
  428. };
  429. it('should not detect a difference', function () {
  430. expect(deep.diff(lhs, rhs)).to.be(undefined);
  431. });
  432. });
  433. describe('regression test for issue #70', function () {
  434. it('should detect a difference with undefined property on lhs', function () {
  435. var diff = deep.diff({ foo: undefined }, {});
  436. expect(diff).to.be.an(Array);
  437. expect(diff.length).to.be(1);
  438. expect(diff[0].kind).to.be('D');
  439. expect(diff[0].path).to.be.an('array');
  440. expect(diff[0].path).to.have.length(1);
  441. expect(diff[0].path[0]).to.be('foo');
  442. expect(diff[0].lhs).to.be(undefined);
  443. });
  444. it('should detect a difference with undefined property on rhs', function () {
  445. var diff = deep.diff({}, { foo: undefined });
  446. expect(diff).to.be.an(Array);
  447. expect(diff.length).to.be(1);
  448. expect(diff[0].kind).to.be('N');
  449. expect(diff[0].path).to.be.an('array');
  450. expect(diff[0].path).to.have.length(1);
  451. expect(diff[0].path[0]).to.be('foo');
  452. expect(diff[0].rhs).to.be(undefined);
  453. });
  454. });
  455. describe('regression test for issue #98', function () {
  456. var lhs = { foo: undefined };
  457. var rhs = { foo: undefined };
  458. it('should not detect a difference with two undefined property values', function () {
  459. var diff = deep.diff(lhs, rhs);
  460. expect(diff).to.be(undefined);
  461. });
  462. });
  463. describe('regression tests for issue #102', function () {
  464. it('should not throw a TypeError', function () {
  465. var diff = deep.diff(null, undefined);
  466. expect(diff).to.be.an(Array);
  467. expect(diff.length).to.be(1);
  468. expect(diff[0].kind).to.be('D');
  469. expect(diff[0].lhs).to.be(null);
  470. });
  471. it('should not throw a TypeError', function () {
  472. var diff = deep.diff(Object.create(null), { foo: undefined });
  473. expect(diff).to.be.an(Array);
  474. expect(diff.length).to.be(1);
  475. expect(diff[0].kind).to.be('N');
  476. expect(diff[0].rhs).to.be(undefined);
  477. });
  478. });
  479. describe('Order independent hash testing', function () {
  480. function sameHash(a, b) {
  481. expect(deep.orderIndepHash(a)).to.equal(deep.orderIndepHash(b));
  482. }
  483. function differentHash(a, b) {
  484. expect(deep.orderIndepHash(a)).to.not.equal(deep.orderIndepHash(b));
  485. }
  486. describe('Order indepdendent hash function should give different values for different objects', function () {
  487. it('should give different values for different "simple" types', function () {
  488. differentHash(1, -20);
  489. differentHash('foo', 45);
  490. differentHash('pie', 'something else');
  491. differentHash(1.3332, 1);
  492. differentHash(1, null);
  493. differentHash('this is kind of a long string, don\'t you think?', 'the quick brown fox jumped over the lazy doge');
  494. differentHash(true, 2);
  495. differentHash(false, 'flooog');
  496. });
  497. it('should give different values for string and object with string', function () {
  498. differentHash('some string', { key: 'some string' });
  499. });
  500. it('should give different values for number and array', function () {
  501. differentHash(1, [1]);
  502. });
  503. it('should give different values for string and array of string', function () {
  504. differentHash('string', ['string']);
  505. });
  506. it('should give different values for boolean and object with boolean', function () {
  507. differentHash(true, { key: true });
  508. });
  509. it('should give different values for different arrays', function () {
  510. differentHash([1, 2, 3], [1, 2]);
  511. differentHash([1, 4, 5, 6], ['foo', 1, true, undefined]);
  512. differentHash([1, 4, 6], [1, 4, 7]);
  513. differentHash([1, 3, 5], ['1', '3', '5']);
  514. });
  515. it('should give different values for different objects', function () {
  516. differentHash({ key: 'value' }, { other: 'value' });
  517. differentHash({ a: { b: 'c' } }, { a: 'b' });
  518. });
  519. it('should differentiate between arrays and objects', function () {
  520. differentHash([1, true, '1'], { a: 1, b: true, c: '1' });
  521. });
  522. });
  523. describe('Order independent hash function should work in pathological cases', function () {
  524. it('should work in funky javascript cases', function () {
  525. differentHash(undefined, null);
  526. differentHash(0, undefined);
  527. differentHash(0, null);
  528. differentHash(0, false);
  529. differentHash(0, []);
  530. differentHash('', []);
  531. differentHash(3.22, '3.22');
  532. differentHash(true, 'true');
  533. differentHash(false, 0);
  534. });
  535. it('should work on empty array and object', function () {
  536. differentHash([], {});
  537. });
  538. it('should work on empty object and undefined', function () {
  539. differentHash({}, undefined);
  540. });
  541. it('should work on empty array and array with 0', function () {
  542. differentHash([], [0]);
  543. });
  544. });
  545. describe('Order independent hash function should be order independent', function () {
  546. it('should not care about array order', function () {
  547. sameHash([1, 2, 3], [3, 2, 1]);
  548. sameHash(['hi', true, 9.4], [true, 'hi', 9.4]);
  549. });
  550. it('should not care about key order in an object', function () {
  551. sameHash({ foo: 'bar', foz: 'baz' }, { foz: 'baz', foo: 'bar' });
  552. });
  553. it('should work with complicated objects', function () {
  554. var obj1 = {
  555. foo: 'bar',
  556. faz: [
  557. 1,
  558. 'pie',
  559. {
  560. food: 'yum'
  561. }
  562. ]
  563. };
  564. var obj2 = {
  565. faz: [
  566. 'pie',
  567. {
  568. food: 'yum'
  569. },
  570. 1
  571. ],
  572. foo: 'bar'
  573. };
  574. sameHash(obj1, obj2);
  575. });
  576. });
  577. });
  578. describe('Order indepedent array comparison should work', function () {
  579. it('can compare simple arrays in an order independent fashion', function () {
  580. var lhs = [1, 2, 3];
  581. var rhs = [1, 3, 2];
  582. var diff = deep.orderIndependentDiff(lhs, rhs);
  583. expect(diff).to.be(undefined);
  584. });
  585. it('still works with repeated elements', function () {
  586. var lhs = [1, 1, 2];
  587. var rhs = [1, 2, 1];
  588. var diff = deep.orderIndependentDiff(lhs, rhs);
  589. expect(diff).to.be(undefined);
  590. });
  591. it('works on complex objects', function () {
  592. var obj1 = {
  593. foo: 'bar',
  594. faz: [
  595. 1,
  596. 'pie',
  597. {
  598. food: 'yum'
  599. }
  600. ]
  601. };
  602. var obj2 = {
  603. faz: [
  604. 'pie',
  605. {
  606. food: 'yum'
  607. },
  608. 1
  609. ],
  610. foo: 'bar'
  611. };
  612. var diff = deep.orderIndependentDiff(obj1, obj2);
  613. expect(diff).to.be(undefined);
  614. });
  615. it('should report some difference in non-equal arrays', function () {
  616. var lhs = [1, 2, 3];
  617. var rhs = [2, 2, 3];
  618. var diff = deep.orderIndependentDiff(lhs, rhs);
  619. expect(diff.length).to.be.ok();
  620. });
  621. });
  622. });
  623. }));