Chart.js/test/core.helpers.tests.js
2016-08-22 17:53:31 -04:00

935 lines
23 KiB
JavaScript

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
}, {
display: true,
gridLines: {
color: "rgba(0, 0, 0, 0.1)",
drawBorder: true,
drawOnChartArea: true,
drawTicks: true, // draw ticks extending towards the label
tickMarkLength: 10,
lineWidth: 1,
offsetGridLines: false,
display: true,
zeroLineColor: "rgba(0,0,0,0.25)",
zeroLineWidth: 1,
borderDash: [],
borderDashOffset: 0.0
},
position: "right",
scaleLabel: {
labelString: '',
display: false,
},
ticks: {
beginAtZero: false,
minRotation: 0,
maxRotation: 50,
mirror: false,
padding: 10,
reverse: false,
display: true,
callback: merged.scales.yAxes[1].ticks.callback, // make it nicer, then check explicitly below
autoSkip: true,
autoSkipPadding: 0,
labelOffset: 0,
},
type: 'linear'
}, {
display: true,
gridLines: {
color: "rgba(0, 0, 0, 0.1)",
drawBorder: true,
drawOnChartArea: true,
drawTicks: true, // draw ticks extending towards the label,
tickMarkLength: 10,
lineWidth: 1,
offsetGridLines: false,
display: true,
zeroLineColor: "rgba(0,0,0,0.25)",
zeroLineWidth: 1,
borderDash: [],
borderDashOffset: 0.0
},
position: "left",
scaleLabel: {
labelString: '',
display: false,
},
ticks: {
beginAtZero: false,
minRotation: 0,
maxRotation: 50,
mirror: false,
padding: 10,
reverse: false,
display: true,
callback: merged.scales.yAxes[2].ticks.callback, // make it nicer, then check explicitly below
autoSkip: true,
autoSkipPadding: 0,
labelOffset: 0,
},
type: 'linear'
}]
}
});
// Are these actually functions
expect(merged.scales.yAxes[1].ticks.callback).toEqual(jasmine.any(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) {
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)).toBeCloseTo(3, 1e-9);
});
it('should correctly determine if two numbers are essentially equal', function() {
expect(helpers.almostEquals(0, Number.EPSILON, 2 * Number.EPSILON)).toBe(true);
expect(helpers.almostEquals(1, 1.1, 0.0001)).toBe(false);
expect(helpers.almostEquals(1e30, 1e30 + Number.EPSILON, 0)).toBe(false);
expect(helpers.almostEquals(1e30, 1e30 + Number.EPSILON, 2 * Number.EPSILON)).toBe(true);
});
it('should generate integer ids', function() {
var uid = helpers.uid();
expect(uid).toEqual(jasmine.any(Number));
expect(helpers.uid()).toBe(uid + 1);
expect(helpers.uid()).toBe(uid + 2);
expect(helpers.uid()).toBe(uid + 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 spline curves with monotone cubic interpolation', function() {
var dataPoints = [
{ _model: { x: 0, y: 0, skip: false } },
{ _model: { x: 3, y: 6, skip: false } },
{ _model: { x: 9, y: 6, skip: false } },
{ _model: { x: 12, y: 60, skip: false } },
{ _model: { x: 15, y: 60, skip: false } },
{ _model: { x: 18, y: 120, skip: false } },
{ _model: { x: null, y: null, skip: true } },
{ _model: { x: 21, y: 180, skip: false } },
{ _model: { x: 24, y: 120, skip: false } },
{ _model: { x: 27, y: 125, skip: false } },
{ _model: { x: 30, y: 105, skip: false } },
{ _model: { x: 33, y: 110, skip: false } },
{ _model: { x: 36, y: 170, skip: false } }
];
helpers.splineCurveMonotone(dataPoints);
expect(dataPoints).toEqual([{
_model: {
x: 0,
y: 0,
skip: false,
controlPointNextX: 1,
controlPointNextY: 2
}
},
{
_model: {
x: 3,
y: 6,
skip: false,
controlPointPreviousX: 2,
controlPointPreviousY: 6,
controlPointNextX: 5,
controlPointNextY: 6
}
},
{
_model: {
x: 9,
y: 6,
skip: false,
controlPointPreviousX: 7,
controlPointPreviousY: 6,
controlPointNextX: 10,
controlPointNextY: 6
}
},
{
_model: {
x: 12,
y: 60,
skip: false,
controlPointPreviousX: 11,
controlPointPreviousY: 60,
controlPointNextX: 13,
controlPointNextY: 60
}
},
{
_model: {
x: 15,
y: 60,
skip: false,
controlPointPreviousX: 14,
controlPointPreviousY: 60,
controlPointNextX: 16,
controlPointNextY: 60
}
},
{
_model: {
x: 18,
y: 120,
skip: false,
controlPointPreviousX: 17,
controlPointPreviousY: 100
}
},
{
_model: {
x: null,
y: null,
skip: true
}
},
{
_model: {
x: 21,
y: 180,
skip: false,
controlPointNextX: 22,
controlPointNextY: 160
}
},
{
_model: {
x: 24,
y: 120,
skip: false,
controlPointPreviousX: 23,
controlPointPreviousY: 120,
controlPointNextX: 25,
controlPointNextY: 120
}
},
{
_model: {
x: 27,
y: 125,
skip: false,
controlPointPreviousX: 26,
controlPointPreviousY: 125,
controlPointNextX: 28,
controlPointNextY: 125
}
},
{
_model: {
x: 30,
y: 105,
skip: false,
controlPointPreviousX: 29,
controlPointPreviousY: 105,
controlPointNextX: 31,
controlPointNextY: 105
}
},
{
_model: {
x: 33,
y: 110,
skip: false,
controlPointPreviousX: 32,
controlPointPreviousY: 105,
controlPointNextX: 34,
controlPointNextY: 115
}
},
{
_model: {
x: 36,
y: 170,
skip: false,
controlPointPreviousX: 35,
controlPointPreviousY: 150
}
}]);
});
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);
});
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";
var arrayOfThings_1D = ['FooBar','Bar'];
var arrayOfThings_2D = [['FooBar_1','Bar_2'],'Foo_1'];
// Regardless 'FooBar' is the longest label it should return (charcters * 10)
expect(helpers.longestText(context, font, arrayOfThings_1D, {})).toEqual(60);
expect(helpers.longestText(context, font, arrayOfThings_2D, {})).toEqual(80);
// We check to make sure we made the right calls to the canvas.
expect(context.getCalls()).toEqual([{
name: 'measureText',
args: ['FooBar']
}, {
name: 'measureText',
args: ['Bar']
}, {
name: 'measureText',
args: ['FooBar_1']
}, {
name: 'measureText',
args: ['Bar_2']
}, {
name: 'measureText',
args: ['Foo_1']
}]);
});
it('compare text with current longest and update', function() {
var context = window.createMockContext();
var data = {};
var gc = [];
var longest = 70;
expect(helpers.measureText(context, data, gc, longest, 'foobar')).toEqual(70);
expect(helpers.measureText(context, data, gc, longest, 'foobar_')).toEqual(70);
expect(helpers.measureText(context, data, gc, longest, 'foobar_1')).toEqual(80);
// We check to make sure we made the right calls to the canvas.
expect(context.getCalls()).toEqual([{
name: 'measureText',
args: ['foobar']
}, {
name: 'measureText',
args: ['foobar_']
}, {
name: 'measureText',
args: ['foobar_1']
}]);
});
it('count look at all the labels and return maximum number of lines', function() {
var context = window.createMockContext();
var arrayOfThings_1 = ['Foo','Bar'];
var arrayOfThings_2 = [['Foo','Bar'],'Foo'];
var arrayOfThings_3 = [['Foo','Bar','Boo'],['Foo','Bar'],'Foo'];
expect(helpers.numberOfLabelLines(arrayOfThings_1)).toEqual(1);
expect(helpers.numberOfLabelLines(arrayOfThings_2)).toEqual(2);
expect(helpers.numberOfLabelLines(arrayOfThings_3)).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');
div.style.width = "200px";
div.style.height = "300px";
document.body.appendChild(div);
// Create the div we want to get the max size for
var innerDiv = document.createElement('div');
div.appendChild(innerDiv);
expect(helpers.getMaximumWidth(innerDiv)).toBe(200);
expect(helpers.getMaximumHeight(innerDiv)).toBe(300);
document.body.removeChild(div);
});
it ('should get the maximum width of a node that has a max-width style', function() {
// Create div with fixed size as a test bed
var div = document.createElement('div');
div.style.width = "200px";
div.style.height = "300px";
document.body.appendChild(div);
// Create the div we want to get the max size for and set a max-width style
var innerDiv = document.createElement('div');
innerDiv.style.maxWidth = "150px";
div.appendChild(innerDiv);
expect(helpers.getMaximumWidth(innerDiv)).toBe(150);
document.body.removeChild(div);
});
it ('should get the maximum height of a node that has a max-height style', function() {
// Create div with fixed size as a test bed
var div = document.createElement('div');
div.style.width = "200px";
div.style.height = "300px";
document.body.appendChild(div);
// Create the div we want to get the max size for and set a max-height style
var innerDiv = document.createElement('div');
innerDiv.style.maxHeight = "150px";
div.appendChild(innerDiv);
expect(helpers.getMaximumHeight(innerDiv)).toBe(150);
document.body.removeChild(div);
});
it ('should get the maximum width of a node when the parent has a max-width style', function() {
// Create div with fixed size as a test bed
var div = document.createElement('div');
div.style.width = "200px";
div.style.height = "300px";
document.body.appendChild(div);
// Create an inner wrapper around our div we want to size and give that a max-width style
var parentDiv = document.createElement('div');
parentDiv.style.maxWidth = "150px";
div.appendChild(parentDiv);
// Create the div we want to get the max size for
var innerDiv = document.createElement('div');
parentDiv.appendChild(innerDiv);
expect(helpers.getMaximumWidth(innerDiv)).toBe(150);
document.body.removeChild(div);
});
it ('should get the maximum height of a node when the parent has a max-height style', function() {
// Create div with fixed size as a test bed
var div = document.createElement('div');
div.style.width = "200px";
div.style.height = "300px";
document.body.appendChild(div);
// Create an inner wrapper around our div we want to size and give that a max-height style
var parentDiv = document.createElement('div');
parentDiv.style.maxHeight = "150px";
div.appendChild(parentDiv);
// Create the div we want to get the max size for
var innerDiv = document.createElement('div');
innerDiv.style.height = "300px"; // make it large
parentDiv.appendChild(innerDiv);
expect(helpers.getMaximumHeight(innerDiv)).toBe(150);
document.body.removeChild(div);
});
it ('should get the maximum width of a node that has a percentage max-width style', function() {
// Create div with fixed size as a test bed
var div = document.createElement('div');
div.style.width = "200px";
div.style.height = "300px";
document.body.appendChild(div);
// Create the div we want to get the max size for and set a max-width style
var innerDiv = document.createElement('div');
innerDiv.style.maxWidth = "50%";
div.appendChild(innerDiv);
expect(helpers.getMaximumWidth(innerDiv)).toBe(100);
document.body.removeChild(div);
});
it ('should get the maximum height of a node that has a percentage max-height style', function() {
// Create div with fixed size as a test bed
var div = document.createElement('div');
div.style.width = "200px";
div.style.height = "300px";
document.body.appendChild(div);
// Create the div we want to get the max size for and set a max-height style
var innerDiv = document.createElement('div');
innerDiv.style.maxHeight = "50%";
div.appendChild(innerDiv);
expect(helpers.getMaximumHeight(innerDiv)).toBe(150);
document.body.removeChild(div);
});
it ('should get the maximum width of a node when the parent has a percentage max-width style', function() {
// Create div with fixed size as a test bed
var div = document.createElement('div');
div.style.width = "200px";
div.style.height = "300px";
document.body.appendChild(div);
// Create an inner wrapper around our div we want to size and give that a max-width style
var parentDiv = document.createElement('div');
parentDiv.style.maxWidth = "50%";
div.appendChild(parentDiv);
// Create the div we want to get the max size for
var innerDiv = document.createElement('div');
parentDiv.appendChild(innerDiv);
expect(helpers.getMaximumWidth(innerDiv)).toBe(100);
document.body.removeChild(div);
});
it ('should get the maximum height of a node when the parent has a percentage max-height style', function() {
// Create div with fixed size as a test bed
var div = document.createElement('div');
div.style.width = "200px";
div.style.height = "300px";
document.body.appendChild(div);
// Create an inner wrapper around our div we want to size and give that a max-height style
var parentDiv = document.createElement('div');
parentDiv.style.maxHeight = "50%";
div.appendChild(parentDiv);
var innerDiv = document.createElement('div');
innerDiv.style.height = "300px"; // make it large
parentDiv.appendChild(innerDiv);
expect(helpers.getMaximumHeight(innerDiv)).toBe(150);
document.body.removeChild(div);
});
describe('Color helper', function() {
function isColorInstance(obj) {
return typeof obj === 'object' && obj.hasOwnProperty('values') && obj.values.hasOwnProperty('rgb');
}
it('should return a color when called with a color', function() {
expect(isColorInstance(helpers.color('rgb(1, 2, 3)'))).toBe(true);
});
it('should return a color when called with a CanvasGradient instance', function() {
var context = document.createElement('canvas').getContext('2d');
var gradient = context.createLinearGradient(0, 1, 2, 3);
expect(isColorInstance(helpers.color(gradient))).toBe(true);
});
});
describe('Background hover color helper', function() {
it('should return a CanvasPattern when called with a CanvasPattern', function(done) {
var dots = new Image();
dots.src = 'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAA4AAAAOCAMAAAAolt3jAAAAD1BMVEUAAAD///////////////+PQt5oAAAABXRSTlMAHlFhZsfk/BEAAAAqSURBVHgBY2BgZGJmYmSAAUYWEIDzmcBcJhiXGcxlRpPFrhdmMiqgvX0AcGIBEUAo6UAAAAAASUVORK5CYII=';
dots.onload = function() {
var chartContext = document.createElement('canvas').getContext('2d');
var patternCanvas = document.createElement('canvas');
var patternContext = patternCanvas.getContext('2d');
var pattern = patternContext.createPattern(dots, 'repeat');
patternContext.fillStyle = pattern;
var backgroundColor = helpers.getHoverColor(chartContext.createPattern(patternCanvas, 'repeat'));
expect(backgroundColor instanceof CanvasPattern).toBe(true);
done();
}
});
it('should return a modified version of color when called with a color', function() {
var originalColorRGB = 'rgb(70, 191, 189)';
expect(helpers.getHoverColor('#46BFBD')).not.toEqual(originalColorRGB);
});
});
});