diff --git a/process/UnitTest_lib/process.js b/process/UnitTest_lib/process.js index 382d37e021567c924734a1350a7a368acd1796d3..210ab48b8f435e633a665feed0a9dd766630d990 100644 --- a/process/UnitTest_lib/process.js +++ b/process/UnitTest_lib/process.js @@ -261,7 +261,7 @@ Tester.prototype.equals = function(pExpect, pCustomDescription) if(Utils.isObject(this.actualValue) || Utils.isObject(this.expectedValue)) { - this.expectedDisplayValue = JSON.stringify(this.actualValue); + this.expectedDisplayValue = JSON.stringify(this.actualValue, _getCircularReplacer()); this._testResult = Utils.isEqual(this.actualValue, this.expectedValue); this._generateAssertDescription({custom: pCustomDescription, operator: "===", name: "Object value"}); } @@ -272,6 +272,24 @@ Tester.prototype.equals = function(pExpect, pCustomDescription) } return this; + + //custom replacer that supports cyclic references + function _getCircularReplacer () + { + var seen = new WeakSet(); + return function (key, value) + { + if (typeof value === "object" && value !== null) + { + if (seen.has(value)) + { + return "{...}"; + } + seen.add(value); + } + return value; + } + } } /** @@ -1202,7 +1220,7 @@ Tester.prototype._generateTestTitle = function(pTest, pDataProviderIndex) var titleValues = []; this.dataProvider[pDataProviderIndex].forEach(function(pElement) { - titleValues.push(pElement === undefined ? "undefined" : JSON.stringify(pElement)); + titleValues.push(pElement === undefined ? "undefined" : JSON.stringify(pElement, _getCircularReplacer())); }); this._log("", this.t.info("\t\u2699 Test: " + pTest.name) + this.t.debug(" (#" + (pDataProviderIndex + 1) + ": " + titleValues.join(" | ") + ")")); } @@ -1210,6 +1228,24 @@ Tester.prototype._generateTestTitle = function(pTest, pDataProviderIndex) { this._log("info", "\t\u2699 Test: " + pTest.name); } + + //custom replacer that supports cyclic references + function _getCircularReplacer () + { + var seen = new WeakSet(); + return function (key, value) + { + if (typeof value === "object" && value !== null) + { + if (seen.has(value)) + { + return "{...}"; + } + seen.add(value); + } + return value; + } + } } /** diff --git a/process/Util_lib/process.js b/process/Util_lib/process.js index 8ea572643943a2724f7a323e207122ff530957de..4ef286fe09d3ed55ed6394c8b950ed650c71c67c 100644 --- a/process/Util_lib/process.js +++ b/process/Util_lib/process.js @@ -100,7 +100,7 @@ Utils.isNotNullOrEmptyString = function (pValue) */ Utils.clone = function (pObject) { - var referenceMap = new Map(); + var referenceMap = new WeakMap(); return _clone(pObject); function _clone (pObject) @@ -173,35 +173,62 @@ Utils.clone = function (pObject) */ Utils.isEqual = function (pFirstObject, pSecondObject) { - var firstType = typeof pFirstObject; - var secondType = typeof pSecondObject; - if (firstType !== secondType) - return false; - if (firstType === "object" && pFirstObject !== null && pSecondObject !== null) //check for null because typeof null is also "object" + var comparedObjects = new WeakMap(); + return _isEqual(pFirstObject, pSecondObject); + + function _isEqual (pFirstObject, pSecondObject) { - var isFirstArray = Array.isArray(pFirstObject); - var isSecondArray = Array.isArray(pSecondObject); - if (isFirstArray !== isSecondArray) //return false if only one object is an array - return false; - if (isFirstArray && pFirstObject.length !== pSecondObject.length) + var firstType = typeof pFirstObject; + var secondType = typeof pSecondObject; + if (firstType !== secondType) + { return false; - - for (let key in pSecondObject) + } + if (firstType === "object" && pFirstObject !== null && pSecondObject !== null) //check for null because typeof null is also "object" { - if (!(key in pFirstObject)) + //All object comparisons are saved in comparedObjects, this makes it possible to detect recursions. + //If two objects have been checked for equality already, it can be safely assumed they are equal, + //because if they weren't, this function would have returned false already + if (comparedObjects.get(pFirstObject) === pSecondObject) + { + return true; + } + comparedObjects.set(pFirstObject, pSecondObject); + + var isFirstArray = Array.isArray(pFirstObject); + var isSecondArray = Array.isArray(pSecondObject); + if (isFirstArray !== isSecondArray) //return false if only one object is an array + { + return false; + } + if (isFirstArray && pFirstObject.length !== pSecondObject.length) + { return false; + } + + for (let key in pSecondObject) + { + if (!(key in pFirstObject)) + { + return false; + } + } + for (let key in pFirstObject) + { + if (!(key in pSecondObject) || !_isEqual(pFirstObject[key], pSecondObject[key])) + { + return false; + } + } + return true; } - for (let key in pFirstObject) + if (firstType === "number" && Number.isNaN(pFirstObject) && Number.isNaN(pSecondObject)) //NaN should be equal to NaN { - if (!(key in pSecondObject) || !Utils.isEqual(pFirstObject[key], pSecondObject[key])) - return false; + return true; } - return true; + + return pFirstObject === pSecondObject; } - if (firstType === "number" && Number.isNaN(pFirstObject) && Number.isNaN(pSecondObject)) //NaN should be equal to NaN - return true; - - return pFirstObject === pSecondObject; } /** diff --git a/process/Utils_test/Utils_test.aod b/process/Utils_test/Utils_test.aod new file mode 100644 index 0000000000000000000000000000000000000000..f877069734b9b8465243129b62e612e9f887887a --- /dev/null +++ b/process/Utils_test/Utils_test.aod @@ -0,0 +1,11 @@ +<?xml version="1.0" encoding="UTF-8"?> +<process xmlns="http://www.adito.de/2018/ao/Model" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" VERSION="1.2.2" xsi:schemaLocation="http://www.adito.de/2018/ao/Model adito://models/xsd/process/1.2.2"> + <name>Utils_test</name> + <title>[TEST] Util_lib</title> + <majorModelMode>DISTRIBUTED</majorModelMode> + <icon>VAADIN:CHECK_CIRCLE</icon> + <process>%aditoprj%/process/Utils_test/process.js</process> + <variants> + <element>EXECUTABLE</element> + </variants> +</process> diff --git a/process/Utils_test/process.js b/process/Utils_test/process.js new file mode 100644 index 0000000000000000000000000000000000000000..3a094ad4dcb4e17784be072b5c6089ac636b2d8a --- /dev/null +++ b/process/Utils_test/process.js @@ -0,0 +1,542 @@ +import("Util_lib"); +import("system.result"); +import("system.vars"); +import("UnitTest_lib"); + +var isEmpty = new TestSuite("Utils.isEmpty", [ + new Test("should test if an object, string or array is empty", + function (pTester, pDataProvider) + { + var expectValue = pDataProvider[0]; + var actualValue = Utils.isEmpty(pDataProvider[1]); + + pTester.expectThat(actualValue).equals(expectValue).assert(); + }, + function dataProvider() + { + return [ + [true, ""], + [true, []], + [true, {}], + [true, new Set()], + [true, new Map()], + [true, function () {}], + [false, "never gonna"], + [false, ["give"]], + [false, {you: "up"}], + [false, new Set(["never gonna"])], + [false, new Map([["let", "you down"]])] + ]; + } + ) +]); + +var isNullOrEmpty = new TestSuite("Utils.isNullOrEmpty", [ + new Test("should test if value is null, undefined or empty", + function (pTester, pDataProvider) + { + var expectValue = pDataProvider[0]; + var actualValue = Utils.isNullOrEmpty(pDataProvider[1]); + + pTester.expectThat(actualValue).equals(expectValue).assert(); + }, + function dataProvider() + { + return [ + [true, null], + [true, undefined], + [true, ""], + [true, []], + [true, {}], + [true, new Set()], + [true, new Map()], + [true, function () {}], + [false, "never gonna"], + [false, ["give"]], + [false, {you: "up"}], + [false, new Set(["never gonna"])], + [false, new Map([["let", "you down"]])] + ]; + } + ) +]); + +var isNullOrEmptyString = new TestSuite("Utils.isNullOrEmptyString", [ + new Test("should test if value is null, undefined or empty string", + function (pTester, pDataProvider) + { + var expectValue = pDataProvider[0]; + var actualValue = Utils.isNullOrEmptyString(pDataProvider[1]); + + pTester.expectThat(actualValue).equals(expectValue).assert(); + }, + function dataProvider() + { + return [ + [true, null], + [true, undefined], + [true, ""], + [false, 0], + [false, 42], + [false, false], + [false, true], + [false, []], + [false, {}], + [false, new Set()], + [false, new Map()], + [false, function () {}], + [false, "never gonna"], + [false, ["give"]], + [false, {you: "up"}], + [false, new Set(["never gonna"])], + [false, new Map([["let", "you down"]])] + ]; + } + ) +]); + +var isNotNullOrEmptyString = new TestSuite("Utils.isNotNullOrEmptyString", [ + new Test("should test if value isn't null, undefined or empty string", + function (pTester, pDataProvider) + { + var expectValue = pDataProvider[0]; + var actualValue = Utils.isNotNullOrEmptyString(pDataProvider[1]); + + pTester.expectThat(actualValue).equals(expectValue).assert(); + }, + function dataProvider() + { + return [ + [false, null], + [false, undefined], + [false, ""], + [true, 0], + [true, 42], + [true, false], + [true, true], + [true, []], + [true, {}], + [true, new Set()], + [true, new Map()], + [true, function () {}], + [true, "never gonna"], + [true, ["give"]], + [true, {you: "up"}], + [true, new Set(["never gonna"])], + [true, new Map([["let", "you down"]])] + ]; + } + ) +]); + +var clone = new TestSuite("Utils.clone", [ + new Test("should test if value is copied correctly", + function (pTester, pDataProvider) + { + var original = pDataProvider[0]; + var copy = Utils.clone(original); + + pTester.expectThat(original).equals(copy).assert(); + }, + function dataProvider() + { + var recursiveObj = {test: "Test"}; + recursiveObj.recursion = recursiveObj; + return [ + [null], + [undefined], + [0], + [42], + [""], + ["yeee"], + [[]], + [{}], + [["one", "two", 3, 4]], + [{one: "two", three: 4}], + [["one", "two", [3, 4, {five: "6"}]]], + [{one: "two", three: [4, {five: "6"}]}], + [new Set(["one", "two", 3, 4])], + [new Map([["one", "two"], [3, 4]])], + [recursiveObj] + ]; + } + ) +]); + +var isEqual = new TestSuite("Utils.isEqual", [ + new Test("should test if object equality is checked correctly", + function (pTester, pDataProvider) + { + var expectValue = pDataProvider[0]; + var actualValue = Utils.isEqual(pDataProvider[1], pDataProvider[2]); + + pTester.expectThat(actualValue).equals(expectValue).assert(); + }, + function dataProvider() + { + var num = 0; + var str = "0"; + var objStr = new String("0"); + var obj_1 = {a: 1, b: 2, c: 3}; + var obj_2 = {x: null, y: "test", z: true}; + var recursiveObj1 = {test: "Test"}; + recursiveObj1.recursion = recursiveObj1; + var recursiveObj2 = {test: "Test"}; + recursiveObj2.recursion = recursiveObj2; + + return [ + [true, num, num], + [true, str, str], + [true, true, true], + [true, objStr, objStr], + [true, obj_1, obj_1], + [true, obj_2, obj_2], + [false, true, false], + [false, obj_1, obj_2], + [false, num, objStr], + [false, num, str], + [false, objStr, str], + [false, null, undefined], + [false, objStr, null], + [false, objStr, undefined], + [false, null, "null"], + [true, recursiveObj1, recursiveObj2] + ]; + } + ) +]); + +var isFunction = new TestSuite("Utils.isFunction", [ + new Test("should test if functions are identified correctly", + function (pTester, pDataProvider) + { + var expectValue = pDataProvider[0]; + var actualValue = Utils.isFunction(pDataProvider[1]); + + pTester.expectThat(actualValue).equals(expectValue).assert(); + }, + function dataProvider() + { + return [ + [true, function () {}], + [true, (function () {}).bind({})], + [false, null], + [false, undefined], + [false, true], + [false, false], + [false, 0], + [false, "str"], + [false, {}], + [false, []], + [false, new Map()], + [false, new Set()] + ]; + } + ) +]); + +var isString = new TestSuite("Utils.isString", [ + new Test("should test if strings are identified correctly", + function (pTester, pDataProvider) + { + var expectValue = pDataProvider[0]; + var actualValue = Utils.isString(pDataProvider[1]); + + pTester.expectThat(actualValue).equals(expectValue).assert(); + }, + function dataProvider() + { + return [ + [false, null], + [false, undefined], + [false, true], + [false, false], + [false, 0], + [true, ""], + [true, "str"], + [false, new String("")], + [false, {}], + [false, []], + [false, function () {}], + [false, new Map()], + [false, new Set()] + ]; + } + ) +]); + +var isNumber = new TestSuite("Utils.isNumber", [ + new Test("should test if numbers are identified correctly", + function (pTester, pDataProvider) + { + var expectValue = pDataProvider[0]; + var actualValue = Utils.isNumber(pDataProvider[1]); + + pTester.expectThat(actualValue).equals(expectValue).assert(); + }, + function dataProvider() + { + return [ + [false, null], + [false, undefined], + [false, true], + [false, false], + [true, 0], + [true, NaN], + [true, Infinity], + [false, "0"], + [false, {}], + [false, []], + [false, function () {}], + [false, new Map()], + [false, new Set()] + ]; + } + ) +]); + +var isNumeric = new TestSuite("Utils.isNumeric", [ + new Test("should test if numbers are identified correctly", + function (pTester, pDataProvider) + { + var expectValue = pDataProvider[0]; + var actualValue = Utils.isNumeric(pDataProvider[1]); + + pTester.expectThat(actualValue).equals(expectValue).assert(); + }, + function dataProvider() + { + return [ + [false, null], + [false, undefined], + [false, true], + [false, false], + [true, 0], + [true, "0"], + [false, "notnumber"], + [false, {}], + [false, []], + [false, function () {}], + [false, new Map()], + [false, new Set()] + ]; + } + ) +]); + +var isInteger = new TestSuite("Utils.isInteger", [ + new Test("should test if integers are identified correctly", + function (pTester, pDataProvider) + { + var expectValue = pDataProvider[0]; + var actualValue = Utils.isInteger(pDataProvider[1]); + + pTester.expectThat(actualValue).equals(expectValue).assert(); + }, + function dataProvider() + { + return [ + [false, null], + [false, undefined], + [false, true], + [false, false], + [true, 0], + [true, "0"], + [true, 1.0], + [true, "1.0"], + [false, 1.2], + [false, "1.2"], + [false, NaN], + [false, Infinity], + [false, "notnumber"], + [false, {}], + [false, []], + [false, function () {}], + [false, new Map()], + [false, new Set()] + ]; + } + ) +]); + +var isFloat = new TestSuite("Utils.isFloat", [ + new Test("should test if floats are identified correctly", + function (pTester, pDataProvider) + { + var expectValue = pDataProvider[0]; + var actualValue = Utils.isFloat(pDataProvider[1]); + + pTester.expectThat(actualValue).equals(expectValue).assert(); + }, + function dataProvider() + { + return [ + [false, null], + [false, undefined], + [false, true], + [false, false], + [false, 0], + [false, "0"], + [false, 1.0], + [false, "1.0"], + [true, 1.2], + [true, "1.2"], + [false, NaN], + [false, Infinity], + [false, "notnumber"], + [false, {}], + [false, []], + [false, function () {}], + [false, new Map()], + [false, new Set()] + ]; + } + ) +]); + +var isObject = new TestSuite("Utils.isObject", [ + new Test("should test if objects are identified correctly", + function (pTester, pDataProvider) + { + var expectValue = pDataProvider[0]; + var actualValue = Utils.isObject(pDataProvider[1]); + + pTester.expectThat(actualValue).equals(expectValue).assert(); + }, + function dataProvider() + { + return [ + [false, null], + [false, undefined], + [false, true], + [false, false], + [false, 0], + [false, "0"], + [false, NaN], + [false, Infinity], + [true, {}], + [true, []], + [false, function () {}], + [true, new Map()], + [true, new Set()] + ]; + } + ) +]); + +var isMap = new TestSuite("Utils.isMap", [ + new Test("should test if maps are identified correctly", + function (pTester, pDataProvider) + { + var expectValue = pDataProvider[0]; + var actualValue = Utils.isMap(pDataProvider[1]); + + pTester.expectThat(actualValue).equals(expectValue).assert(); + }, + function dataProvider() + { + return [ + [false, null], + [false, undefined], + [false, true], + [false, false], + [false, 0], + [false, "0"], + [false, NaN], + [false, Infinity], + [false, {}], + [false, []], + [false, function () {}], + [true, new Map()], + [false, new Set()] + ]; + } + ) +]); + +var isBoolean = new TestSuite("Utils.isBoolean", [ + new Test("should test if booleans are identified correctly", + function (pTester, pDataProvider) + { + var expectValue = pDataProvider[0]; + var actualValue = Utils.isBoolean(pDataProvider[1]); + + pTester.expectThat(actualValue).equals(expectValue).assert(); + }, + function dataProvider() + { + return [ + [false, null], + [false, undefined], + [true, true], + [true, false], + [false, 0], + [false, "0"], + [false, NaN], + [false, Infinity], + [false, {}], + [false, []], + [false, function () {}], + [false, new Map()], + [false, new Set()] + ]; + } + ) +]); + +var toBoolean = new TestSuite("Utils.toBoolean", [ + new Test("should test if boolean-ish values are parsed correctly", + function (pTester, pDataProvider) + { + var expectValue = pDataProvider[0]; + var actualValue = Utils.toBoolean(pDataProvider[1]); + + pTester.expectThat(actualValue).equals(expectValue).assert(); + }, + function dataProvider() + { + return [ + [true, true], + [true, 1], + [true, "1"], + [true, "true"], + [true, {}], + [true, []], + [true, function () {}], + [true, new Map()], + [true, new Set()], + [false, false], + [false, 0], + [false, "0"], + [false, ""], + [false, "false"], + [false, null], + [false, "null"], + [false, undefined], + [false, "undefined"] + ]; + } + ) +]); + +var tester = new Tester("Test Util_lib"); +tester.initCoverage(Utils); +tester.test(isEmpty); +tester.test(isNullOrEmpty); +tester.test(isNullOrEmptyString); +tester.test(clone); +tester.test(isEqual); +tester.test(isFunction); +tester.test(isString); +tester.test(isNumber); +tester.test(isNumeric); +tester.test(isInteger); +tester.test(isFloat); +tester.test(isObject); +tester.test(isMap); +tester.test(isBoolean); +tester.test(toBoolean); + +tester.summary(); + +result.object(tester.getResults());