• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1/*
2 * Copyright (c) 2022-2024 Huawei Device Co., Ltd.
3 * Licensed under the Apache License, Version 2.0 (the "License");
4 * you may not use this file except in compliance with the License.
5 * You may obtain a copy of the License at
6 *
7 *     http://www.apache.org/licenses/LICENSE-2.0
8 *
9 * Unless required by applicable law or agreed to in writing, software
10 * distributed under the License is distributed on an "AS IS" BASIS,
11 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 * See the License for the specific language governing permissions and
13 * limitations under the License.
14 */
15
16import DeepTypeUtils from './DeepTypeUtils';
17function assertDeepEquals(actualValue, expected) {
18    let result = eq(actualValue, expected[0]);
19    let msg = logMsg(actualValue, expected[0]);
20    return {
21        pass: result,
22        message: msg
23    };
24}
25
26function getMapLog(item) {
27    let valueStr = '';
28    let keyValue = '';
29    if (item.length > 1) {
30        let key = item[0];
31        let value = item[1];
32        if (value !== value) {
33            valueStr = value;
34        } else {
35            valueStr = JSON.stringify(value);
36        }
37        keyValue = JSON.stringify(key);
38    }
39    return '[' + keyValue + ',' + valueStr + ']';
40}
41
42function getArrayLog(item) {
43    // NAN
44    if (item !== item) {
45        return item;
46    }
47    if (item === undefined) {
48        return 'undefined';
49    }
50    return JSON.stringify(item);
51}
52
53
54function getCollectionLog(data) {
55    // 获取a的对象名称
56    let finallyResult = '';
57    const aClassName = Object.prototype.toString.call(data);
58    if (aClassName === '[object Map]') {
59        let result = Array.from(data);
60        finallyResult = result.flatMap((item) => {
61            return getMapLog(item);
62        });
63    }
64    if (aClassName === '[object Set]') {
65        let setArray = Array.from(data);
66        finallyResult = setArray.flatMap((item) => {
67            return getArrayLog(item);
68        });
69    }
70    if (aClassName === '[object Array]') {
71        finallyResult = data.flatMap((item) => {
72            return getArrayLog(item);
73        });
74    }
75    return finallyResult;
76}
77
78function getActualValueLog(actualValue) {
79    // 获取a的对象名称
80    const aClassName = Object.prototype.toString.call(actualValue);
81    let actualMsg = '';
82    if (aClassName === '[object Function]') {
83        actualMsg = 'actualValue Function';
84    } else if (aClassName === '[object Promise]') {
85        actualMsg = 'actualValue Promise';
86    } else if (aClassName === '[object Map]') {
87        let finallyResult = getCollectionLog(actualValue);
88        actualMsg = '[' + finallyResult + ']';
89    } else if (aClassName === '[object Set]') {
90        let flatMapResult = getCollectionLog(actualValue);
91        actualMsg = '[' + flatMapResult + ']';
92    } else if (aClassName === '[object Array]') {
93        let flatMapResult = getCollectionLog(actualValue);
94        actualMsg = '[' + flatMapResult + ']';
95    } else if (aClassName === '[object RegExp]') {
96        actualMsg = JSON.stringify(actualValue.source.replace('\\', ''));
97    } else if (aClassName === '[object BigInt]') {
98        actualMsg = actualValue;
99    } else if (aClassName === '[object Error]') {
100        actualMsg = actualValue.message;
101    } else if (aClassName === '[object ArrayBuffer]') {
102        actualMsg = actualValue.byteLength;
103    }
104    else {
105        // NAN
106        if (actualValue !== actualValue) {
107            actualMsg = actualValue.toString();
108        } else {
109            actualMsg = JSON.stringify(actualValue);
110        }
111    }
112    return actualMsg;
113}
114
115function getExpectedLog(expected) {
116    const bClassName = Object.prototype.toString.call(expected);
117    let expectMsg = '';
118    if (bClassName === '[object Function]') {
119        expectMsg = 'expected Function';
120    } else if (bClassName === '[object Promise]') {
121        expectMsg = 'expected Promise';
122    } else if (bClassName === '[object Map]') {
123        let finallyResult = getCollectionLog(expected);
124        expectMsg = '[' + finallyResult + ']';
125    } else if (bClassName === '[object Set]') {
126        let flatMapResult = getCollectionLog(expected);
127        expectMsg = '[' + flatMapResult + ']';
128    } else if (bClassName === '[object Array]') {
129        let flatMapResult = getCollectionLog(expected);
130        expectMsg = '[' + flatMapResult + ']';
131    } else if (bClassName === '[object RegExp]') {
132        expectMsg = JSON.stringify(expected.source.replace('\\', ''));
133    } else if (bClassName === '[object BigInt]') {
134        expectMsg = expected;
135    } else if (bClassName === '[object Error]') {
136        expectMsg = expected.message;
137    } else if (bClassName === '[object ArrayBuffer]') {
138        expectMsg = expected.byteLength;
139    }
140    else {
141        // NAN
142        if (expected !== expected) {
143            expectMsg = expected.toString();
144        } else {
145            expectMsg = JSON.stringify(expected);
146        }
147    }
148    return expectMsg;
149}
150
151/**
152 * 获取失败显示日志
153 * @param actualValue 实际对象
154 * @param expected 期待比较对象
155 */
156function logMsg(actualValue, expected) {
157    // 获取a的对象名称
158    let actualMsg = getActualValueLog(actualValue);
159    let expectMsg = getExpectedLog(expected);
160
161    return 'expect ' + actualMsg + ' deep equals ' + expectMsg;
162}
163
164function eq(a, b) {
165    let result = true;
166
167    if (a === b) {
168        result = a !== 0 || 1 / a === 1 / b;
169        return result;
170    }
171
172    if (a === null || b === null) {
173        result = a === b;
174        return result;
175    }
176    // 获取a的对象名称
177    const aClassName = Object.prototype.toString.call(a);
178    const bClassName = Object.prototype.toString.call(b);
179    // 不同类型不同对象
180    if (aClassName !== bClassName) {
181        return false;
182    }
183    if (aClassName === '[object String]' || aClassName === '[object Number]' || aClassName === '[object Date]' ||
184        aClassName === '[object Boolean]' || aClassName === '[object ArrayBuffer]' ||
185        aClassName === '[object RegExp]' || aClassName === '[object Error]') {
186        result = isEqualSampleObj(a, b);
187        return result;
188    }
189
190    if (typeof a !== 'object' || typeof b !== 'object') {
191        return false;
192    }
193
194    if (DeepTypeUtils.isDomNode(a) || DeepTypeUtils.isPromise(a) || DeepTypeUtils.isFunction(a)) {
195        result = isEqualNodeOrPromiseOrFunction(a, b);
196        return result;
197    }
198
199    if (aClassName === '[object Array]' || aClassName === '[object Map]' || aClassName === '[object Set]') {
200        result = isEqualCollection(a, b);
201        return result;
202    }
203
204    result = isEqualObj(a, b);
205    return result;
206}
207
208function isEqualNodeOrPromiseOrFunction(a, b) {
209    let equalNodeOrPromiseOrFunction = true;
210    if (DeepTypeUtils.isDomNode(a) && DeepTypeUtils.isDomNode(b)) {
211        const aIsDomNode = DeepTypeUtils.isDomNode(a);
212        const bIsDomNode = DeepTypeUtils.isDomNode(b);
213        if (aIsDomNode && bIsDomNode) {
214            // At first try to use DOM3 method isEqualNode
215            equalNodeOrPromiseOrFunction = a.isEqualNode(b);
216            return equalNodeOrPromiseOrFunction;
217        }
218        if (aIsDomNode || bIsDomNode) {
219            equalNodeOrPromiseOrFunction = false;
220            return false;
221        }
222    }
223
224    if (DeepTypeUtils.isPromise(a) && DeepTypeUtils.isPromise(b)) {
225        const aIsPromise = DeepTypeUtils.isPromise(a);
226        const bIsPromise = DeepTypeUtils.isPromise(b);
227        // 俩个Promise对象
228        if (aIsPromise && bIsPromise) {
229            equalNodeOrPromiseOrFunction = a === b;
230            return a === b;
231        }
232    }
233    if (DeepTypeUtils.isFunction(a) && DeepTypeUtils.isFunction(b)) {
234        // 俩个函数对象
235        const aCtor = a.constructor,
236            bCtor = b.constructor;
237        if (
238            aCtor !== bCtor &&
239            DeepTypeUtils.isFunction(aCtor) &&
240            DeepTypeUtils.isFunction(bCtor) &&
241                a instanceof aCtor &&
242                b instanceof bCtor &&
243                !(aCtor instanceof aCtor && bCtor instanceof bCtor)
244        ) {
245            equalNodeOrPromiseOrFunction = false;
246            return false;
247        }
248    }
249    return equalNodeOrPromiseOrFunction;
250}
251
252function isEqualCollection(a, b) {
253    let equalCollection = true;
254    // 获取a的对象名称
255    const aClassName = Object.prototype.toString.call(a);
256    const bClassName = Object.prototype.toString.call(b);
257    // 都是数组
258    if (aClassName === '[object Array]') {
259        equalCollection = isEqualArray(a, b);
260        return equalCollection;
261    }
262
263    // 都是Map
264    if (DeepTypeUtils.isMap(a) && DeepTypeUtils.isMap(b)) {
265        equalCollection = isEqualMap(a, b);
266        return equalCollection;
267    }
268
269    // 都是Set
270    if (DeepTypeUtils.isSet(a) && DeepTypeUtils.isSet(b)) {
271        equalCollection = isEqualSet(a, b);
272        return equalCollection;
273    }
274
275    return true;
276}
277
278function isEqualSampleObj(a, b) {
279    let equalSampleObj = true;
280    const aClassName = Object.prototype.toString.call(a);
281    const bClassName = Object.prototype.toString.call(b);
282    // 俩个string对象
283    if (aClassName === '[object String]') {
284        equalSampleObj = a === String(b);
285        return equalSampleObj;
286    }
287    // 俩个Number对象
288    if (aClassName === '[object Number]') {
289        // NAN
290        if (a !== a && b !== b) {
291            return a === b;
292        }
293        equalSampleObj = a !== +a ? b !== +b : a === 0 && b === 0 ? 1 / a === 1 / b : a === +b;
294        return equalSampleObj;
295    }
296
297    // 俩个Date对象/ boolean对象
298    if (aClassName === '[object Date]' || aClassName === '[object Boolean]') {
299        equalSampleObj = +a === +b;
300        return equalSampleObj;
301    }
302
303    // 俩个ArrayBuffer
304    if (aClassName === '[object ArrayBuffer]') {
305        equalSampleObj = eq(new Uint8Array(a), new Uint8Array(b));
306        return equalSampleObj;
307    }
308
309    // 正则表达式
310    if (aClassName === '[object RegExp]') {
311        return (
312            a.source === b.source &&
313                a.global === b.global &&
314                a.multiline === b.multiline &&
315                a.ignoreCase === b.ignoreCase
316        );
317    }
318
319    if (a instanceof Error && b instanceof Error) {
320        equalSampleObj = a.message === b.message;
321        return equalSampleObj;
322    }
323
324    return equalSampleObj;
325}
326
327function isEqualObj(a, b) {
328    let equalObj = true;
329    const aClassName = Object.prototype.toString.call(a);
330    const bClassName = Object.prototype.toString.call(b);
331    const aKeys = DeepTypeUtils.keys(a, aClassName === '[object Array]');
332    let size = aKeys.length;
333
334    // 俩个对象属性长度不一致, 俩对象不相同
335    if (DeepTypeUtils.keys(b, bClassName === '[object Array]').length !== size) {
336        return false;
337    }
338
339    // 俩对象属性数量相同, 递归比较每个属性值得值
340    for (const key of aKeys) {
341        // b 没有 key 属性
342        if (!DeepTypeUtils.has(b, key)) {
343            equalObj = false;
344            continue;
345        }
346        if (!eq(a[key], b[key])) {
347            equalObj = false;
348        }
349    }
350    return equalObj;
351}
352
353function isEqualArray(a, b) {
354    let equalArray = true;
355    const aLength = a.length;
356    const bLength = b.length;
357    if (aLength !== bLength) {
358        // 数组长度不同,不是同一个对象
359        return false;
360    }
361    for (let i = 0; i < aLength || i < bLength; i++) {
362        // 递归每一个元素是否相同
363        equalArray = eq(i < aLength ? a[i] : void 0, i < bLength ? b[i] : void 0) && equalArray;
364    }
365    return equalArray;
366}
367
368function isEqualMap(a, b) {
369    let equalMap = true;
370    if (a.size !== b.size) {
371        return false;
372    }
373    const keysA = [];
374    const keysB = [];
375    a.forEach(function(valueA, keyA) {
376        keysA.push(keyA);
377    });
378    b.forEach(function(valueB, keyB) {
379        keysB.push(keyB);
380    });
381    const mapKeys = [keysA, keysB];
382    const cmpKeys = [keysB, keysA];
383    for (let i = 0; equalMap && i < mapKeys.length; i++) {
384        const mapIter = mapKeys[i];
385        const cmpIter = cmpKeys[i];
386
387        for (let j = 0; equalMap && j < mapIter.length; j++) {
388            const mapKey = mapIter[j];
389            const cmpKey = cmpIter[j];
390            const mapValueA = a.get(mapKey);
391            let mapValueB;
392            if (eq(mapKey, cmpKey)) {
393                mapValueB = b.get(cmpKey);
394            } else {
395                mapValueB = b.get(mapKey);
396            }
397            equalMap = eq(mapValueA, mapValueB);
398        }
399    }
400    return equalMap;
401}
402
403function isEqualSet(a, b) {
404    let equalSet = true;
405    if (a.size !== b.size) {
406        return false;
407    }
408    const valuesA = [];
409    a.forEach(function(valueA) {
410        valuesA.push(valueA);
411    });
412    const valuesB = [];
413    b.forEach(function(valueB) {
414        valuesB.push(valueB);
415    });
416    const setPairs = [[valuesA, valuesB], [valuesB, valuesA]];
417    for (let i = 0; equalSet && i < setPairs.length; i++) {
418        const baseValues = setPairs[i][0];
419        const otherValues = setPairs[i][1];
420        for (const baseValue of baseValues) {
421            let found = false;
422            for (let j = 0; !found && j < otherValues.length; j++) {
423                const otherValue = otherValues[j];
424                // 深度比较对象
425                found = eq(baseValue, otherValue);
426            }
427            equalSet = equalSet && found;
428        }
429    }
430    return equalSet;
431}
432
433export default assertDeepEquals;
434