]> git.ipfire.org Git - thirdparty/Chart.js.git/commitdiff
Cleanup and reorganize core and canvas helpers
authorSimon Brunel <simonbrunel@users.noreply.github.com>
Sat, 24 Jun 2017 09:46:06 +0000 (11:46 +0200)
committerEvert Timberg <evert.timberg+github@gmail.com>
Sat, 24 Jun 2017 17:28:52 +0000 (13:28 -0400)
Move some of the "core" and "canvas" utils in `helpers.core.js` and `helpers.canvas.js` and introduce the new `isNullOrUndef` and `isObject` helpers. Deprecate `indexOf` and rename `drawRoundedRectangle` to `roundedRect` which now creates a simple `rect` path if radius is 0. Write missing unit tests for the moved helpers.

18 files changed:
.gitignore
src/chart.js
src/controllers/controller.line.js
src/core/core.controller.js
src/core/core.helpers.js
src/core/core.scale.js
src/elements/element.point.js
src/helpers/helpers.canvas.js [moved from src/core/core.canvasHelpers.js with 55% similarity]
src/helpers/helpers.core.js [new file with mode: 0644]
src/platforms/platform.dom.js
src/plugins/plugin.legend.js
src/scales/scale.category.js
test/jasmine.context.js
test/specs/core.helpers.tests.js
test/specs/element.point.tests.js
test/specs/global.deprecations.tests.js
test/specs/helpers.canvas.tests.js [new file with mode: 0644]
test/specs/helpers.core.tests.js [new file with mode: 0644]

index 0853b7efd9320b045c4242bc8787d369f41a2863..53ce8fedb1c674d41883e49e4d8d72b1b69a6a01 100644 (file)
@@ -4,6 +4,7 @@
 /dist
 /docs/index.md
 /gh-pages
+/jsdoc
 /node_modules
 .DS_Store
 .idea
index f36d240c2b855a03aa7a084c3ae085c56658ff65..517224c9340fbfe21ed9c1506da1f50f1db4c2db 100644 (file)
@@ -3,11 +3,12 @@
  */
 var Chart = require('./core/core.js')();
 
+require('./helpers/helpers.core')(Chart);
 require('./core/core.helpers')(Chart);
 require('./helpers/helpers.time')(Chart);
+require('./helpers/helpers.canvas')(Chart);
 
 require('./platforms/platform.js')(Chart);
-require('./core/core.canvasHelpers')(Chart);
 require('./core/core.element')(Chart);
 require('./core/core.plugin.js')(Chart);
 require('./core/core.animation')(Chart);
