@itwin/core-backend
Version:
iTwin.js backend components
67 lines • 3.76 kB
JavaScript
/*---------------------------------------------------------------------------------------------
* Copyright (c) Bentley Systems, Incorporated. All rights reserved.
* See LICENSE.md in the project root for license terms and full copyright notice.
*--------------------------------------------------------------------------------------------*/
import { Assertion, util } from "chai";
import { Geometry } from "@itwin/core-geometry";
export const defaultOpts = {
tolerance: 1e-10,
considerNonExistingAndUndefinedEqual: false,
};
/** get whether two numbers are almost equal within a tolerance */
const isAlmostEqualNumber = (a, b, tol) => Geometry.isSameCoordinate(a, b, tol);
/** normalize a classname for comparisons */
const normalizeClassName = (name) => name.toLowerCase().replace(/:/, ".");
/**
* The diff shown on failure will show undefined fields as part of the diff even if
* consideringNonExistingAndUndefinedEqual is true. You can ignore that.
* We would have to mutate the object to clean the diff, although it is worth considering.
*/
export function advancedDeepEqual(e, a, options = {}) {
const normalizedClassNameProps = options.normalizeClassNameProps === true
? ["classFullName", "relClassName"]
: options.normalizeClassNameProps || [];
if (options.tolerance === undefined)
options.tolerance = defaultOpts.tolerance;
if (e === a)
return true;
if (typeof e !== typeof a)
return false;
switch (typeof e) {
case "number":
return isAlmostEqualNumber(e, a, options.tolerance);
case "string":
case "boolean":
case "function":
case "symbol":
case "undefined":
return false; // these objects can only be strict equal which was already tested
case "object":
if ((e === null) !== (a === null))
return false;
const eSize = Object.keys(e).filter((k) => options.considerNonExistingAndUndefinedEqual && e[k] !== undefined).length;
const aSize = Object.keys(a).filter((k) => options.considerNonExistingAndUndefinedEqual && a[k] !== undefined).length;
return (eSize === aSize || !!options.useSubsetEquality) && Object.keys(e).every((keyOfE) => (keyOfE in a || options.considerNonExistingAndUndefinedEqual) &&
normalizedClassNameProps.includes(keyOfE)
? advancedDeepEqual(normalizeClassName(e[keyOfE]), normalizeClassName(a[keyOfE]))
: advancedDeepEqual(e[keyOfE], a[keyOfE], options));
default: // bigint unhandled
throw Error(`unhandled deep compare type code returned from typeof, "${typeof e}"`);
}
}
Assertion.addMethod("advancedEqual", function advancedEqual(expected, options = {}) {
if (options.tolerance === undefined)
options.tolerance = 1e-10;
const actual = this._obj;
const isDeep = util.flag(this, "deep");
this.assert(isDeep
? advancedDeepEqual(expected, actual, options)
: isAlmostEqualNumber(expected, actual, options.tolerance), `expected ${isDeep ? "deep equality of " : " "}#{exp} and #{act} with a tolerance of ${options.tolerance}`, `expected ${isDeep ? "deep inequality of " : " "}#{exp} and #{act} with a tolerance of ${options.tolerance}`, expected, actual);
});
Assertion.addMethod("subsetEqual", function subsetEqual(expected, options = {}) {
if (options.tolerance === undefined)
options.tolerance = 1e-10;
const actual = this._obj;
this.assert(advancedDeepEqual(expected, actual, { ...options, useSubsetEquality: true }), `expected #{act} to contain as a subset #{exp}`, `expected #{act} not to contain as a subset #{exp}`, expected, actual);
});
//# sourceMappingURL=AdvancedEqual.js.map