/* * Copyright (c) 2022-2024 Huawei Device Co., Ltd. * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ import DeepTypeUtils from './DeepTypeUtils'; function assertDeepEquals(actualValue, expected) { let result = eq(actualValue, expected[0]); let msg = logMsg(actualValue, expected[0]); return { pass: result, message: msg }; } /** * 获取失败显示日志 * @param actualValue 实际对象 * @param expected 期待比较对象 */ function logMsg(actualValue, expected) { // 获取a的对象名称 const aClassName = Object.prototype.toString.call(actualValue); const bClassName = Object.prototype.toString.call(expected); let actualMsg; let expectMsg; if (aClassName == '[object Function]') { actualMsg = 'actualValue Function'; } else if (aClassName == '[object Promise]') { actualMsg = 'actualValue Promise'; } else if (aClassName == '[object Set]' || aClassName == '[object Map]') { actualMsg = JSON.stringify(Array.from(actualValue)); } else if (aClassName == '[object RegExp]') { actualMsg = JSON.stringify(actualValue.source.replace('\\', '')); } else if (aClassName == '[object BigInt]') { actualMsg = actualValue; } else if (aClassName == '[object Error]') { actualMsg = actualValue.message; } else if (aClassName == '[object ArrayBuffer]') { actualMsg = actualValue.byteLength; } else { actualMsg = JSON.stringify(actualValue); } if (bClassName == '[object Function]') { expectMsg = 'expected Function'; } else if (bClassName == '[object Promise]') { expectMsg = 'expected Promise'; } else if (bClassName == '[object Set]' || bClassName == '[object Map]') { expectMsg = JSON.stringify(Array.from(expected)); } else if (bClassName == '[object RegExp]') { expectMsg = JSON.stringify(expected.source.replace('\\', '')); } else if (bClassName == '[object BigInt]') { expectMsg = expected; } else if (bClassName == '[object Error]') { expectMsg = expected.message; } else if (bClassName == '[object ArrayBuffer]') { expectMsg = expected.byteLength; } else { expectMsg = JSON.stringify(expected); } return actualMsg + ' is not deep equal ' + expectMsg; } function eq(a, b) { let result = true; if (a === b) { result = a !== 0 || 1 / a === 1 / b; return result; } if (a === null || b === null) { result = a === b; return result; } // 获取a的对象名称 const aClassName = Object.prototype.toString.call(a); const bClassName = Object.prototype.toString.call(b); // 不同类型不同对象 if (aClassName !== bClassName) { return false; } if (aClassName === '[object String]' || aClassName === '[object Number]' || aClassName === '[object Date]' || aClassName === '[object Boolean]' || aClassName === '[object ArrayBuffer]' || aClassName === '[object RegExp]' || aClassName === '[object Error]') { result = isEqualSampleObj(a, b); return result; } if (typeof a !== 'object' || typeof b !== 'object') { return false; } if (DeepTypeUtils.isDomNode(a) || DeepTypeUtils.isPromise(a) || DeepTypeUtils.isFunction(a)) { result = isEqualNodeOrPromiseOrFunction(a, b); return result; } if (aClassName === '[object Array]' || aClassName === '[object Map]' || aClassName === '[object Set]') { result = isEqualCollection(a, b); return result; } result = isEqualObj(a, b); return result; } function isEqualNodeOrPromiseOrFunction(a, b) { let equalNodeOrPromiseOrFunction = true; if (DeepTypeUtils.isDomNode(a) && DeepTypeUtils.isDomNode(b)) { const aIsDomNode = DeepTypeUtils.isDomNode(a); const bIsDomNode = DeepTypeUtils.isDomNode(b); if (aIsDomNode && bIsDomNode) { // At first try to use DOM3 method isEqualNode equalNodeOrPromiseOrFunction = a.isEqualNode(b); return equalNodeOrPromiseOrFunction; } if (aIsDomNode || bIsDomNode) { equalNodeOrPromiseOrFunction = false; return false; } } if (DeepTypeUtils.isPromise(a) && DeepTypeUtils.isPromise(b)) { const aIsPromise = DeepTypeUtils.isPromise(a); const bIsPromise = DeepTypeUtils.isPromise(b); // 俩个Promise对象 if (aIsPromise && bIsPromise) { equalNodeOrPromiseOrFunction = a === b; return a === b; } } if (DeepTypeUtils.isFunction(a) && DeepTypeUtils.isFunction(b)) { // 俩个函数对象 const aCtor = a.constructor, bCtor = b.constructor; if ( aCtor !== bCtor && DeepTypeUtils.isFunction(aCtor) && DeepTypeUtils.isFunction(bCtor) && a instanceof aCtor && b instanceof bCtor && !(aCtor instanceof aCtor && bCtor instanceof bCtor) ) { equalNodeOrPromiseOrFunction = false; return false; } } return equalNodeOrPromiseOrFunction; } function isEqualCollection(a, b) { let equalCollection = true; // 获取a的对象名称 const aClassName = Object.prototype.toString.call(a); const bClassName = Object.prototype.toString.call(b); // 都是数组 if (aClassName === '[object Array]') { equalCollection = isEqualArray(a, b); return equalCollection; } // 都是Map if (DeepTypeUtils.isMap(a) && DeepTypeUtils.isMap(b)) { equalCollection = isEqualMap(a, b); return equalCollection; } // 都是Set if (DeepTypeUtils.isSet(a) && DeepTypeUtils.isSet(b)) { equalCollection = isEqualSet(a, b); return equalCollection; } return true; } function isEqualSampleObj(a, b) { let equalSampleObj = true; const aClassName = Object.prototype.toString.call(a); const bClassName = Object.prototype.toString.call(b); // 俩个string对象 if (aClassName === '[object String]') { equalSampleObj = a === String(b); return equalSampleObj; } // 俩个Number对象 if (aClassName === '[object Number]') { equalSampleObj = a !== +a ? b !== +b : a === 0 && b === 0 ? 1 / a === 1 / b : a === +b; return equalSampleObj; } // 俩个Date对象/ boolean对象 if (aClassName === '[object Date]' || aClassName === '[object Boolean]') { equalSampleObj = +a === +b; return equalSampleObj; } // 俩个ArrayBuffer if (aClassName === '[object ArrayBuffer]') { equalSampleObj = eq(new Uint8Array(a), new Uint8Array(b)); return equalSampleObj; } // 正则表达式 if (aClassName === '[object RegExp]') { return ( a.source === b.source && a.global === b.global && a.multiline === b.multiline && a.ignoreCase === b.ignoreCase ); } if (a instanceof Error && b instanceof Error) { equalSampleObj = a.message === b.message; return equalSampleObj; } return equalSampleObj; } function isEqualObj(a, b) { let equalObj = true; const aClassName = Object.prototype.toString.call(a); const bClassName = Object.prototype.toString.call(b); const aKeys = DeepTypeUtils.keys(a, aClassName === '[object Array]'); let size = aKeys.length; // 俩个对象属性长度不一致, 俩对象不相同 if (DeepTypeUtils.keys(b, bClassName === '[object Array]').length !== size) { return false; } // 俩对象属性数量相同, 递归比较每个属性值得值 for (const key of aKeys) { // b 没有 key 属性 if (!DeepTypeUtils.has(b, key)) { equalObj = false; continue; } if (!eq(a[key], b[key])) { equalObj = false; } } return equalObj; } function isEqualArray(a, b) { let equalArray = true; const aLength = a.length; const bLength = b.length; if (aLength !== bLength) { // 数组长度不同,不是同一个对象 return false; } for (let i = 0; i < aLength || i < bLength; i++) { // 递归每一个元素是否相同 equalArray = eq(i < aLength ? a[i] : void 0, i < bLength ? b[i] : void 0) && equalArray; } return equalArray; } function isEqualMap(a, b) { let equalMap = true; if (a.size !== b.size) { return false; } const keysA = []; const keysB = []; a.forEach(function(valueA, keyA) { keysA.push(keyA); }); b.forEach(function(valueB, keyB) { keysB.push(keyB); }); const mapKeys = [keysA, keysB]; const cmpKeys = [keysB, keysA]; for (let i = 0; equalMap && i < mapKeys.length; i++) { const mapIter = mapKeys[i]; const cmpIter = cmpKeys[i]; for (let j = 0; equalMap && j < mapIter.length; j++) { const mapKey = mapIter[j]; const cmpKey = cmpIter[j]; const mapValueA = a.get(mapKey); let mapValueB; if (eq(mapKey, cmpKey)) { mapValueB = b.get(cmpKey); } else { mapValueB = b.get(mapKey); } equalMap = eq(mapValueA, mapValueB); } } return equalMap; } function isEqualSet(a, b) { let equalSet = true; if (a.size !== b.size) { return false; } const valuesA = []; a.forEach(function(valueA) { valuesA.push(valueA); }); const valuesB = []; b.forEach(function(valueB) { valuesB.push(valueB); }); const setPairs = [[valuesA, valuesB], [valuesB, valuesA]]; for (let i = 0; equalSet && i < setPairs.length; i++) { const baseValues = setPairs[i][0]; const otherValues = setPairs[i][1]; for (const baseValue of baseValues) { let found = false; for (let j = 0; !found && j < otherValues.length; j++) { const otherValue = otherValues[j]; // 深度比较对象 found = eq(baseValue, otherValue); } equalSet = equalSet && found; } } return equalSet; } export default assertDeepEquals;