]> git.ipfire.org Git - thirdparty/Chart.js.git/commitdiff
Start writing tests for core.helpers. Fix a number of small bugs found during testing
authorEvert Timberg <evert.timberg@gmail.com>
Sat, 29 Aug 2015 02:32:56 +0000 (22:32 -0400)
committerEvert Timberg <evert.timberg@gmail.com>
Sat, 29 Aug 2015 02:32:56 +0000 (22:32 -0400)
src/core/core.helpers.js
test/core.helpers.tests.js

index 2fbb644a9080cab5a925579df31662e79256239e..46ad752d88210a6e1ac3800737b9985f705fafb3 100644 (file)
                        });
                        return base;
                },
-               merge = helpers.merge = function(base, master) {
-                       //Merge properties in left object over to a shallow clone of object right.
-                       var args = Array.prototype.slice.call(arguments, 0);
-                       args.unshift({});
-                       return extend.apply(null, args);
-               },
                // Need a special merge function to chart configs since they are now grouped
                configMerge = helpers.configMerge = function(_base) {
                        var base = clone(_base);
                                                        helpers.each(value, function(valueObj, index) {
 
                                                                if (index < baseArray.length) {
-                                                                       baseArray[index] = helpers.configMerge(baseArray[index], valueObj);
+                                                                       if (typeof baseArray[index] == 'object' && baseArray[index] !== null && typeof valueObj == 'object' && valueObj !== null) {
+                                                                               // Two objects are coming together. Do a merge of them.
+                                                                               baseArray[index] = helpers.configMerge(baseArray[index], valueObj);
+                                                                       } else {
+                                                                               // Just overwrite in this case since there is nothing to merge
+                                                                               baseArray[index] = valueObj;
+                                                                       }
                                                                } else {
                                                                        baseArray.push(valueObj); // nothing to merge
                                                                }
                        return base;
                },
                getValueAtIndexOrDefault = helpers.getValueAtIndexOrDefault = function(value, index, defaultValue) {
-                       if (!value) {
+                       if (value === undefined || value === null) {
                                return defaultValue;
                        }
 
-                       if (helpers.isArray(value) && index < value.length) {
-                               return value[index];
+                       if (helpers.isArray(value)) {
+                               return index < value.length ? value[index] : defaultValue;
                        }
 
                        return value;
                },
                findNextWhere = helpers.findNextWhere = function(arrayToSearch, filterCallback, startIndex) {
                        // Default to start of the array
-                       if (!startIndex) {
+                       if (startIndex === undefined || startIndex === null) {
                                startIndex = -1;
                        }
                        for (var i = startIndex + 1; i < arrayToSearch.length; i++) {
                },
                findPreviousWhere = helpers.findPreviousWhere = function(arrayToSearch, filterCallback, startIndex) {
                        // Default to end of the array
-                       if (!startIndex) {
+                       if (startIndex === undefined || startIndex === null) {
                                startIndex = arrayToSearch.length;
                        }
                        for (var i = startIndex - 1; i >= 0; i--) {
                                return Math.log(x) / Math.LN10;
                        }
                },
-               cap = helpers.cap = function(valueToCap, maxValue, minValue) {
-                       if (isNumber(maxValue)) {
-                               if (valueToCap > maxValue) {
-                                       return maxValue;
-                               }
-                       } else if (isNumber(minValue)) {
-                               if (valueToCap < minValue) {
-                                       return minValue;
-                               }
-                       }
-                       return valueToCap;
-               },
                getDecimalPlaces = helpers.getDecimalPlaces = function(num) {
                        if (num % 1 !== 0 && isNumber(num)) {
                                var s = num.toString();
                },
                nextItem = helpers.nextItem = function(collection, index, loop) {
                        if (loop) {
-                               return collection[index + 1] || collection[0];
+                               return index >= collection.length - 1 ? collection[0] : collection[index + 1];
                        }
-                       return collection[index + 1] || collection[collection.length - 1];
+
+                       return index >= collection.length - 1 ? collection[collection.length - 1] : collection[index + 1];
                },
                previousItem = helpers.previousItem = function(collection, index, loop) {
                        if (loop) {
-                               return collection[index - 1] || collection[collection.length - 1];
-                       }
-                       return collection[index - 1] || collection[0];
-               },
-               calculateOrderOfMagnitude = helpers.calculateOrderOfMagnitude = function(val) {
-                       return Math.floor(Math.log(val) / Math.LN10);
-               },
-               calculateScaleRange = helpers.calculateScaleRange = function(valuesArray, drawingSize, textSize, startFromZero, integersOnly) {
-
-                       //Set a minimum step of two - a point at the top of the graph, and a point at the base
-                       var minSteps = 2,
-                               maxSteps = Math.floor(drawingSize / (textSize * 1.5)),
-                               skipFitting = (minSteps >= maxSteps);
-
-                       var maxValue = max(valuesArray),
-                               minValue = min(valuesArray);
-
-                       // We need some degree of seperation here to calculate the scales if all the values are the same
-                       // Adding/minusing 0.5 will give us a range of 1.
-                       if (maxValue === minValue) {
-                               maxValue += 0.5;
-                               // So we don't end up with a graph with a negative start value if we've said always start from zero
-                               if (minValue >= 0.5 && !startFromZero) {
-                                       minValue -= 0.5;
-                               } else {
-                                       // Make up a whole number above the values
-                                       maxValue += 0.5;
-                               }
-                       }
-
-                       var valueRange = Math.abs(maxValue - minValue),
-                               rangeOrderOfMagnitude = calculateOrderOfMagnitude(valueRange),
-                               graphMax = Math.ceil(maxValue / (1 * Math.pow(10, rangeOrderOfMagnitude))) * Math.pow(10, rangeOrderOfMagnitude),
-                               graphMin = (startFromZero) ? 0 : Math.floor(minValue / (1 * Math.pow(10, rangeOrderOfMagnitude))) * Math.pow(10, rangeOrderOfMagnitude),
-                               graphRange = graphMax - graphMin,
-                               stepValue = Math.pow(10, rangeOrderOfMagnitude),
-                               numberOfSteps = Math.round(graphRange / stepValue);
-
-                       //If we have more space on the graph we'll use it to give more definition to the data
-                       while ((numberOfSteps > maxSteps || (numberOfSteps * 2) < maxSteps) && !skipFitting) {
-                               if (numberOfSteps > maxSteps) {
-                                       stepValue *= 2;
-                                       numberOfSteps = Math.round(graphRange / stepValue);
-                                       // Don't ever deal with a decimal number of steps - cancel fitting and just use the minimum number of steps.
-                                       if (numberOfSteps % 1 !== 0) {
-                                               skipFitting = true;
-                                       }
-                               }
-                               //We can fit in double the amount of scale points on the scale
-                               else {
-                                       //If user has declared ints only, and the step value isn't a decimal
-                                       if (integersOnly && rangeOrderOfMagnitude >= 0) {
-                                               //If the user has said integers only, we need to check that making the scale more granular wouldn't make it a float
-                                               if (stepValue / 2 % 1 === 0) {
-                                                       stepValue /= 2;
-                                                       numberOfSteps = Math.round(graphRange / stepValue);
-                                               }
-                                               //If it would make it a float break out of the loop
-                                               else {
-                                                       break;
-                                               }
-                                       }
-                                       //If the scale doesn't have to be an int, make the scale more granular anyway.
-                                       else {
-                                               stepValue /= 2;
-                                               numberOfSteps = Math.round(graphRange / stepValue);
-                                       }
-
-                               }
+                               return index <= 0 ? collection[collection.length - 1] : collection[index - 1];
                        }
-
-                       if (skipFitting) {
-                               numberOfSteps = minSteps;
-                               stepValue = graphRange / numberOfSteps;
-                       }
-                       return {
-                               steps: numberOfSteps,
-                               stepValue: stepValue,
-                               min: graphMin,
-                               max: graphMin + (numberOfSteps * stepValue)
-                       };
-
+                       return index <= 0 ? collection[0] : collection[index - 1];
                },
                // Implementation of the nice number algorithm used in determining where axis labels will go
                niceNum = helpers.niceNum = function(range, round) {
                        return tmpl(templateString, valuesObject);
                },
                /* jshint ignore:end */
-               generateLabels = helpers.generateLabels = function(templateString, numberOfSteps, graphMin, stepValue) {
-                       var labelsArray = new Array(numberOfSteps);
-                       if (templateString) {
-                               each(labelsArray, function(val, index) {
-                                       labelsArray[index] = template(templateString, {
-                                               value: (graphMin + (stepValue * (index + 1)))
-                                       });
-                               });
-                       }
-                       return labelsArray;
-               },
                //--Animation methods
                //Easing functions adapted from Robert Penner's easing equations
                //http://www.robertpenner.com/easing/
index e69de29bb2d1d6434b8b29ae775ad8c2e48c5391..730222b5b57b876f8a71a4d5a97e3abd423951bf 100644 (file)
@@ -0,0 +1,385 @@
+describe('Core helper tests', function() {
+
+       var helpers;
+
+       beforeAll(function() {
+               helpers = window.Chart.helpers;
+       });
+
+       it('Should iterate over an array and pass the extra data to that function', function() {
+               var testData = [0, 9, "abc"];
+               var scope = {}; // fake out the scope and ensure that 'this' is the correct thing
+
+               helpers.each(testData, function(item, index) {
+                       expect(item).not.toBe(undefined);
+                       expect(index).not.toBe(undefined);
+
+                       expect(testData[index]).toBe(item);
+                       expect(this).toBe(scope);
+               }, scope);
+
+               // Reverse iteration
+               var iterated = [];
+               helpers.each(testData, function(item, index) {
+                       expect(item).not.toBe(undefined);
+                       expect(index).not.toBe(undefined);
+
+                       expect(testData[index]).toBe(item);
+                       expect(this).toBe(scope);
+
+                       iterated.push(item);
+               }, scope, true);
+
+               expect(iterated.slice().reverse()).toEqual(testData);
+       });
+
+       it('Should iterate over properties in an object', function() {
+               var testData = {
+                       myProp1: 'abc',
+                       myProp2: 276,
+                       myProp3: ['a', 'b']
+               };
+
+               helpers.each(testData, function(value, key) {
+                       if (key === 'myProp1') {
+                               expect(value).toBe('abc');
+                       } else if (key === 'myProp2') {
+                               expect(value).toBe(276);
+                       } else if (key === 'myProp3') {
+                               expect(value).toEqual(['a', 'b']);
+                       } else {
+                               expect(false).toBe(true);
+                       }
+               });
+       });
+
+       it('should not error when iterating over a null object', function() {
+               expect(function() {
+                       helpers.each(undefined);
+               }).not.toThrow();
+       });
+
+       it('Should clone an object', function() {
+               var testData = {
+                       myProp1: 'abc',
+                       myProp2: ['a', 'b'],
+                       myProp3: {
+                               myProp4: 5,
+                               myProp5: [1, 2]
+                       }
+               };
+
+               var clone = helpers.clone(testData);
+               expect(clone).toEqual(testData);
+               expect(clone).not.toBe(testData);
+
+               expect(clone.myProp2).not.toBe(testData.myProp2);
+               expect(clone.myProp3).not.toBe(testData.myProp3);
+               expect(clone.myProp3.myProp5).not.toBe(testData.myProp3.myProp5);
+       });
+
+       it('should extend an object', function() {
+               var original = {
+                       myProp1: 'abc',
+                       myProp2: 56
+               };
+
+               var extension = {
+                       myProp3: [2, 5, 6],
+                       myProp2: 0
+               };
+
+               helpers.extend(original, extension);
+
+               expect(original).toEqual({
+                       myProp1: 'abc',
+                       myProp2: 0,
+                       myProp3: [2, 5, 6],
+               });
+       });
+
+       it('Should merge a normal config without scales', function() {
+               var baseConfig = {
+                       valueProp: 5,
+                       arrayProp: [1, 2, 3, 4, 5, 6],
+                       objectProp: {
+                               prop1: 'abc',
+                               prop2: 56
+                       }
+               };
+
+               var toMerge = {
+                       valueProp2: null,
+                       arrayProp: ['a', 'c'],
+                       objectProp: {
+                               prop1: 'c',
+                               prop3: 'prop3'
+                       }
+               };
+
+               var merged = helpers.configMerge(baseConfig, toMerge);
+               expect(merged).toEqual({
+                       valueProp: 5,
+                       valueProp2: null,
+                       arrayProp: ['a', 'c', 3, 4, 5, 6],
+                       objectProp: {
+                               prop1: 'c',
+                               prop2: 56,
+                               prop3: 'prop3'
+                       }
+               });
+       });
+
+       it('should merge arrays containing objects', function() {
+               var baseConfig = {
+                       arrayProp: [{
+                               prop1: 'abc',
+                               prop2: 56
+                       }],
+               };
+
+               var toMerge = {
+                       arrayProp: [{
+                               prop1: 'myProp1',
+                               prop3: 'prop3'
+                       }, 2, {
+                               prop1: 'myProp1'
+                       }],
+               };
+
+               var merged = helpers.configMerge(baseConfig, toMerge);
+               expect(merged).toEqual({
+                       arrayProp: [{
+                               prop1: 'myProp1',
+                               prop2: 56,
+                               prop3: 'prop3'
+                       },
+                       2, {
+                               prop1: 'myProp1'
+                       }],
+               });
+       });
+
+       it ('Should merge scale configs', function() {
+               var baseConfig = {
+                       scales: {
+                               prop1: {
+                                       abc: 123,
+                                       def: '456'
+                               },
+                               prop2: 777,
+                               yAxes: [{
+                                       type: 'linear',
+                               }, {
+                                       type: 'log'
+                               }]
+                       }
+               };
+
+               var toMerge = {
+                       scales: {
+                               prop1: {
+                                       def: 'bbb',
+                                       ghi: 78
+                               },
+                               prop2: null,
+                               yAxes: [{
+                                       type: 'linear',
+                                       axisProp: 456
+                               }, {
+                                       // pulls in linear default config since axis type changes
+                                       type: 'linear',
+                                       position: 'right'
+                               }, {
+                                       // Pulls in linear default config since axis not in base
+                                       type: 'linear'
+                               }]
+                       }
+               };
+
+               var merged = helpers.configMerge(baseConfig, toMerge);
+               expect(merged).toEqual({
+                       scales: {
+                               prop1: {
+                                       abc: 123,
+                                       def: 'bbb',
+                                       ghi: 78
+                               },
+                               prop2: null,
+                               yAxes: [{
+                                       type: 'linear',
+                                       axisProp: 456
+                               }, {
+                                       type: 'linear',
+                                       display: true,
+                                       position: "right",
+                                       gridLines: {
+                                               show: true,
+                                               color: "rgba(0, 0, 0, 0.1)",
+                                               lineWidth: 1,
+                                               drawOnChartArea: true,
+                                               drawTicks: true,
+                                               zeroLineWidth: 1,
+                                               zeroLineColor: "rgba(0,0,0,0.25)",
+                                       },
+                                       reverse: false,
+                                       beginAtZero: false,
+                                       override: null,
+                                       labels: {
+                                               show: true,
+                                               mirror: false,
+                                               padding: 10,
+                                               template: "<%=value.toLocaleString()%>",
+                                               fontSize: 12,
+                                               fontStyle: "normal",
+                                               fontColor: "#666",
+                                               fontFamily: "Helvetica Neue"
+                                       }
+                               }, {
+                                       type: 'linear',
+                                       display: true,
+                                       position: "left",
+                                       gridLines: {
+                                               show: true,
+                                               color: "rgba(0, 0, 0, 0.1)",
+                                               lineWidth: 1,
+                                               drawOnChartArea: true,
+                                               drawTicks: true,
+                                               zeroLineWidth: 1,
+                                               zeroLineColor: "rgba(0,0,0,0.25)",
+                                       },
+                                       reverse: false,
+                                       beginAtZero: false,
+                                       override: null,
+                                       labels: {
+                                               show: true,
+                                               mirror: false,
+                                               padding: 10,
+                                               template: "<%=value.toLocaleString()%>",
+                                               fontSize: 12,
+                                               fontStyle: "normal",
+                                               fontColor: "#666",
+                                               fontFamily: "Helvetica Neue"
+                                       }
+                               }]
+                       }
+               });
+       });
+
+       it ('should get value or default', function() {
+               expect(helpers.getValueAtIndexOrDefault(98, 0, 56)).toBe(98);
+               expect(helpers.getValueAtIndexOrDefault(0, 0, 56)).toBe(0); 
+               expect(helpers.getValueAtIndexOrDefault(undefined, undefined, 56)).toBe(56);
+               expect(helpers.getValueAtIndexOrDefault([1, 2, 3], 1, 100)).toBe(2);
+               expect(helpers.getValueAtIndexOrDefault([1, 2, 3], 3, 100)).toBe(100);
+       });
+
+       it ('should filter an array', function() {
+               var data = [-10, 0, 6, 0, 7];
+               var callback = function(item) { return item > 2};
+               expect(helpers.where(data, callback)).toEqual([6, 7]);
+               expect(helpers.findNextWhere(data, callback)).toEqual(6);
+               expect(helpers.findNextWhere(data, callback, 2)).toBe(7);
+               expect(helpers.findNextWhere(data, callback, 4)).toBe(undefined);
+               expect(helpers.findPreviousWhere(data, callback)).toBe(7);
+               expect(helpers.findPreviousWhere(data, callback, 3)).toBe(6);
+               expect(helpers.findPreviousWhere(data, callback, 0)).toBe(undefined);
+       });
+
+       it ('Should get the correct sign', function() {
+               expect(helpers.sign(0)).toBe(0);
+               expect(helpers.sign(10)).toBe(1);
+               expect(helpers.sign(-5)).toBe(-1);
+       });
+
+       it ('should do a log10 operation', function() {
+               expect(helpers.log10(0)).toBe(-Infinity);
+               expect(helpers.log10(1)).toBe(0);
+               expect(helpers.log10(1000)).toBe(3);
+       });
+
+       it ('Should generate ids', function() {
+               expect(helpers.uid()).toBe('chart-0');
+               expect(helpers.uid()).toBe('chart-1');
+               expect(helpers.uid()).toBe('chart-2');
+               expect(helpers.uid()).toBe('chart-3');
+       });
+
+       it ('should detect a number', function() {
+               expect(helpers.isNumber(123)).toBe(true);
+               expect(helpers.isNumber('123')).toBe(true);
+               expect(helpers.isNumber(null)).toBe(false);
+               expect(helpers.isNumber(NaN)).toBe(false);
+               expect(helpers.isNumber(undefined)).toBe(false);
+               expect(helpers.isNumber('cbc')).toBe(false);
+       });
+
+       it ('should convert between radians and degrees', function() {
+               expect(helpers.toRadians(180)).toBe(Math.PI);
+               expect(helpers.toRadians(90)).toBe(0.5 * Math.PI);
+               expect(helpers.toDegrees(Math.PI)).toBe(180);
+               expect(helpers.toDegrees(Math.PI * 3 /2)).toBe(270);
+       });
+
+       it ('should get an angle from a point', function() {
+               var center = {
+                       x: 0,
+                       y: 0
+               };
+
+               expect(helpers.getAngleFromPoint(center, {x: 0, y: 10})).toEqual({
+                       angle: Math.PI / 2,
+                       distance: 10,
+               });
+
+               expect(helpers.getAngleFromPoint(center, {x: Math.sqrt(2), y: Math.sqrt(2)})).toEqual({
+                       angle: Math.PI / 4,
+                       distance: 2
+               });
+
+               expect(helpers.getAngleFromPoint(center, {x: -1.0 * Math.sqrt(2), y: -1.0 * Math.sqrt(2)})).toEqual({
+                       angle: Math.PI * 1.25,
+                       distance: 2
+               });
+       });
+
+       it ('should spline curves', function() {
+               expect(helpers.splineCurve({x: 0, y: 0}, {x: 1, y: 1}, { x: 2, y: 0}, 0)).toEqual({
+                       previous: {
+                               x: 1,
+                               y: 1,
+                       },
+                       next: {
+                               x: 1,
+                               y: 1,
+                       }
+               });
+
+               expect(helpers.splineCurve({x: 0, y: 0}, {x: 1, y: 1}, { x: 2, y: 0}, 1)).toEqual({
+                       previous: {
+                               x: 0,
+                               y: 1,
+                       },
+                       next: {
+                               x: 2,
+                               y: 1,
+                       }
+               });
+       });
+
+       it ('should get the next or previous item in an array', function() {
+               var testData = [0, 1, 2];
+
+               expect(helpers.nextItem(testData, 0, false)).toEqual(1);
+               expect(helpers.nextItem(testData, 2, false)).toEqual(2);
+               expect(helpers.nextItem(testData, 2, true)).toEqual(0);
+               expect(helpers.nextItem(testData, 1, true)).toEqual(2);
+               expect(helpers.nextItem(testData, -1, false)).toEqual(0);
+
+               expect(helpers.previousItem(testData, 0, false)).toEqual(0);
+               expect(helpers.previousItem(testData, 0, true)).toEqual(2);
+               expect(helpers.previousItem(testData, 2, false)).toEqual(1);
+               expect(helpers.previousItem(testData, 1, true)).toEqual(0);
+
+       });
+});
\ No newline at end of file