index 1a26a04b7ef5b32558c5d6f11f16eac1272822c2..1695109543794d0fbf793cfbbf9b7edc59102869 100644 (file)
@@ -285,13 +285,13 @@ module.exports = function(Chart) {
                        var ilen = points.length;
                        var i = 0;
 
-                       Chart.canvasHelpers.clipArea(chart.ctx, area);
+                       Chart.helpers.canvas.clipArea(chart.ctx, area);
 
                        if (lineEnabled(me.getDataset(), chart.options)) {
                                meta.dataset.draw();
                        }
 
-                       Chart.canvasHelpers.unclipArea(chart.ctx);
+                       Chart.helpers.canvas.unclipArea(chart.ctx);
 
                        // Draw the points
                        for (; i<ilen; ++i) {
index 0d21fc5f400f0dcb73de9ee62274d817fd757018..08f57fd3fdcfc169eae0721c16f4d7ccb8a33220 100644 (file)
@@ -151,7 +151,7 @@ module.exports = function(Chart) {
                },
 
                clear: function() {
-                       helpers.clear(this);
+                       helpers.canvas.clear(this);
                        return this;
                },
 
@@ -511,7 +511,7 @@ module.exports = function(Chart) {
 
                        me.clear();
 
-                       if (easingValue === undefined || easingValue === null) {
+                       if (helpers.isNullOrUndef(easingValue)) {
                                easingValue = 1;
                        }
 
@@ -688,7 +688,7 @@ module.exports = function(Chart) {
 
                        if (canvas) {
                                me.unbindEvents();
-                               helpers.clear(me);
+                               helpers.canvas.clear(me);
                                platform.releaseContext(me.ctx);
                                me.canvas = null;
                                me.ctx = null;
index 273f282e44d526c3f4f4c577d448824913857bcb..a8baa606da47a031fc52b82dce39deee432aef75 100644 (file)
@@ -5,32 +5,9 @@
 var color = require('chartjs-color');
 
 module.exports = function(Chart) {
-       // Global Chart helpers object for utility methods and classes
-       var helpers = Chart.helpers = {};
+       var helpers = Chart.helpers;
 
        // -- Basic js utility methods
-       helpers.each = function(loopable, callback, self, reverse) {
-               // Check to see if null or undefined firstly.
-               var i, len;
-               if (helpers.isArray(loopable)) {
-                       len = loopable.length;
-                       if (reverse) {
-                               for (i = len - 1; i >= 0; i--) {
-                                       callback.call(self, loopable[i], i);
-                               }
-                       } else {
-                               for (i = 0; i < len; i++) {
-                                       callback.call(self, loopable[i], i);
-                               }
-                       }
-               } else if (typeof loopable === 'object') {
-                       var keys = Object.keys(loopable);
-                       len = keys.length;
-                       for (i = 0; i < len; i++) {
-                               callback.call(self, loopable[keys[i]], keys[i]);
-                       }
-               }
-       };
        helpers.clone = function(obj) {
                var objClone = {};
                helpers.each(obj, function(value, key) {
@@ -123,32 +100,7 @@ module.exports = function(Chart) {
 
                return base;
        };
-       helpers.getValueAtIndexOrDefault = function(value, index, defaultValue) {
-               if (value === undefined || value === null) {
-                       return defaultValue;
-               }
-
-               if (helpers.isArray(value)) {
-                       return index < value.length ? value[index] : defaultValue;
-               }
 
-               return value;
-       };
-       helpers.getValueOrDefault = function(value, defaultValue) {
-               return value === undefined ? defaultValue : value;
-       };
-       helpers.indexOf = Array.prototype.indexOf?
-               function(array, item) {
-                       return array.indexOf(item);
-               }:
-               function(array, item) {
-                       for (var i = 0, ilen = array.length; i < ilen; ++i) {
-                               if (array[i] === item) {
-                                       return i;
-                               }
-                       }
-                       return -1;
-               };
        helpers.where = function(collection, filterCallback) {
                if (helpers.isArray(collection) && Array.prototype.filter) {
                        return collection.filter(filterCallback);
@@ -178,7 +130,7 @@ module.exports = function(Chart) {
                };
        helpers.findNextWhere = function(arrayToSearch, filterCallback, startIndex) {
                // Default to start of the array
-               if (startIndex === undefined || startIndex === null) {
+               if (helpers.isNullOrUndef(startIndex)) {
                        startIndex = -1;
                }
                for (var i = startIndex + 1; i < arrayToSearch.length; i++) {
@@ -190,7 +142,7 @@ module.exports = function(Chart) {
        };
        helpers.findPreviousWhere = function(arrayToSearch, filterCallback, startIndex) {
                // Default to end of the array
-               if (startIndex === undefined || startIndex === null) {
+               if (helpers.isNullOrUndef(startIndex)) {
                        startIndex = arrayToSearch.length;
                }
                for (var i = startIndex - 1; i >= 0; i--) {
@@ -223,13 +175,6 @@ module.exports = function(Chart) {
 
                return ChartElement;
        };
-       helpers.noop = function() {};
-       helpers.uid = (function() {
-               var id = 0;
-               return function() {
-                       return id++;
-               };
-       }());
        // -- Math methods
        helpers.isNumber = function(n) {
                return !isNaN(parseFloat(n)) && isFinite(n);
@@ -837,9 +782,6 @@ module.exports = function(Chart) {
                canvas.style.width = width + 'px';
        };
        // -- Canvas methods
-       helpers.clear = function(chart) {
-               chart.ctx.clearRect(0, 0, chart.width, chart.height);
-       };
        helpers.fontString = function(pixelSize, fontStyle, fontFamily) {
                return fontStyle + ' ' + pixelSize + 'px ' + fontFamily;
        };
@@ -903,19 +845,6 @@ module.exports = function(Chart) {
                });
                return numberOfLines;
        };
-       helpers.drawRoundedRectangle = function(ctx, x, y, width, height, radius) {
-               ctx.beginPath();
-               ctx.moveTo(x + radius, y);
-               ctx.lineTo(x + width - radius, y);
-               ctx.quadraticCurveTo(x + width, y, x + width, y + radius);
-               ctx.lineTo(x + width, y + height - radius);
-               ctx.quadraticCurveTo(x + width, y + height, x + width - radius, y + height);
-               ctx.lineTo(x + radius, y + height);
-               ctx.quadraticCurveTo(x, y + height, x, y + height - radius);
-               ctx.lineTo(x, y + radius);
-               ctx.quadraticCurveTo(x, y, x + radius, y);
-               ctx.closePath();
-       };
 
        helpers.color = !color?
                function(value) {
@@ -931,54 +860,10 @@ module.exports = function(Chart) {
                        return color(value);
                };
 
-       helpers.isArray = Array.isArray?
-               function(obj) {
-                       return Array.isArray(obj);
-               } :
-               function(obj) {
-                       return Object.prototype.toString.call(obj) === '[object Array]';
-               };
-       // ! @see http://stackoverflow.com/a/14853974
-       helpers.arrayEquals = function(a0, a1) {
-               var i, ilen, v0, v1;
-
-               if (!a0 || !a1 || a0.length !== a1.length) {
-                       return false;
-               }
-
-               for (i = 0, ilen=a0.length; i < ilen; ++i) {
-                       v0 = a0[i];
-                       v1 = a1[i];
-
-                       if (v0 instanceof Array && v1 instanceof Array) {
-                               if (!helpers.arrayEquals(v0, v1)) {
-                                       return false;
-                               }
-                       } else if (v0 !== v1) {
-                               // NOTE: two different object instances will never be equal: {x:20} != {x:20}
-                               return false;
-                       }
-               }
-
-               return true;
-       };
-       helpers.callback = function(fn, args, thisArg) {
-               if (fn && typeof fn.call === 'function') {
-                       return fn.apply(thisArg, args);
-               }
-       };
        helpers.getHoverColor = function(colorValue) {
                /* global CanvasPattern */
                return (colorValue instanceof CanvasPattern) ?
                        colorValue :
                        helpers.color(colorValue).saturate(0.5).darken(0.1).rgbString();
        };
-
-       /**
-        * Provided for backward compatibility, use Chart.helpers#callback instead.
-        * @function Chart.helpers#callCallback
-        * @deprecated since version 2.6.0
-        * @todo remove at version 3
-        */
-       helpers.callCallback = helpers.callback;
 };
index be69ed9089fa4d11530ee36e9b7175affd57bd7f..48c99bc6cbccd454a5d325767ff51f2a574ba7fe 100644 (file)
@@ -422,7 +422,7 @@ module.exports = function(Chart) {
                // Get the correct value. NaN bad inputs, If the value type is object get the x or y based on whether we are horizontal or not
                getRightValue: function(rawValue) {
                        // Null and undefined values first
-                       if (rawValue === null || typeof(rawValue) === 'undefined') {
+                       if (helpers.isNullOrUndef(rawValue)) {
                                return NaN;
                        }
                        // isNaN(object) returns true, so make sure NaN is checking for a number; Discard Infinite values
@@ -575,7 +575,7 @@ module.exports = function(Chart) {
                        helpers.each(me.ticks, function(tick, index) {
                                var label = (tick && tick.value) || tick;
                                // If the callback returned a null or undefined value, do not draw this line
-                               if (label === undefined || label === null) {
+                               if (helpers.isNullOrUndef(label)) {
                                        return;
                                }
 
@@ -583,7 +583,7 @@ module.exports = function(Chart) {
 
                                // Since we always show the last tick,we need may need to hide the last shown one before
                                var shouldSkip = (skipRatio > 1 && index % skipRatio > 0) || (index % skipRatio === 0 && index + skipRatio >= me.ticks.length);
-                               if (shouldSkip && !isLastTick || (label === undefined || label === null)) {
+                               if (shouldSkip && !isLastTick || helpers.isNullOrUndef(label)) {
                                        return;
                                }
 
index c68edc7861b694d5975cfa25e97055dbe4d14a96..b0758195348ec5d9e9c37b6568f2caed13dc54ac 100644 (file)
@@ -94,7 +94,7 @@ module.exports = function(Chart) {
                                ctx.fillStyle = color(ctx.fillStyle).alpha(ratio).rgbString();
                        }
 
-                       Chart.canvasHelpers.drawPoint(ctx, pointStyle, radius, x, y);
+                       Chart.helpers.canvas.drawPoint(ctx, pointStyle, radius, x, y);
                }
        });
 };
similarity index 55%
rename from src/core/core.canvasHelpers.js
rename to src/helpers/helpers.canvas.js
index 610095122b8c618ebfed83d953f091c2cf6a26ed..917b296158d5bb025fc6a6c689f5e806222b4ea9 100644 (file)
@@ -1,10 +1,50 @@
 'use strict';
 
 module.exports = function(Chart) {
-       // Global Chart canvas helpers object for drawing items to canvas
-       var helpers = Chart.canvasHelpers = {};
+       var helpers = Chart.helpers;
 
-       helpers.drawPoint = function(ctx, pointStyle, radius, x, y) {
+       /**
+        * @namespace Chart.helpers.canvas
+        */
+       helpers.canvas = {
+               /**
+                * Clears the entire canvas associated to the given `chart`.
+                * @param {Chart} chart - The chart for which to clear the canvas.
+                */
+               clear: function(chart) {
+                       chart.ctx.clearRect(0, 0, chart.width, chart.height);
+               },
+
+               /**
+                * Creates a "path" for a rectangle with rounded corners at position (x, y) with a
+                * given size (width, height) and the same `radius` for all corners.
+                * @param {CanvasRenderingContext2D} ctx - The canvas 2D Context.
+                * @param {Number} x - The x axis of the coordinate for the rectangle starting point.
+                * @param {Number} y - The y axis of the coordinate for the rectangle starting point.
+                * @param {Number} width - The rectangle's width.
+                * @param {Number} height - The rectangle's height.
+                * @param {Number} radius - The rounded amount (in pixels) for the four corners.
+                * @todo handler `radius` as top-left, top-right, bottom-right, bottom-left array/object?
+                * @todo clamp `radius` to the maximum "correct" value.
+                */
+               roundedRect: function(ctx, x, y, width, height, radius) {
+                       if (radius) {
+                               ctx.moveTo(x + radius, y);
+                               ctx.lineTo(x + width - radius, y);
+                               ctx.quadraticCurveTo(x + width, y, x + width, y + radius);
+                               ctx.lineTo(x + width, y + height - radius);
+                               ctx.quadraticCurveTo(x + width, y + height, x + width - radius, y + height);
+                               ctx.lineTo(x + radius, y + height);
+                               ctx.quadraticCurveTo(x, y + height, x, y + height - radius);
+                               ctx.lineTo(x, y + radius);
+                               ctx.quadraticCurveTo(x, y, x + radius, y);
+                       } else {
+                               ctx.rect(x, y, width, height);
+                       }
+               }
+       };
+
+       helpers.canvas.drawPoint = function(ctx, pointStyle, radius, x, y) {
                var type, edgeLength, xOffset, yOffset, height, size;
 
                if (typeof pointStyle === 'object') {
@@ -48,7 +88,9 @@ module.exports = function(Chart) {
                        var leftX = x - offset;
                        var topY = y - offset;
                        var sideSize = Math.SQRT2 * radius;
-                       Chart.helpers.drawRoundedRectangle(ctx, leftX, topY, sideSize, sideSize, radius / 2);
+                       ctx.beginPath();
+                       this.roundedRect(ctx, leftX, topY, sideSize, sideSize, radius / 2);
+                       ctx.closePath();
                        ctx.fill();
                        break;
                case 'rectRot':
@@ -110,18 +152,18 @@ module.exports = function(Chart) {
                ctx.stroke();
        };
 
-       helpers.clipArea = function(ctx, clipArea) {
+       helpers.canvas.clipArea = function(ctx, clipArea) {
                ctx.save();
                ctx.beginPath();
                ctx.rect(clipArea.left, clipArea.top, clipArea.right - clipArea.left, clipArea.bottom - clipArea.top);
                ctx.clip();
        };
 
-       helpers.unclipArea = function(ctx) {
+       helpers.canvas.unclipArea = function(ctx) {
                ctx.restore();
        };
 
-       helpers.lineTo = function(ctx, previous, target, flip) {
+       helpers.canvas.lineTo = function(ctx, previous, target, flip) {
                if (target.steppedLine) {
                        if (target.steppedLine === 'after') {
                                ctx.lineTo(previous.x, target.y);
@@ -146,5 +188,34 @@ module.exports = function(Chart) {
                        target.y);
        };
 
-       Chart.helpers.canvas = helpers;
+       /**
+        * Provided for backward compatibility, use Chart.helpers.canvas instead.
+        * @namespace Chart.canvasHelpers
+        * @deprecated since version 2.6.0
+        * @todo remove at version 3
+        * @private
+        */
+       Chart.canvasHelpers = helpers.canvas;
+
+       /**
+        * Provided for backward compatibility, use Chart.helpers.canvas.clear instead.
+        * @namespace Chart.helpers.clear
+        * @deprecated since version 2.7.0
+        * @todo remove at version 3
+        * @private
+        */
+       helpers.clear = helpers.canvas.clear;
+
+       /**
+        * Provided for backward compatibility, use Chart.helpers.canvas.roundedRect instead.
+        * @namespace Chart.helpers.drawRoundedRectangle
+        * @deprecated since version 2.7.0
+        * @todo remove at version 3
+        * @private
+        */
+       helpers.drawRoundedRectangle = function(ctx) {
+               ctx.beginPath();
+               helpers.canvas.roundedRect.apply(this, arguments);
+               ctx.closePath();
+       };
 };
diff --git a/src/helpers/helpers.core.js b/src/helpers/helpers.core.js
new file mode 100644 (file)
index 0000000..3d2a7c8
--- /dev/null
@@ -0,0 +1,182 @@
+'use strict';
+
+module.exports = function(Chart) {
+       /**
+        * @namespace Chart.helpers
+        */
+       var helpers = Chart.helpers = {
+               /**
+                * An empty function that can be used, for example, for optional callback.
+                */
+               noop: function() {},
+
+               /**
+                * Returns a unique id, sequentially generated from a global variable.
+                * @returns {Number}
+                * @function
+                */
+               uid: (function() {
+                       var id = 0;
+                       return function() {
+                               return id++;
+                       };
+               }()),
+
+               /**
+                * Returns true if `value` is neither null nor undefined, else returns false.
+                * @param {*} value - The value to test.
+                * @returns {Boolean}
+                * @since 2.7.0
+                */
+               isNullOrUndef: function(value) {
+                       return value === null || typeof value === 'undefined';
+               },
+
+               /**
+                * Returns true if `value` is an array, else returns false.
+                * @param {*} value - The value to test.
+                * @returns {Boolean}
+                * @function
+                */
+               isArray: Array.isArray? Array.isArray : function(value) {
+                       return Object.prototype.toString.call(value) === '[object Array]';
+               },
+
+               /**
+                * Returns true if `value` is an object (excluding null), else returns false.
+                * @param {*} value - The value to test.
+                * @returns {Boolean}
+                * @since 2.7.0
+                */
+               isObject: function(value) {
+                       return value !== null && Object.prototype.toString.call(value) === '[object Object]';
+               },
+
+               /**
+                * Returns `value` if defined, else returns `defaultValue`.
+                * @param {*} value - The value to return if defined.
+                * @param {*} defaultValue - The value to return if `value` is undefined.
+                * @returns {*}
+                */
+               getValueOrDefault: function(value, defaultValue) {
+                       return typeof value === 'undefined'? defaultValue : value;
+               },
+
+               /**
+                * Returns value at the given `index` in array if defined, else returns `defaultValue`.
+                * @param {Array} value - The array to lookup for value at `index`.
+                * @param {Number} index - The index in `value` to lookup for value.
+                * @param {*} defaultValue - The value to return if `value[index]` is undefined.
+                * @returns {*}
+                */
+               getValueAtIndexOrDefault: function(value, index, defaultValue) {
+                       if (helpers.isNullOrUndef(value)) {
+                               return defaultValue;
+                       }
+
+                       if (helpers.isArray(value)) {
+                               value = value[index];
+                               return typeof value === 'undefined'? defaultValue : value;
+                       }
+
+                       return value;
+               },
+
+               /**
+                * Calls `fn` with the given `args` in the scope defined by `thisArg` and returns the
+                * value returned by `fn`. If `fn` is not a function, this method returns undefined.
+                * @param {Function} fn - The function to call.
+                * @param {Array|undefined|null} args - The arguments with which `fn` should be called.
+                * @param {Object} [thisArg] - The value of `this` provided for the call to `fn`.
+                * @returns {*}
+                */
+               callback: function(fn, args, thisArg) {
+                       if (fn && typeof fn.call === 'function') {
+                               return fn.apply(thisArg, args);
+                       }
+               },
+
+               /**
+                * Note(SB) for performance sake, this method should only be used when loopable type
+                * is unknown or in none intensive code (not called often and small loopable). Else
+                * it's preferable to use a regular for() loop and save extra function calls.
+                * @param {Object|Array} loopable - The object or array to be iterated.
+                * @param {Function} fn - The function to call for each item.
+                * @param {Object} [thisArg] - The value of `this` provided for the call to `fn`.
+                * @param {Boolean} [reverse] - If true, iterates backward on the loopable.
+                */
+               each: function(loopable, fn, thisArg, reverse) {
+                       var i, len, keys;
+                       if (helpers.isArray(loopable)) {
+                               len = loopable.length;
+                               if (reverse) {
+                                       for (i = len - 1; i >= 0; i--) {
+                                               fn.call(thisArg, loopable[i], i);
+                                       }
+                               } else {
+                                       for (i = 0; i < len; i++) {
+                                               fn.call(thisArg, loopable[i], i);
+                                       }
+                               }
+                       } else if (helpers.isObject(loopable)) {
+                               keys = Object.keys(loopable);
+                               len = keys.length;
+                               for (i = 0; i < len; i++) {
+                                       fn.call(thisArg, loopable[keys[i]], keys[i]);
+                               }
+                       }
+               },
+
+               /**
+                * Returns true if the `a0` and `a1` arrays have the same content, else returns false.
+                * @see http://stackoverflow.com/a/14853974
+                * @param {Array} a0 - The array to compare
+                * @param {Array} a1 - The array to compare
+                * @returns {Boolean}
+                */
+               arrayEquals: function(a0, a1) {
+                       var i, ilen, v0, v1;
+
+                       if (!a0 || !a1 || a0.length !== a1.length) {
+                               return false;
+                       }
+
+                       for (i = 0, ilen=a0.length; i < ilen; ++i) {
+                               v0 = a0[i];
+                               v1 = a1[i];
+
+                               if (v0 instanceof Array && v1 instanceof Array) {
+                                       if (!helpers.arrayEquals(v0, v1)) {
+                                               return false;
+                                       }
+                               } else if (v0 !== v1) {
+                                       // NOTE: two different object instances will never be equal: {x:20} != {x:20}
+                                       return false;
+                               }
+                       }
+
+                       return true;
+               }
+       };
+
+       /**
+        * Provided for backward compatibility, use Chart.helpers.callback instead.
+        * @function Chart.helpers.callCallback
+        * @deprecated since version 2.6.0
+        * @todo remove at version 3
+        * @private
+        */
+       helpers.callCallback = helpers.callback;
+
+       /**
+        * Provided for backward compatibility, use Array.prototype.indexOf instead.
+        * Array.prototype.indexOf compatibility: Chrome, Opera, Safari, FF1.5+, IE9+
+        * @function Chart.helpers.indexOf
+        * @deprecated since version 2.7.0
+        * @todo remove at version 3
+        * @private
+        */
+       helpers.indexOf = function(array, item, fromIndex) {
+               return Array.prototype.indexOf.call(array, item, fromIndex);
+       };
+};
index d498bc854b58cb670b6c9d97a5dd6480e50c0295..09c23820bf70437a174c565bb29cc29c99657bbf 100644 (file)
@@ -229,7 +229,7 @@ module.exports = function(Chart) {
                        var initial = canvas._chartjs.initial;
                        ['height', 'width'].forEach(function(prop) {
                                var value = initial[prop];
-                               if (value === undefined || value === null) {
+                               if (helpers.isNullOrUndef(value)) {
                                        canvas.removeAttribute(prop);
                                } else {
                                        canvas.setAttribute(prop, value);
index 588e20600fb16fa19fcaadd0e849f97ea74ea71f..0d6344835d2f31ad75379a9266942b7448da0dc1 100644 (file)
@@ -360,7 +360,7 @@ module.exports = function(Chart) {
                                                var centerY = y + offSet;
 
                                                // Draw pointStyle as legend symbol
-                                               Chart.canvasHelpers.drawPoint(ctx, legendItem.pointStyle, radius, centerX, centerY);
+                                               Chart.helpers.canvas.drawPoint(ctx, legendItem.pointStyle, radius, centerX, centerY);
                                        } else {
                                                // Draw box as legend symbol
                                                if (!isLineWidthZero) {
index cc5478adff98d142b46687ad97075e15c230ca70..6b1532c1714fb74d136c94f7d888c53676d5b035 100644 (file)
@@ -2,7 +2,6 @@
 
 module.exports = function(Chart) {
 
-       var helpers = Chart.helpers;
        // Default config for a category scale
        var defaultConfig = {
                position: 'bottom'
@@ -28,13 +27,13 @@ module.exports = function(Chart) {
 
                        if (me.options.ticks.min !== undefined) {
                                // user specified min value
-                               findIndex = helpers.indexOf(labels, me.options.ticks.min);
+                               findIndex = labels.indexOf(me.options.ticks.min);
                                me.minIndex = findIndex !== -1 ? findIndex : me.minIndex;
                        }
 
                        if (me.options.ticks.max !== undefined) {
                                // user specified max value
-                               findIndex = helpers.indexOf(labels, me.options.ticks.max);
+                               findIndex = labels.indexOf(me.options.ticks.max);
                                me.maxIndex = findIndex !== -1 ? findIndex : me.maxIndex;
                        }
 
index 814c24622e0864226b2ae82b27cf94e6f45a2f99..8f4171fee4d57427c157820efdc7f6018e26ae6f 100644 (file)
@@ -89,6 +89,7 @@ Context.prototype._initMethods = function() {
                },
                moveTo: function() {},
                quadraticCurveTo: function() {},
+               rect: function() {},
                restore: function() {},
                rotate: function() {},
                save: function() {},
index 7777c9b16a736822a1382ca63041899930128cfc..c1d1e8710b8b0a624b7de418fee1322da139c864 100644 (file)
@@ -6,59 +6,6 @@ describe('Core helper tests', 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',
@@ -268,14 +215,6 @@ describe('Core helper tests', function() {
                expect(merged.scales.yAxes[2].ticks.callback).toEqual(jasmine.any(Function));
        });
 
-       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) {
@@ -588,20 +527,6 @@ describe('Core helper tests', function() {
                expect(helpers.previousItem(testData, 1, true)).toEqual(0);
        });
 
-       it('should clear a canvas', function() {
-               var context = window.createMockContext();
-               helpers.clear({
-                       width: 100,
-                       height: 150,
-                       ctx: context
-               });
-
-               expect(context.getCalls()).toEqual([{
-                       name: 'clearRect',
-                       args: [0, 0, 100, 150]
-               }]);
-       });
-
        it('should return the width of the longest text in an Array and 2D Array', function() {
                var context = window.createMockContext();
                var font = "normal 12px 'Helvetica Neue', 'Helvetica', 'Arial', sans-serif";
@@ -664,46 +589,6 @@ describe('Core helper tests', function() {
                expect(helpers.numberOfLabelLines(arrayOfThings3)).toEqual(3);
        });
 
-       it('should draw a rounded rectangle', function() {
-               var context = window.createMockContext();
-               helpers.drawRoundedRectangle(context, 10, 20, 30, 40, 5);
-
-               expect(context.getCalls()).toEqual([{
-                       name: 'beginPath',
-                       args: []
-               }, {
-                       name: 'moveTo',
-                       args: [15, 20]
-               }, {
-                       name: 'lineTo',
-                       args: [35, 20]
-               }, {
-                       name: 'quadraticCurveTo',
-                       args: [40, 20, 40, 25]
-               }, {
-                       name: 'lineTo',
-                       args: [40, 55]
-               }, {
-                       name: 'quadraticCurveTo',
-                       args: [40, 60, 35, 60]
-               }, {
-                       name: 'lineTo',
-                       args: [15, 60]
-               }, {
-                       name: 'quadraticCurveTo',
-                       args: [10, 60, 10, 55]
-               }, {
-                       name: 'lineTo',
-                       args: [10, 25]
-               }, {
-                       name: 'quadraticCurveTo',
-                       args: [10, 20, 15, 20]
-               }, {
-                       name: 'closePath',
-                       args: []
-               }]);
-       });
-
        it ('should get the maximum width and height for a node', function() {
                // Create div with fixed size as a test bed
                var div = document.createElement('div');
index 01eba3046ef2d258db391d6023c2e80f19f663a3..f09b912d3c0288b4f952cb10d4a9fcc2fed5f7e8 100644 (file)
@@ -209,9 +209,9 @@ describe('Point element tests', function() {
                }]);
 
                var drawRoundedRectangleSpy = jasmine.createSpy('drawRoundedRectangle');
-               var drawRoundedRectangle = Chart.helpers.drawRoundedRectangle;
+               var drawRoundedRectangle = Chart.helpers.canvas.roundedRect;
                var offset = point._view.radius / Math.SQRT2;
-               Chart.helpers.drawRoundedRectangle = drawRoundedRectangleSpy;
+               Chart.helpers.canvas.roundedRect = drawRoundedRectangleSpy;
                mockContext.resetCalls();
                point._view.pointStyle = 'rectRounded';
                point.draw();
@@ -231,7 +231,7 @@ describe('Point element tests', function() {
                        })
                );
 
-               Chart.helpers.drawRoundedRectangle = drawRoundedRectangle;
+               Chart.helpers.canvas.roundedRect = drawRoundedRectangle;
                mockContext.resetCalls();
                point._view.pointStyle = 'rectRot';
                point.draw();
index 917c1529a0992bef6141dd91855f946c1dbc151e..50a5cc0530c6cba999cdd58dcebb6effc33e9bdf 100644 (file)
@@ -1,8 +1,8 @@
 describe('Deprecations', function() {
        describe('Version 2.7.0', function() {
                describe('Chart.Controller.update(duration, lazy)', function() {
-                       beforeEach(function() {
-                               this.chart = acquireChart({
+                       it('should add an animation with the provided options', function() {
+                               var chart = acquireChart({
                                        type: 'doughnut',
                                        options: {
                                                animation: {
@@ -12,14 +12,12 @@ describe('Deprecations', function() {
                                        }
                                });
 
-                               this.addAnimationSpy = spyOn(Chart.animationService, 'addAnimation');
-                       });
+                               spyOn(Chart.animationService, 'addAnimation');
 
-                       it('should add an animation with the provided options', function() {
-                               this.chart.update(800, false);
+                               chart.update(800, false);
 
-                               expect(this.addAnimationSpy).toHaveBeenCalledWith(
-                                       this.chart,
+                               expect(Chart.animationService.addAnimation).toHaveBeenCalledWith(
+                                       chart,
                                        jasmine.objectContaining({easing: 'linear'}),
                                        800,
                                        false
@@ -28,8 +26,8 @@ describe('Deprecations', function() {
                });
 
                describe('Chart.Controller.render(duration, lazy)', function() {
-                       beforeEach(function() {
-                               this.chart = acquireChart({
+                       it('should add an animation with the provided options', function() {
+                               var chart = acquireChart({
                                        type: 'doughnut',
                                        options: {
                                                animation: {
@@ -39,20 +37,56 @@ describe('Deprecations', function() {
                                        }
                                });
 
-                               this.addAnimationSpy = spyOn(Chart.animationService, 'addAnimation');
-                       });
+                               spyOn(Chart.animationService, 'addAnimation');
 
-                       it('should add an animation with the provided options', function() {
-                               this.chart.render(800, true);
+                               chart.render(800, true);
 
-                               expect(this.addAnimationSpy).toHaveBeenCalledWith(
-                                       this.chart,
+                               expect(Chart.animationService.addAnimation).toHaveBeenCalledWith(
+                                       chart,
                                        jasmine.objectContaining({easing: 'linear'}),
                                        800,
                                        true
                                );
                        });
                });
+
+               describe('Chart.helpers.indexOf', function() {
+                       it('should be defined and a function', function() {
+                               expect(Chart.helpers.indexOf).toBeDefined();
+                               expect(typeof Chart.helpers.indexOf).toBe('function');
+                       });
+                       it('should returns the correct index', function() {
+                               expect(Chart.helpers.indexOf([1, 2, 42], 42)).toBe(2);
+                               expect(Chart.helpers.indexOf([1, 2, 42], 3)).toBe(-1);
+                               expect(Chart.helpers.indexOf([1, 42, 2, 42], 42, 2)).toBe(3);
+                               expect(Chart.helpers.indexOf([1, 42, 2, 42], 3, 2)).toBe(-1);
+                       });
+               });
+
+               describe('Chart.helpers.clear', function() {
+                       it('should be defined and an alias of Chart.helpers.canvas.clear', function() {
+                               expect(Chart.helpers.clear).toBeDefined();
+                               expect(Chart.helpers.clear).toBe(Chart.helpers.canvas.clear);
+                       });
+               });
+
+               describe('Chart.helpers.drawRoundedRectangle', function() {
+                       it('should be defined and a function', function() {
+                               expect(Chart.helpers.drawRoundedRectangle).toBeDefined();
+                               expect(typeof Chart.helpers.drawRoundedRectangle).toBe('function');
+                       });
+                       it('should call Chart.helpers.canvas.roundedRect', function() {
+                               var ctx = window.createMockContext();
+                               spyOn(Chart.helpers.canvas, 'roundedRect');
+
+                               Chart.helpers.drawRoundedRectangle(ctx, 10, 20, 30, 40, 5);
+
+                               var calls = ctx.getCalls();
+                               expect(calls[0]).toEqual({name: 'beginPath', args: []});
+                               expect(calls[calls.length-1]).toEqual({name: 'closePath', args: []});
+                               expect(Chart.helpers.canvas.roundedRect).toHaveBeenCalledWith(ctx, 10, 20, 30, 40, 5);
+                       });
+               });
        });
 
        describe('Version 2.6.0', function() {
@@ -163,6 +197,13 @@ describe('Deprecations', function() {
                                });
                        });
                });
+
+               describe('Chart.helpers.callCallback', function() {
+                       it('should be defined and an alias of Chart.helpers.callback', function() {
+                               expect(Chart.helpers.callCallback).toBeDefined();
+                               expect(Chart.helpers.callCallback).toBe(Chart.helpers.callback);
+                       });
+               });
        });
 
        describe('Version 2.5.0', function() {
diff --git a/test/specs/helpers.canvas.tests.js b/test/specs/helpers.canvas.tests.js
new file mode 100644 (file)
index 0000000..8132652
--- /dev/null
@@ -0,0 +1,50 @@
+'use strict';
+
+describe('Chart.helpers.canvas', function() {
+       var helpers = Chart.helpers;
+
+       describe('clear', function() {
+               it('should clear the chart canvas', function() {
+                       var chart = acquireChart({}, {
+                               canvas: {
+                                       style: 'width: 150px; height: 245px'
+                               }
+                       });
+
+                       spyOn(chart.ctx, 'clearRect');
+
+                       helpers.canvas.clear(chart);
+
+                       expect(chart.ctx.clearRect.calls.count()).toBe(1);
+                       expect(chart.ctx.clearRect.calls.first().object).toBe(chart.ctx);
+                       expect(chart.ctx.clearRect.calls.first().args).toEqual([0, 0, 150, 245]);
+               });
+       });
+
+       describe('roundedRect', function() {
+               it('should create a rounded rectangle path', function() {
+                       var context = window.createMockContext();
+
+                       helpers.canvas.roundedRect(context, 10, 20, 30, 40, 5);
+
+                       expect(context.getCalls()).toEqual([
+                               {name: 'moveTo', args: [15, 20]},
+                               {name: 'lineTo', args: [35, 20]},
+                               {name: 'quadraticCurveTo', args: [40, 20, 40, 25]},
+                               {name: 'lineTo', args: [40, 55]},
+                               {name: 'quadraticCurveTo', args: [40, 60, 35, 60]},
+                               {name: 'lineTo', args: [15, 60]},
+                               {name: 'quadraticCurveTo', args: [10, 60, 10, 55]},
+                               {name: 'lineTo', args: [10, 25]},
+                               {name: 'quadraticCurveTo', args: [10, 20, 15, 20]}
+                       ]);
+               });
+               it('should optimize path if radius is 0', function() {
+                       var context = window.createMockContext();
+
+                       helpers.canvas.roundedRect(context, 10, 20, 30, 40, 0);
+
+                       expect(context.getCalls()).toEqual([{name: 'rect', args: [10, 20, 30, 40]}]);
+               });
+       });
+});
diff --git a/test/specs/helpers.core.tests.js b/test/specs/helpers.core.tests.js
new file mode 100644 (file)
index 0000000..9bb5f5f
--- /dev/null
@@ -0,0 +1,239 @@
+'use strict';
+
+describe('Chart.helpers.core', function() {
+       var helpers = Chart.helpers;
+
+       describe('noop', function() {
+               it('should be callable', function() {
+                       expect(helpers.noop).toBeDefined();
+                       expect(typeof helpers.noop).toBe('function');
+                       expect(typeof helpers.noop.call).toBe('function');
+               });
+               it('should returns "undefined"', function() {
+                       expect(helpers.noop(42)).not.toBeDefined();
+                       expect(helpers.noop.call(this, 42)).not.toBeDefined();
+               });
+       });
+
+       describe('isArray', function() {
+               it('should return true if value is an array', function() {
+                       expect(helpers.isArray([])).toBeTruthy();
+                       expect(helpers.isArray([42])).toBeTruthy();
+                       expect(helpers.isArray(new Array())).toBeTruthy();
+                       expect(helpers.isArray(Array.prototype)).toBeTruthy();
+               });
+               it('should return false if value is not an array', function() {
+                       expect(helpers.isArray()).toBeFalsy();
+                       expect(helpers.isArray({})).toBeFalsy();
+                       expect(helpers.isArray(undefined)).toBeFalsy();
+                       expect(helpers.isArray(null)).toBeFalsy();
+                       expect(helpers.isArray(true)).toBeFalsy();
+                       expect(helpers.isArray(false)).toBeFalsy();
+                       expect(helpers.isArray(42)).toBeFalsy();
+                       expect(helpers.isArray('Array')).toBeFalsy();
+                       expect(helpers.isArray({__proto__: Array.prototype})).toBeFalsy();
+               });
+       });
+
+       describe('isObject', function() {
+               it('should return true if value is an object', function() {
+                       expect(helpers.isObject({})).toBeTruthy();
+                       expect(helpers.isObject({a: 42})).toBeTruthy();
+                       expect(helpers.isObject(new Object())).toBeTruthy();
+               });
+               it('should return false if value is not an object', function() {
+                       expect(helpers.isObject()).toBeFalsy();
+                       expect(helpers.isObject(undefined)).toBeFalsy();
+                       expect(helpers.isObject(null)).toBeFalsy();
+                       expect(helpers.isObject(true)).toBeFalsy();
+                       expect(helpers.isObject(false)).toBeFalsy();
+                       expect(helpers.isObject(42)).toBeFalsy();
+                       expect(helpers.isObject('Object')).toBeFalsy();
+                       expect(helpers.isObject([])).toBeFalsy();
+                       expect(helpers.isObject([42])).toBeFalsy();
+                       expect(helpers.isObject(new Array())).toBeFalsy();
+                       expect(helpers.isObject(new Date())).toBeFalsy();
+               });
+       });
+
+       describe('isNullOrUndef', function() {
+               it('should return true if value is null/undefined', function() {
+                       expect(helpers.isNullOrUndef(null)).toBeTruthy();
+                       expect(helpers.isNullOrUndef(undefined)).toBeTruthy();
+               });
+               it('should return false if value is not null/undefined', function() {
+                       expect(helpers.isNullOrUndef(true)).toBeFalsy();
+                       expect(helpers.isNullOrUndef(false)).toBeFalsy();
+                       expect(helpers.isNullOrUndef('')).toBeFalsy();
+                       expect(helpers.isNullOrUndef('String')).toBeFalsy();
+                       expect(helpers.isNullOrUndef(0)).toBeFalsy();
+                       expect(helpers.isNullOrUndef([])).toBeFalsy();
+                       expect(helpers.isNullOrUndef({})).toBeFalsy();
+                       expect(helpers.isNullOrUndef([42])).toBeFalsy();
+                       expect(helpers.isNullOrUndef(new Date())).toBeFalsy();
+               });
+       });
+
+       describe('getValueOrDefault', function() {
+               it('should return value if defined', function() {
+                       var object = {};
+                       var array = [];
+
+                       expect(helpers.getValueOrDefault(null, 42)).toBe(null);
+                       expect(helpers.getValueOrDefault(false, 42)).toBe(false);
+                       expect(helpers.getValueOrDefault(object, 42)).toBe(object);
+                       expect(helpers.getValueOrDefault(array, 42)).toBe(array);
+                       expect(helpers.getValueOrDefault('', 42)).toBe('');
+                       expect(helpers.getValueOrDefault(0, 42)).toBe(0);
+               });
+               it('should return default if undefined', function() {
+                       expect(helpers.getValueOrDefault(undefined, 42)).toBe(42);
+                       expect(helpers.getValueOrDefault({}.foo, 42)).toBe(42);
+               });
+       });
+
+       describe('getValueAtIndexOrDefault', function() {
+               it('should return the passed value if not an array', function() {
+                       expect(helpers.getValueAtIndexOrDefault(0, 0, 42)).toBe(0);
+                       expect(helpers.getValueAtIndexOrDefault('', 0, 42)).toBe('');
+                       expect(helpers.getValueAtIndexOrDefault(false, 0, 42)).toBe(false);
+                       expect(helpers.getValueAtIndexOrDefault(98, 0, 42)).toBe(98);
+               });
+               it('should return the default value if the passed value is null or undefined', function() {
+                       expect(helpers.getValueAtIndexOrDefault(null, 0, 42)).toBe(42);
+                       expect(helpers.getValueAtIndexOrDefault(undefined, 0, 42)).toBe(42);
+               });
+               it('should return the value at index if defined', function() {
+                       expect(helpers.getValueAtIndexOrDefault([1, false, 'foo'], 1, 42)).toBe(false);
+                       expect(helpers.getValueAtIndexOrDefault([1, false, 'foo'], 2, 42)).toBe('foo');
+               });
+               it('should return the default value if value at index is undefined', function() {
+                       expect(helpers.getValueAtIndexOrDefault([1, false, 'foo'], 3, 42)).toBe(42);
+                       expect(helpers.getValueAtIndexOrDefault([1, undefined, 'foo'], 1, 42)).toBe(42);
+               });
+       });
+
+       describe('callback', function() {
+               it('should return undefined if fn is not a function', function() {
+                       expect(helpers.callback()).not.toBeDefined();
+                       expect(helpers.callback(null)).not.toBeDefined();
+                       expect(helpers.callback(42)).not.toBeDefined();
+                       expect(helpers.callback([])).not.toBeDefined();
+                       expect(helpers.callback({})).not.toBeDefined();
+               });
+               it('should call fn with the given args', function() {
+                       var spy = jasmine.createSpy('spy');
+                       helpers.callback(spy);
+                       helpers.callback(spy, []);
+                       helpers.callback(spy, ['foo']);
+                       helpers.callback(spy, [42, 'bar']);
+
+                       expect(spy.calls.argsFor(0)).toEqual([]);
+                       expect(spy.calls.argsFor(1)).toEqual([]);
+                       expect(spy.calls.argsFor(2)).toEqual(['foo']);
+                       expect(spy.calls.argsFor(3)).toEqual([42, 'bar']);
+               });
+               it('should call fn with the given scope', function() {
+                       var spy = jasmine.createSpy('spy');
+                       var scope = {};
+
+                       helpers.callback(spy);
+                       helpers.callback(spy, [], null);
+                       helpers.callback(spy, [], undefined);
+                       helpers.callback(spy, [], scope);
+
+                       expect(spy.calls.all()[0].object).toBe(window);
+                       expect(spy.calls.all()[1].object).toBe(window);
+                       expect(spy.calls.all()[2].object).toBe(window);
+                       expect(spy.calls.all()[3].object).toBe(scope);
+               });
+               it('should return the value returned by fn', function() {
+                       expect(helpers.callback(helpers.noop, [41])).toBe(undefined);
+                       expect(helpers.callback(function(i) {
+                               return i+1;
+                       }, [41])).toBe(42);
+               });
+       });
+
+       describe('each', function() {
+               it('should iterate over an array forward if reverse === false', function() {
+                       var scope = {};
+                       var scopes = [];
+                       var items = [];
+                       var keys = [];
+
+                       helpers.each(['foo', 'bar', 42], function(item, key) {
+                               scopes.push(this);
+                               items.push(item);
+                               keys.push(key);
+                       }, scope);
+
+                       expect(scopes).toEqual([scope, scope, scope]);
+                       expect(items).toEqual(['foo', 'bar', 42]);
+                       expect(keys).toEqual([0, 1, 2]);
+               });
+               it('should iterate over an array backward if reverse === true', function() {
+                       var scope = {};
+                       var scopes = [];
+                       var items = [];
+                       var keys = [];
+
+                       helpers.each(['foo', 'bar', 42], function(item, key) {
+                               scopes.push(this);
+                               items.push(item);
+                               keys.push(key);
+                       }, scope, true);
+
+                       expect(scopes).toEqual([scope, scope, scope]);
+                       expect(items).toEqual([42, 'bar', 'foo']);
+                       expect(keys).toEqual([2, 1, 0]);
+               });
+               it('should iterate over object properties', function() {
+                       var scope = {};
+                       var scopes = [];
+                       var items = [];
+
+                       helpers.each({a: 'foo', b: 'bar', c: 42}, function(item, key) {
+                               scopes.push(this);
+                               items[key] = item;
+                       }, scope);
+
+                       expect(scopes).toEqual([scope, scope, scope]);
+                       expect(items).toEqual(jasmine.objectContaining({a: 'foo', b: 'bar', c: 42}));
+               });
+               it('should not throw when called with a non iterable object', function() {
+                       expect(function() {
+                               helpers.each(undefined);
+                       }).not.toThrow();
+                       expect(function() {
+                               helpers.each(null);
+                       }).not.toThrow();
+                       expect(function() {
+                               helpers.each(42);
+                       }).not.toThrow();
+               });
+       });
+
+       describe('arrayEquals', function() {
+               it('should return false if arrays are not the same', function() {
+                       expect(helpers.arrayEquals([], [42])).toBeFalsy();
+                       expect(helpers.arrayEquals([42], ['42'])).toBeFalsy();
+                       expect(helpers.arrayEquals([1, 2, 3], [1, 2, 3, 4])).toBeFalsy();
+                       expect(helpers.arrayEquals(['foo', 'bar'], ['bar', 'foo'])).toBeFalsy();
+                       expect(helpers.arrayEquals([1, 2, 3], [1, 2, 'foo'])).toBeFalsy();
+                       expect(helpers.arrayEquals([1, 2, [3, 4]], [1, 2, [3, 'foo']])).toBeFalsy();
+                       expect(helpers.arrayEquals([{a: 42}], [{a: 42}])).toBeFalsy();
+               });
+               it('should return false if arrays are not the same', function() {
+                       var o0 = {};
+                       var o1 = {};
+                       var o2 = {};
+
+                       expect(helpers.arrayEquals([], [])).toBeTruthy();
+                       expect(helpers.arrayEquals([1, 2, 3], [1, 2, 3])).toBeTruthy();
+                       expect(helpers.arrayEquals(['foo', 'bar'], ['foo', 'bar'])).toBeTruthy();
+                       expect(helpers.arrayEquals([true, false, true], [true, false, true])).toBeTruthy();
+                       expect(helpers.arrayEquals([o0, o1, o2], [o0, o1, o2])).toBeTruthy();
+               });
+       });
+});