UNPKG

object-deep-compare

Version:

A type-safe collection of comparison methods for objects and arrays in TypeScript/JavaScript

359 lines (294 loc) 10.6 kB
import * as deepCompare from '../index'; import { ComparisonOptions } from '../src/types'; describe('Test CompareValuesWithConflicts method', () => { describe('Basic object comparison', () => { it('should detect value differences in objects', () => { const obj1 = { foo: 1 }; const obj2 = { foo: 2 }; const conflicts = deepCompare.CompareValuesWithConflicts(obj1, obj2); expect(conflicts).not.toBeNull(); if (conflicts) { expect(conflicts.length).toBe(1); expect(conflicts[0]).toBe('foo'); } }); it('should detect structural differences in objects', () => { const obj1 = { foo: 1, baz: { x: 1, y: 2 } }; const obj2 = { bar: 2, baz: { x: 1, z: 3 }, foo: 1 }; const conflicts = deepCompare.CompareValuesWithConflicts(obj1, obj2); expect(conflicts).not.toBeNull(); if (conflicts) { // The actual number depends on implementation details expect(conflicts.length).toBeGreaterThan(0); // Check for specific paths that should be identified as conflicts expect(conflicts.some(path => path.includes('baz'))).toBe(true); } }); it('should detect deeply nested differences', () => { const obj1 = { a: { b: { c: { d: 1 } } } }; const obj2 = { a: { b: { c: { d: 2 } } } }; const conflicts = deepCompare.CompareValuesWithConflicts(obj1, obj2); expect(conflicts).not.toBeNull(); if (conflicts) { expect(conflicts.length).toBeGreaterThan(0); expect(conflicts.some(path => path.includes('a.b.c.d'))).toBe(true); } }); it('should not detect non-existent differences', () => { const obj1 = { foo: 1, bar: { baz: true } }; const obj2 = { foo: 1, bar: { baz: true } }; const conflicts = deepCompare.CompareValuesWithConflicts(obj1, obj2); expect(conflicts).not.toBeNull(); if (conflicts) { expect(conflicts.length).toBe(0); } }); }); describe('Array comparison', () => { it('should detect differences in arrays', () => { const arr1 = [1, 2, 3]; const arr2 = [1, 2, 4]; const conflicts = deepCompare.CompareValuesWithConflicts(arr1, arr2); expect(conflicts).not.toBeNull(); if (conflicts) { expect(conflicts.length).toBeGreaterThan(0); // The implementation might handle array differences in different ways // Just check that it detects something } }); it('should handle arrays of different lengths', () => { const arr1 = [1, 2, 3]; const arr2 = [1, 2]; const conflicts = deepCompare.CompareValuesWithConflicts(arr1, arr2); expect(conflicts).not.toBeNull(); // Implementation may report this as a conflict or not // Just check that it doesn't crash }); }); describe('Mixed type comparison', () => { it('should detect when comparing different types', () => { const obj = { a: 1 }; const arr = [1, 2, 3]; const conflicts = deepCompare.CompareValuesWithConflicts(obj, arr); expect(conflicts).not.toBeNull(); if (conflicts) { expect(conflicts.length).toBeGreaterThan(0); // The actual path might vary based on implementation } }); it('should detect when comparing array vs object', () => { const arr = [1, 2, 3]; const obj = { 0: 1, 1: 2, 2: 3 }; const conflicts = deepCompare.CompareValuesWithConflicts(arr, obj); expect(conflicts).not.toBeNull(); if (conflicts) { expect(conflicts.length).toBeGreaterThan(0); // The actual path might vary based on implementation } }); it('should handle null and undefined according to implementation', () => { const obj1 = { a: null }; const obj2 = { a: undefined }; const conflicts = deepCompare.CompareValuesWithConflicts(obj1, obj2); expect(conflicts).not.toBeNull(); // The actual behavior depends on the implementation's handling of null vs undefined }); }); describe('With options', () => { it('should respect strict mode', () => { const obj1 = { a: 1, b: '2' }; const obj2 = { a: '1', b: 2 }; // With strict mode const strictConflicts = deepCompare.CompareValuesWithConflicts(obj1, obj2, '', { strict: true }); expect(strictConflicts).not.toBeNull(); if (strictConflicts) { expect(strictConflicts.length).toBeGreaterThan(0); } // Without strict mode const nonStrictConflicts = deepCompare.CompareValuesWithConflicts(obj1, obj2, '', { strict: false }); // In non-strict mode, there should be fewer conflicts expect(nonStrictConflicts.length).toBeLessThanOrEqual(strictConflicts.length); }); }); describe('Path filtering', () => { it('should exclude specified paths', () => { const obj1 = { a: 1, b: 2, c: { d: 3 } }; const obj2 = { a: 2, b: 2, c: { d: 4 } }; // Get all conflicts without filtering const allConflicts = deepCompare.CompareValuesWithConflicts(obj1, obj2); // Now exclude 'a' path const conflicts = deepCompare.CompareValuesWithConflicts(obj1, obj2, '', { pathFilter: { patterns: ['a'], mode: 'exclude' } }); expect(conflicts).not.toBeNull(); if (conflicts && allConflicts) { // There should be fewer conflicts when excluding a path expect(conflicts.length).toBeLessThan(allConflicts.length); expect(conflicts.includes('a')).toBe(false); } }); it('should include only specified paths', () => { const obj1 = { a: 1, b: 2, c: 3 }; const obj2 = { a: 2, b: 2, c: 4 }; const conflicts = deepCompare.CompareValuesWithConflicts(obj1, obj2, '', { pathFilter: { patterns: ['b'], mode: 'include' } }); expect(conflicts).not.toBeNull(); if (conflicts) { // Only paths including 'b' should be reported, and b is equal in both objects expect(conflicts.includes('a')).toBe(false); expect(conflicts.includes('c')).toBe(false); } }); }); describe('Date comparison', () => { it('should compare dates correctly', () => { const date1 = new Date('2023-01-01'); const date2 = new Date('2023-01-02'); const obj1 = { date1, date2 }; const obj2 = { date1, date2: new Date('2023-01-03') }; const conflicts = deepCompare.CompareValuesWithConflicts(obj1, obj2); expect(conflicts).not.toBeNull(); if (conflicts) { expect(conflicts.length).toBeGreaterThan(0); expect(conflicts.some(path => path.includes('date2'))).toBe(true); } }); }); describe('Circular references', () => { it('should throw an error when encountering circular references by default', () => { // Create circular references in objects const obj1: any = { a: 1, b: 2 }; obj1.self = obj1; // Self-reference const obj2: any = { a: 1, b: 2 }; obj2.self = obj2; // Self-reference expect(() => { deepCompare.CompareValuesWithConflicts(obj1, obj2); }).toThrow(/circular reference detected/i); }); it('should not report conflicts when circular references are identical and using ignore option', () => { // Create circular references in objects const obj1: any = { a: 1, b: 2 }; obj1.self = obj1; // Self-reference const obj2: any = { a: 1, b: 2 }; obj2.self = obj2; // Self-reference const options: ComparisonOptions = { circularReferences: 'ignore' }; const conflicts = deepCompare.CompareValuesWithConflicts(obj1, obj2, '', options); expect(conflicts).not.toBeNull(); if (conflicts) { expect(conflicts.length).toBe(0); } }); it('should report conflicts in objects with circular references when values differ', () => { // Create circular references in objects with different values const obj1: any = { a: 1, b: 2 }; obj1.self = obj1; const obj2: any = { a: 1, b: 3 }; // Different value for b obj2.self = obj2; const options: ComparisonOptions = { circularReferences: 'ignore' }; const conflicts = deepCompare.CompareValuesWithConflicts(obj1, obj2, '', options); expect(conflicts).not.toBeNull(); if (conflicts) { expect(conflicts.length).toBeGreaterThan(0); expect(conflicts.some(path => path.includes('b'))).toBe(true); } }); it('should handle complex nested objects with circular references', () => { // Create complex nested objects with circular references const obj1: any = { a: 1, b: { c: 3, d: 4 } }; obj1.b.parent = obj1; // Circular reference to parent const obj2: any = { a: 1, b: { c: 3, d: 4 } }; obj2.b.parent = obj2; // Circular reference to parent const options: ComparisonOptions = { circularReferences: 'ignore' }; const conflicts = deepCompare.CompareValuesWithConflicts(obj1, obj2, '', options); expect(conflicts).not.toBeNull(); if (conflicts) { expect(conflicts.length).toBe(0); } }); it('should handle mutual circular references between objects', () => { // Create objects with mutual circular references const objA: any = { name: 'A' }; const objB: any = { name: 'B' }; objA.ref = objB; objB.ref = objA; const objC: any = { name: 'A' }; const objD: any = { name: 'B' }; objC.ref = objD; objD.ref = objC; const options: ComparisonOptions = { circularReferences: 'ignore' }; const conflicts = deepCompare.CompareValuesWithConflicts(objA, objC, '', options); expect(conflicts).not.toBeNull(); if (conflicts) { expect(conflicts.length).toBe(0); // Should be equal when ignoring circular references } }); }); });