UNPKG

object-deep-compare

Version:

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

344 lines (292 loc) 10.4 kB
import * as deepCompare from '../index'; import { SchemaValidation, SchemaValidationResult } from '../src/types'; describe('Schema Validation', () => { // Sample objects for testing const user1 = { id: 1, name: 'John', email: 'john@example.com', age: 30, isActive: true }; const user2 = { id: 2, name: 'Jane', email: 'jane@example.com', age: 28, isActive: false }; const userSchema = { id: 'number', name: 'string', email: 'string', age: 'number', isActive: 'boolean' }; describe('Basic validation', () => { it('should validate objects against schema correctly', () => { const schemaValidation: SchemaValidation = { firstObjectSchema: userSchema, secondObjectSchema: userSchema }; const result = deepCompare.ValidateObjectsAgainstSchemas(user1, user2, schemaValidation); expect(result.firstObjectValid).toBe(true); expect(result.secondObjectValid).toBe(true); expect(result.firstObjectErrors).toBeUndefined(); expect(result.secondObjectErrors).toBeUndefined(); }); it('should detect type mismatches', () => { const userWithWrongTypes = { id: '5', // Should be number name: 123, // Should be string email: 'bob@example.com', age: 'thirty', // Should be number isActive: 1 // Should be boolean }; const schemaValidation: SchemaValidation = { firstObjectSchema: userSchema, secondObjectSchema: userSchema }; const result = deepCompare.ValidateObjectsAgainstSchemas(userWithWrongTypes, user2, schemaValidation); expect(result.firstObjectValid).toBe(false); expect(result.secondObjectValid).toBe(true); expect(result.firstObjectErrors?.length).toBeGreaterThan(0); expect(result.firstObjectErrors?.some(err => err.includes('id'))).toBe(true); expect(result.firstObjectErrors?.some(err => err.includes('name'))).toBe(true); expect(result.firstObjectErrors?.some(err => err.includes('age'))).toBe(true); }); it('should detect missing properties', () => { const incompleteUser = { id: 3, name: 'Bob', // Missing email, age, and isActive }; const schemaValidation: SchemaValidation = { firstObjectSchema: userSchema, secondObjectSchema: userSchema }; const result = deepCompare.ValidateObjectsAgainstSchemas(incompleteUser, user2, schemaValidation); expect(result.firstObjectValid).toBe(false); expect(result.secondObjectValid).toBe(true); expect(result.firstObjectErrors?.some(err => err.includes('email'))).toBe(true); expect(result.firstObjectErrors?.some(err => err.includes('age'))).toBe(true); expect(result.firstObjectErrors?.some(err => err.includes('isActive'))).toBe(true); }); it('should throw error when configured to do so', () => { const invalidUser = { id: 'invalid', // Should be number name: 123 // Should be string }; const schemaValidation: SchemaValidation = { firstObjectSchema: userSchema, secondObjectSchema: userSchema, throwOnValidationFailure: true }; expect(() => { deepCompare.ValidateObjectsAgainstSchemas(invalidUser, user2, schemaValidation); }).toThrow('Schema validation failed'); }); }); describe('Complex validations', () => { it('should validate nested objects', () => { const userWithAddress = { id: 1, name: 'John', email: 'john@example.com', address: { street: '123 Main St', city: 'Anytown', zipCode: 12345 } }; const userWithInvalidAddress = { id: 2, name: 'Jane', email: 'jane@example.com', address: { street: 123, // Should be string city: 'Anytown', zipCode: '12345' // Should be number } }; const nestedSchema = { id: 'number', name: 'string', email: 'string', address: { street: 'string', city: 'string', zipCode: 'number' } }; const schemaValidation: SchemaValidation = { firstObjectSchema: nestedSchema, secondObjectSchema: nestedSchema }; const result = deepCompare.ValidateObjectsAgainstSchemas( userWithAddress, userWithInvalidAddress, schemaValidation ); expect(result.firstObjectValid).toBe(true); expect(result.secondObjectValid).toBe(false); expect(result.secondObjectErrors?.some(err => err.includes('address.street'))).toBe(true); expect(result.secondObjectErrors?.some(err => err.includes('address.zipCode'))).toBe(true); }); it('should validate arrays in objects', () => { const userWithArray = { id: 1, name: 'John', tags: ['admin', 'developer'] }; const userWithInvalidArray = { id: 2, name: 'Jane', tags: [1, 2, 3] // Should be strings }; const arraySchema = { id: 'number', name: 'string', tags: 'array<string>' }; const schemaValidation: SchemaValidation = { firstObjectSchema: arraySchema, secondObjectSchema: arraySchema }; const result = deepCompare.ValidateObjectsAgainstSchemas( userWithArray, userWithInvalidArray, schemaValidation ); expect(result.firstObjectValid).toBe(true); expect(result.secondObjectValid).toBe(false); expect(result.secondObjectErrors?.some(err => err.includes('tags'))).toBe(true); }); it('should validate complex nested arrays and objects', () => { const complexObject = { users: [ { id: '1', name: 'User 1', roles: ['admin', 'user'] }, { id: '2', name: 'User 2', roles: ['user'] }, ], config: { version: 1, settings: { enabled: true, timeout: 30 } } }; const invalidComplexObject = { users: [ { id: '1', name: 'User 1', roles: ['admin', 'user'] }, { id: 2, name: 'User 2', roles: [123] } // Invalid id type and role type ], config: { version: '1', // Should be number settings: { enabled: 1, // Should be boolean timeout: '30' // Should be number } } }; const complexSchema = { users: [{ id: 'string', name: 'string', roles: 'array<string>' }], config: { version: 'number', settings: { enabled: 'boolean', timeout: 'number' } } }; const schemaValidation: SchemaValidation = { firstObjectSchema: complexSchema, secondObjectSchema: complexSchema }; const result = deepCompare.ValidateObjectsAgainstSchemas( complexObject, invalidComplexObject, schemaValidation ); expect(result.firstObjectValid).toBe(true); expect(result.secondObjectValid).toBe(false); expect(result.secondObjectErrors?.length).toBeGreaterThan(3); }); }); describe('Edge cases', () => { it('should handle null and undefined objects', () => { const schemaValidation: SchemaValidation = { firstObjectSchema: userSchema, secondObjectSchema: userSchema }; const result1 = deepCompare.ValidateObjectsAgainstSchemas(null, user2, schemaValidation); expect(result1.firstObjectValid).toBe(false); expect(result1.secondObjectValid).toBe(true); expect(result1.firstObjectErrors?.[0]).toBe('First object is null or undefined'); const result2 = deepCompare.ValidateObjectsAgainstSchemas(user1, undefined, schemaValidation); expect(result2.firstObjectValid).toBe(true); expect(result2.secondObjectValid).toBe(false); expect(result2.secondObjectErrors?.[0]).toBe('Second object is null or undefined'); }); it('should handle empty schemas', () => { const schemaValidation: SchemaValidation = { firstObjectSchema: {}, secondObjectSchema: {} }; const result = deepCompare.ValidateObjectsAgainstSchemas(user1, user2, schemaValidation); expect(result.firstObjectValid).toBe(true); expect(result.secondObjectValid).toBe(true); }); it('should handle any type specification', () => { const mixedObject = { id: 1, dynamic: "string value", // Could be any type config: { setting: true, value: 42 } }; const anyTypeSchema = { id: 'number', dynamic: 'any', // Accept any type config: { setting: 'boolean', value: 'any' // Accept any type } }; const schemaValidation: SchemaValidation = { firstObjectSchema: anyTypeSchema }; const result = deepCompare.ValidateObjectsAgainstSchemas(mixedObject, {}, schemaValidation); expect(result.firstObjectValid).toBe(true); }); }); describe('Integration with comparison functions', () => { it('should validate before comparison and report differences', () => { const baseUser = { ...user1 }; const modifiedUser = { ...user1, name: 'Modified Name' }; const schemaValidation: SchemaValidation = { firstObjectSchema: userSchema, secondObjectSchema: userSchema }; const options = { schemaValidation }; // Test with CompareValuesWithConflicts const conflicts = deepCompare.CompareValuesWithConflicts(baseUser, modifiedUser, '', options); expect(conflicts).not.toBeNull(); expect(conflicts).toContain('name'); // Test with CompareValuesWithDetailedDifferences const details = deepCompare.CompareValuesWithDetailedDifferences(baseUser, modifiedUser, '', options); expect(details).not.toBeNull(); expect(details.some(d => d.path === 'name')).toBe(true); // Test with CompareArrays const arr1 = [{ ...user1 }, { ...user2 }]; const arr2 = [{ ...user1 }, { ...user2 }]; const arrResult = deepCompare.CompareArrays(arr1, arr2, options); expect(arrResult).toBe(true); }); }); });