mirror of
https://github.com/chartjs/Chart.js.git
synced 2024-10-07 04:39:06 +02:00
Merge pull request #1425 from etimberg/feature/unit-test
Unit tests for elements
This commit is contained in:
commit
accb5ed654
@ -57,12 +57,6 @@
|
||||
});
|
||||
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);
|
||||
@ -84,7 +78,13 @@
|
||||
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
|
||||
}
|
||||
@ -143,12 +143,12 @@
|
||||
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;
|
||||
@ -176,7 +176,7 @@
|
||||
},
|
||||
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++) {
|
||||
@ -188,7 +188,7 @@
|
||||
},
|
||||
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--) {
|
||||
@ -259,18 +259,6 @@
|
||||
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();
|
||||
@ -335,94 +323,16 @@
|
||||
},
|
||||
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 index <= 0 ? collection[collection.length - 1] : collection[index - 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);
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
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) {
|
||||
@ -505,17 +415,6 @@
|
||||
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/
|
||||
|
@ -37,21 +37,24 @@
|
||||
|
||||
var vm = this._view;
|
||||
|
||||
var pointRelativePosition = helpers.getAngleFromPoint(vm, {
|
||||
x: chartX,
|
||||
y: chartY
|
||||
});
|
||||
if (vm) {
|
||||
var pointRelativePosition = helpers.getAngleFromPoint(vm, {
|
||||
x: chartX,
|
||||
y: chartY
|
||||
});
|
||||
|
||||
// Put into the range of (-PI/2, 3PI/2]
|
||||
var startAngle = vm.startAngle < (-0.5 * Math.PI) ? vm.startAngle + (2.0 * Math.PI) : vm.startAngle > (1.5 * Math.PI) ? vm.startAngle - (2.0 * Math.PI) : vm.startAngle;
|
||||
var endAngle = vm.endAngle < (-0.5 * Math.PI) ? vm.endAngle + (2.0 * Math.PI) : vm.endAngle > (1.5 * Math.PI) ? vm.endAngle - (2.0 * Math.PI) : vm.endAngle
|
||||
// Put into the range of (-PI/2, 3PI/2]
|
||||
var startAngle = vm.startAngle < (-0.5 * Math.PI) ? vm.startAngle + (2.0 * Math.PI) : vm.startAngle > (1.5 * Math.PI) ? vm.startAngle - (2.0 * Math.PI) : vm.startAngle;
|
||||
var endAngle = vm.endAngle < (-0.5 * Math.PI) ? vm.endAngle + (2.0 * Math.PI) : vm.endAngle > (1.5 * Math.PI) ? vm.endAngle - (2.0 * Math.PI) : vm.endAngle
|
||||
|
||||
//Check if within the range of the open/close angle
|
||||
var betweenAngles = (pointRelativePosition.angle >= startAngle && pointRelativePosition.angle <= endAngle),
|
||||
withinRadius = (pointRelativePosition.distance >= vm.innerRadius && pointRelativePosition.distance <= vm.outerRadius);
|
||||
//Check if within the range of the open/close angle
|
||||
var betweenAngles = (pointRelativePosition.angle >= startAngle && pointRelativePosition.angle <= endAngle),
|
||||
withinRadius = (pointRelativePosition.distance >= vm.innerRadius && pointRelativePosition.distance <= vm.outerRadius);
|
||||
|
||||
return (betweenAngles && withinRadius);
|
||||
//Ensure within the outside of the arc centre, but inside arc outer
|
||||
return (betweenAngles && withinRadius);
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
},
|
||||
tooltipPosition: function() {
|
||||
var vm = this._view;
|
||||
|
@ -114,7 +114,7 @@
|
||||
|
||||
// Now draw the line between all the points with any borders
|
||||
ctx.lineCap = vm.borderCapStyle || Chart.defaults.global.elements.line.borderCapStyle;
|
||||
|
||||
|
||||
// IE 9 and 10 do not support line dash
|
||||
if (ctx.setLineDash) {
|
||||
ctx.setLineDash(vm.borderDash || Chart.defaults.global.elements.line.borderDash);
|
||||
@ -122,7 +122,7 @@
|
||||
|
||||
ctx.lineDashOffset = vm.borderDashOffset || Chart.defaults.global.elements.line.borderDashOffset;
|
||||
ctx.lineJoin = vm.borderJoinStyle || Chart.defaults.global.elements.line.borderJoinStyle;
|
||||
ctx.lineWidth = vm.borderWidth || Chart.defaults.global.defaultColor;
|
||||
ctx.lineWidth = vm.borderWidth || Chart.defaults.global.elements.line.borderWidth;
|
||||
ctx.strokeStyle = vm.borderColor || Chart.defaults.global.defaultColor;
|
||||
ctx.beginPath();
|
||||
|
||||
@ -184,10 +184,9 @@
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
ctx.stroke();
|
||||
ctx.restore();
|
||||
},
|
||||
});
|
||||
|
||||
}).call(this);
|
||||
}).call(this);
|
@ -32,8 +32,13 @@
|
||||
Chart.elements.Point = Chart.Element.extend({
|
||||
inRange: function(mouseX, mouseY) {
|
||||
var vm = this._view;
|
||||
var hoverRange = vm.hitRadius + vm.radius;
|
||||
return ((Math.pow(mouseX - vm.x, 2) + Math.pow(mouseY - vm.y, 2)) < Math.pow(hoverRange, 2));
|
||||
|
||||
if (vm) {
|
||||
var hoverRange = vm.hitRadius + vm.radius;
|
||||
return ((Math.pow(mouseX - vm.x, 2) + Math.pow(mouseY - vm.y, 2)) < Math.pow(hoverRange, 2));
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
},
|
||||
inLabelRange: function(mouseX) {
|
||||
var vm = this._view;
|
||||
|
@ -55,15 +55,26 @@
|
||||
},
|
||||
inRange: function(mouseX, mouseY) {
|
||||
var vm = this._view;
|
||||
if (vm.y < vm.base) {
|
||||
return (mouseX >= vm.x - vm.width / 2 && mouseX <= vm.x + vm.width / 2) && (mouseY >= vm.y && mouseY <= vm.base);
|
||||
} else {
|
||||
return (mouseX >= vm.x - vm.width / 2 && mouseX <= vm.x + vm.width / 2) && (mouseY >= vm.base && mouseY <= vm.y);
|
||||
}
|
||||
var inRange = false;
|
||||
|
||||
if (vm) {
|
||||
if (vm.y < vm.base) {
|
||||
inRange = (mouseX >= vm.x - vm.width / 2 && mouseX <= vm.x + vm.width / 2) && (mouseY >= vm.y && mouseY <= vm.base);
|
||||
} else {
|
||||
inRange = (mouseX >= vm.x - vm.width / 2 && mouseX <= vm.x + vm.width / 2) && (mouseY >= vm.base && mouseY <= vm.y);
|
||||
}
|
||||
}
|
||||
|
||||
return inRange;
|
||||
},
|
||||
inLabelRange: function(mouseX) {
|
||||
var vm = this._view;
|
||||
return (mouseX >= vm.x - vm.width / 2 && mouseX <= vm.x + vm.width / 2);
|
||||
|
||||
if (vm) {
|
||||
return (mouseX >= vm.x - vm.width / 2 && mouseX <= vm.x + vm.width / 2);
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
},
|
||||
tooltipPosition: function() {
|
||||
var vm = this._view;
|
||||
|
@ -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);
|
||||
|
||||
});
|
||||
});
|
176
test/element.arc.tests.js
Normal file
176
test/element.arc.tests.js
Normal file
@ -0,0 +1,176 @@
|
||||
// Test the rectangle element
|
||||
|
||||
describe('Arc element tests', function() {
|
||||
it ('Should be constructed', function() {
|
||||
var arc = new Chart.elements.Arc({
|
||||
_datasetIndex: 2,
|
||||
_index: 1
|
||||
});
|
||||
|
||||
expect(arc).not.toBe(undefined);
|
||||
expect(arc._datasetIndex).toBe(2);
|
||||
expect(arc._index).toBe(1);
|
||||
});
|
||||
|
||||
it ('should determine if in range', function() {
|
||||
var arc = new Chart.elements.Arc({
|
||||
_datasetIndex: 2,
|
||||
_index: 1
|
||||
});
|
||||
|
||||
// Make sure we can run these before the view is added
|
||||
expect(arc.inRange(2, 2)).toBe(false);
|
||||
expect(arc.inLabelRange(2)).toBe(false);
|
||||
|
||||
// Mock out the view as if the controller put it there
|
||||
arc._view = {
|
||||
startAngle: 0,
|
||||
endAngle: Math.PI / 2,
|
||||
x: 0,
|
||||
y: 0,
|
||||
innerRadius: 5,
|
||||
outerRadius: 10,
|
||||
};
|
||||
|
||||
expect(arc.inRange(2, 2)).toBe(false);
|
||||
expect(arc.inRange(7, 0)).toBe(true);
|
||||
expect(arc.inRange(0, 11)).toBe(false);
|
||||
expect(arc.inRange(Math.sqrt(32), Math.sqrt(32))).toBe(true);
|
||||
expect(arc.inRange(-1.0 * Math.sqrt(7), Math.sqrt(7))).toBe(false);
|
||||
});
|
||||
|
||||
it ('should get the tooltip position', function() {
|
||||
var arc = new Chart.elements.Arc({
|
||||
_datasetIndex: 2,
|
||||
_index: 1
|
||||
});
|
||||
|
||||
// Mock out the view as if the controller put it there
|
||||
arc._view = {
|
||||
startAngle: 0,
|
||||
endAngle: Math.PI / 2,
|
||||
x: 0,
|
||||
y: 0,
|
||||
innerRadius: 0,
|
||||
outerRadius: Math.sqrt(2),
|
||||
};
|
||||
|
||||
var pos = arc.tooltipPosition();
|
||||
expect(pos.x).toBeCloseTo(0.5);
|
||||
expect(pos.y).toBeCloseTo(0.5);
|
||||
});
|
||||
|
||||
it ('should draw correctly with no border', function() {
|
||||
var mockContext = window.createMockContext();
|
||||
var arc = new Chart.elements.Arc({
|
||||
_datasetIndex: 2,
|
||||
_index: 1,
|
||||
_chart: {
|
||||
ctx: mockContext,
|
||||
}
|
||||
});
|
||||
|
||||
// Mock out the view as if the controller put it there
|
||||
arc._view = {
|
||||
startAngle: 0,
|
||||
endAngle: Math.PI / 2,
|
||||
x: 10,
|
||||
y: 5,
|
||||
innerRadius: 1,
|
||||
outerRadius: 3,
|
||||
|
||||
backgroundColor: 'rgb(0, 0, 255)',
|
||||
borderColor: 'rgb(255, 0, 0)',
|
||||
};
|
||||
|
||||
arc.draw();
|
||||
|
||||
expect(mockContext.getCalls()).toEqual([{
|
||||
name: 'beginPath',
|
||||
args: []
|
||||
}, {
|
||||
name: 'arc',
|
||||
args: [10, 5, 3, 0, Math.PI / 2]
|
||||
}, {
|
||||
name: 'arc',
|
||||
args: [10, 5, 1, Math.PI / 2, 0, true]
|
||||
}, {
|
||||
name: 'closePath',
|
||||
args: []
|
||||
}, {
|
||||
name: 'setStrokeStyle',
|
||||
args: ['rgb(255, 0, 0)']
|
||||
}, {
|
||||
name: 'setLineWidth',
|
||||
args: [undefined]
|
||||
}, {
|
||||
name: 'setFillStyle',
|
||||
args: ['rgb(0, 0, 255)']
|
||||
}, {
|
||||
name: 'fill',
|
||||
args: []
|
||||
}, {
|
||||
name: 'setLineJoin',
|
||||
args: ['bevel']
|
||||
}]);
|
||||
});
|
||||
|
||||
it ('should draw correctly with a border', function() {
|
||||
var mockContext = window.createMockContext();
|
||||
var arc = new Chart.elements.Arc({
|
||||
_datasetIndex: 2,
|
||||
_index: 1,
|
||||
_chart: {
|
||||
ctx: mockContext,
|
||||
}
|
||||
});
|
||||
|
||||
// Mock out the view as if the controller put it there
|
||||
arc._view = {
|
||||
startAngle: 0,
|
||||
endAngle: Math.PI / 2,
|
||||
x: 10,
|
||||
y: 5,
|
||||
innerRadius: 1,
|
||||
outerRadius: 3,
|
||||
|
||||
backgroundColor: 'rgb(0, 0, 255)',
|
||||
borderColor: 'rgb(255, 0, 0)',
|
||||
borderWidth: 5
|
||||
};
|
||||
|
||||
arc.draw();
|
||||
|
||||
expect(mockContext.getCalls()).toEqual([{
|
||||
name: 'beginPath',
|
||||
args: []
|
||||
}, {
|
||||
name: 'arc',
|
||||
args: [10, 5, 3, 0, Math.PI / 2]
|
||||
}, {
|
||||
name: 'arc',
|
||||
args: [10, 5, 1, Math.PI / 2, 0, true]
|
||||
}, {
|
||||
name: 'closePath',
|
||||
args: []
|
||||
}, {
|
||||
name: 'setStrokeStyle',
|
||||
args: ['rgb(255, 0, 0)']
|
||||
}, {
|
||||
name: 'setLineWidth',
|
||||
args: [5]
|
||||
}, {
|
||||
name: 'setFillStyle',
|
||||
args: ['rgb(0, 0, 255)']
|
||||
}, {
|
||||
name: 'fill',
|
||||
args: []
|
||||
}, {
|
||||
name: 'setLineJoin',
|
||||
args: ['bevel']
|
||||
}, {
|
||||
name: 'stroke',
|
||||
args: []
|
||||
}]);
|
||||
});
|
||||
});
|
526
test/element.line.tests.js
Normal file
526
test/element.line.tests.js
Normal file
@ -0,0 +1,526 @@
|
||||
// Tests for the line element
|
||||
describe('Line element tests', function() {
|
||||
it ('should be constructed', function() {
|
||||
var line = new Chart.elements.Line({
|
||||
_datasetindex: 2,
|
||||
_points: [1, 2, 3, 4]
|
||||
});
|
||||
|
||||
expect(line).not.toBe(undefined);
|
||||
expect(line._datasetindex).toBe(2);
|
||||
expect(line._points).toEqual([1, 2, 3, 4]);
|
||||
});
|
||||
|
||||
it ('should draw with default settings', function() {
|
||||
var mockContext = window.createMockContext();
|
||||
|
||||
// Create our points
|
||||
var points = [];
|
||||
points.push(new Chart.elements.Point({
|
||||
_datasetindex: 2,
|
||||
_index: 0,
|
||||
_view: {
|
||||
x: 0,
|
||||
y: 10
|
||||
}
|
||||
}));
|
||||
points.push(new Chart.elements.Point({
|
||||
_datasetindex: 2,
|
||||
_index: 1,
|
||||
_view: {
|
||||
x: 5,
|
||||
y: 0
|
||||
}
|
||||
}));
|
||||
points.push(new Chart.elements.Point({
|
||||
_datasetindex: 2,
|
||||
_index: 2,
|
||||
_view: {
|
||||
x: 15,
|
||||
y: -10
|
||||
}
|
||||
}));
|
||||
points.push(new Chart.elements.Point({
|
||||
_datasetindex: 2,
|
||||
_index: 3,
|
||||
_view: {
|
||||
x: 19,
|
||||
y: -5
|
||||
}
|
||||
}));
|
||||
|
||||
var line = new Chart.elements.Line({
|
||||
_datasetindex: 2,
|
||||
_chart: {
|
||||
ctx: mockContext,
|
||||
},
|
||||
_children: points,
|
||||
// Need to provide some settings
|
||||
_view: {
|
||||
fill: false, // don't want to fill
|
||||
tension: 0.0, // no bezier curve for now
|
||||
}
|
||||
})
|
||||
|
||||
line.draw();
|
||||
|
||||
expect(mockContext.getCalls()).toEqual([{
|
||||
name: 'save',
|
||||
args: [],
|
||||
}, {
|
||||
name: 'moveTo',
|
||||
args: [0, 10]
|
||||
}, {
|
||||
name: 'lineTo',
|
||||
args: [5, 0]
|
||||
}, {
|
||||
name: 'lineTo',
|
||||
args: [15, -10]
|
||||
}, {
|
||||
name: 'lineTo',
|
||||
args: [19, -5]
|
||||
}, {
|
||||
name: 'setLineCap',
|
||||
args: ['butt']
|
||||
}, {
|
||||
name: 'setLineDash',
|
||||
args: [[]]
|
||||
}, {
|
||||
name: 'setLineDashOffset',
|
||||
args: [0.0]
|
||||
}, {
|
||||
name: 'setLineJoin',
|
||||
args: ['miter']
|
||||
}, {
|
||||
name: 'setLineWidth',
|
||||
args: [3]
|
||||
}, {
|
||||
name: 'setStrokeStyle',
|
||||
args: ['rgba(0,0,0,0.1)']
|
||||
}, {
|
||||
name: 'beginPath',
|
||||
args: []
|
||||
}, {
|
||||
name: 'moveTo',
|
||||
args: [0, 10]
|
||||
}, {
|
||||
name: 'lineTo',
|
||||
args: [5, 0]
|
||||
}, {
|
||||
name: 'lineTo',
|
||||
args: [15, -10]
|
||||
}, {
|
||||
name: 'lineTo',
|
||||
args: [19, -5]
|
||||
}, {
|
||||
name: 'stroke',
|
||||
args: [],
|
||||
}, {
|
||||
name: 'restore',
|
||||
args: []
|
||||
}])
|
||||
});
|
||||
|
||||
it ('should draw with custom settings', function() {
|
||||
var mockContext = window.createMockContext();
|
||||
|
||||
// Create our points
|
||||
var points = [];
|
||||
points.push(new Chart.elements.Point({
|
||||
_datasetindex: 2,
|
||||
_index: 0,
|
||||
_view: {
|
||||
x: 0,
|
||||
y: 10
|
||||
}
|
||||
}));
|
||||
points.push(new Chart.elements.Point({
|
||||
_datasetindex: 2,
|
||||
_index: 1,
|
||||
_view: {
|
||||
x: 5,
|
||||
y: 0
|
||||
}
|
||||
}));
|
||||
points.push(new Chart.elements.Point({
|
||||
_datasetindex: 2,
|
||||
_index: 2,
|
||||
_view: {
|
||||
x: 15,
|
||||
y: -10
|
||||
}
|
||||
}));
|
||||
points.push(new Chart.elements.Point({
|
||||
_datasetindex: 2,
|
||||
_index: 3,
|
||||
_view: {
|
||||
x: 19,
|
||||
y: -5
|
||||
}
|
||||
}));
|
||||
|
||||
var line = new Chart.elements.Line({
|
||||
_datasetindex: 2,
|
||||
_chart: {
|
||||
ctx: mockContext,
|
||||
},
|
||||
_children: points,
|
||||
// Need to provide some settings
|
||||
_view: {
|
||||
fill: true,
|
||||
scaleZero: 2, // for filling lines
|
||||
tension: 0.0, // no bezier curve for now
|
||||
|
||||
borderCapStyle: 'round',
|
||||
borderColor: 'rgb(255, 255, 0)',
|
||||
borderDash: [2, 2],
|
||||
borderDashOffset: 1.5,
|
||||
borderJoinStyle: 'bevel',
|
||||
borderWidth: 4,
|
||||
backgroundColor: 'rgb(0, 0, 0)'
|
||||
}
|
||||
})
|
||||
|
||||
line.draw();
|
||||
|
||||
var expected = [{
|
||||
name: 'save',
|
||||
args: [],
|
||||
}, {
|
||||
name: 'moveTo',
|
||||
args: [0, 10]
|
||||
}, {
|
||||
name: 'lineTo',
|
||||
args: [5, 0]
|
||||
}, {
|
||||
name: 'lineTo',
|
||||
args: [15, -10]
|
||||
}, {
|
||||
name: 'lineTo',
|
||||
args: [19, -5]
|
||||
}, {
|
||||
name: 'lineTo',
|
||||
args: [19, 2]
|
||||
}, {
|
||||
name: 'lineTo',
|
||||
args: [0, 2]
|
||||
}, {
|
||||
name: 'setFillStyle',
|
||||
args: ['rgb(0, 0, 0)']
|
||||
}, {
|
||||
name: 'closePath',
|
||||
args: []
|
||||
}, {
|
||||
name: 'fill',
|
||||
args: []
|
||||
}, {
|
||||
name: 'setLineCap',
|
||||
args: ['round']
|
||||
}, {
|
||||
name: 'setLineDash',
|
||||
args: [[2, 2]]
|
||||
}, {
|
||||
name: 'setLineDashOffset',
|
||||
args: [1.5]
|
||||
}, {
|
||||
name: 'setLineJoin',
|
||||
args: ['bevel']
|
||||
}, {
|
||||
name: 'setLineWidth',
|
||||
args: [4]
|
||||
}, {
|
||||
name: 'setStrokeStyle',
|
||||
args: ['rgb(255, 255, 0)']
|
||||
}, {
|
||||
name: 'beginPath',
|
||||
args: []
|
||||
}, {
|
||||
name: 'moveTo',
|
||||
args: [0, 10]
|
||||
}, {
|
||||
name: 'lineTo',
|
||||
args: [5, 0]
|
||||
}, {
|
||||
name: 'lineTo',
|
||||
args: [15, -10]
|
||||
}, {
|
||||
name: 'lineTo',
|
||||
args: [19, -5]
|
||||
}, {
|
||||
name: 'stroke',
|
||||
args: [],
|
||||
}, {
|
||||
name: 'restore',
|
||||
args: []
|
||||
}];
|
||||
expect(mockContext.getCalls()).toEqual(expected)
|
||||
});
|
||||
|
||||
it ('should be able to draw with a loop back to the beginning point', function() {
|
||||
var mockContext = window.createMockContext();
|
||||
|
||||
// Create our points
|
||||
var points = [];
|
||||
points.push(new Chart.elements.Point({
|
||||
_datasetindex: 2,
|
||||
_index: 0,
|
||||
_view: {
|
||||
x: 0,
|
||||
y: 10
|
||||
}
|
||||
}));
|
||||
points.push(new Chart.elements.Point({
|
||||
_datasetindex: 2,
|
||||
_index: 1,
|
||||
_view: {
|
||||
x: 5,
|
||||
y: 0
|
||||
}
|
||||
}));
|
||||
points.push(new Chart.elements.Point({
|
||||
_datasetindex: 2,
|
||||
_index: 2,
|
||||
_view: {
|
||||
x: 15,
|
||||
y: -10
|
||||
}
|
||||
}));
|
||||
points.push(new Chart.elements.Point({
|
||||
_datasetindex: 2,
|
||||
_index: 3,
|
||||
_view: {
|
||||
x: 19,
|
||||
y: -5
|
||||
}
|
||||
}));
|
||||
|
||||
var line = new Chart.elements.Line({
|
||||
_datasetindex: 2,
|
||||
_chart: {
|
||||
ctx: mockContext,
|
||||
},
|
||||
_children: points,
|
||||
_loop: true, // want the line to loop back to the first point
|
||||
// Need to provide some settings
|
||||
_view: {
|
||||
fill: false, // don't want to fill
|
||||
tension: 0.0, // no bezier curve for now
|
||||
}
|
||||
})
|
||||
|
||||
line.draw();
|
||||
|
||||
expect(mockContext.getCalls()).toEqual([{
|
||||
name: 'save',
|
||||
args: [],
|
||||
}, {
|
||||
name: 'moveTo',
|
||||
args: [0, 10]
|
||||
}, {
|
||||
name: 'lineTo',
|
||||
args: [5, 0]
|
||||
}, {
|
||||
name: 'lineTo',
|
||||
args: [15, -10]
|
||||
}, {
|
||||
name: 'lineTo',
|
||||
args: [19, -5]
|
||||
}, {
|
||||
name: 'lineTo',
|
||||
args: [0, 10]
|
||||
}, {
|
||||
name: 'setLineCap',
|
||||
args: ['butt']
|
||||
}, {
|
||||
name: 'setLineDash',
|
||||
args: [[]]
|
||||
}, {
|
||||
name: 'setLineDashOffset',
|
||||
args: [0.0]
|
||||
}, {
|
||||
name: 'setLineJoin',
|
||||
args: ['miter']
|
||||
}, {
|
||||
name: 'setLineWidth',
|
||||
args: [3]
|
||||
}, {
|
||||
name: 'setStrokeStyle',
|
||||
args: ['rgba(0,0,0,0.1)']
|
||||
}, {
|
||||
name: 'beginPath',
|
||||
args: []
|
||||
}, {
|
||||
name: 'moveTo',
|
||||
args: [0, 10]
|
||||
}, {
|
||||
name: 'lineTo',
|
||||
args: [5, 0]
|
||||
}, {
|
||||
name: 'lineTo',
|
||||
args: [15, -10]
|
||||
}, {
|
||||
name: 'lineTo',
|
||||
args: [19, -5]
|
||||
}, {
|
||||
name: 'lineTo',
|
||||
args: [0, 10]
|
||||
}, {
|
||||
name: 'stroke',
|
||||
args: [],
|
||||
}, {
|
||||
name: 'restore',
|
||||
args: []
|
||||
}])
|
||||
});
|
||||
|
||||
it ('should draw with bezier curves if tension > 0', function() {
|
||||
var mockContext = window.createMockContext();
|
||||
|
||||
// Create our points
|
||||
var points = [];
|
||||
points.push(new Chart.elements.Point({
|
||||
_datasetindex: 2,
|
||||
_index: 0,
|
||||
_view: {
|
||||
x: 0,
|
||||
y: 10,
|
||||
controlPointNextX: 1,
|
||||
controlPointNextY: 1,
|
||||
controlPointPreviousX: 0,
|
||||
controlPointPreviousY: 0,
|
||||
}
|
||||
}));
|
||||
points.push(new Chart.elements.Point({
|
||||
_datasetindex: 2,
|
||||
_index: 1,
|
||||
_view: {
|
||||
x: 5,
|
||||
y: 0,
|
||||
controlPointNextX: 6,
|
||||
controlPointNextY: 7,
|
||||
controlPointPreviousX: 4,
|
||||
controlPointPreviousY: 3,
|
||||
}
|
||||
}));
|
||||
points.push(new Chart.elements.Point({
|
||||
_datasetindex: 2,
|
||||
_index: 2,
|
||||
_view: {
|
||||
x: 15,
|
||||
y: -10,
|
||||
controlPointNextX: 16,
|
||||
controlPointNextY: 17,
|
||||
controlPointPreviousX: 14,
|
||||
controlPointPreviousY: 13,
|
||||
}
|
||||
}));
|
||||
points.push(new Chart.elements.Point({
|
||||
_datasetindex: 2,
|
||||
_index: 3,
|
||||
_view: {
|
||||
x: 19,
|
||||
y: -5,
|
||||
controlPointNextX: 20,
|
||||
controlPointNextY: 21,
|
||||
controlPointPreviousX: 18,
|
||||
controlPointPreviousY: 17,
|
||||
}
|
||||
}));
|
||||
|
||||
var line = new Chart.elements.Line({
|
||||
_datasetindex: 2,
|
||||
_chart: {
|
||||
ctx: mockContext,
|
||||
},
|
||||
_children: points,
|
||||
// Need to provide some settings
|
||||
_view: {
|
||||
fill: true,
|
||||
scaleZero: 2, // for filling lines
|
||||
tension: 0.5, // have bezier curves
|
||||
|
||||
borderCapStyle: 'round',
|
||||
borderColor: 'rgb(255, 255, 0)',
|
||||
borderDash: [2, 2],
|
||||
borderDashOffset: 1.5,
|
||||
borderJoinStyle: 'bevel',
|
||||
borderWidth: 4,
|
||||
backgroundColor: 'rgb(0, 0, 0)'
|
||||
}
|
||||
})
|
||||
|
||||
line.draw();
|
||||
|
||||
var expected = [{
|
||||
name: 'save',
|
||||
args: [],
|
||||
}, {
|
||||
name: 'moveTo',
|
||||
args: [0, 10]
|
||||
}, {
|
||||
name: 'bezierCurveTo',
|
||||
args: [1, 1, 4, 3, 5, 0]
|
||||
}, {
|
||||
name: 'bezierCurveTo',
|
||||
args: [6, 7, 14, 13, 15, -10]
|
||||
}, {
|
||||
name: 'bezierCurveTo',
|
||||
args: [16, 17, 18, 17, 19, -5]
|
||||
}, {
|
||||
name: 'lineTo',
|
||||
args: [19, 2]
|
||||
}, {
|
||||
name: 'lineTo',
|
||||
args: [0, 2]
|
||||
}, {
|
||||
name: 'setFillStyle',
|
||||
args: ['rgb(0, 0, 0)']
|
||||
}, {
|
||||
name: 'closePath',
|
||||
args: []
|
||||
}, {
|
||||
name: 'fill',
|
||||
args: []
|
||||
}, {
|
||||
name: 'setLineCap',
|
||||
args: ['round']
|
||||
}, {
|
||||
name: 'setLineDash',
|
||||
args: [[2, 2]]
|
||||
}, {
|
||||
name: 'setLineDashOffset',
|
||||
args: [1.5]
|
||||
}, {
|
||||
name: 'setLineJoin',
|
||||
args: ['bevel']
|
||||
}, {
|
||||
name: 'setLineWidth',
|
||||
args: [4]
|
||||
}, {
|
||||
name: 'setStrokeStyle',
|
||||
args: ['rgb(255, 255, 0)']
|
||||
}, {
|
||||
name: 'beginPath',
|
||||
args: []
|
||||
}, {
|
||||
name: 'moveTo',
|
||||
args: [0, 10]
|
||||
}, {
|
||||
name: 'bezierCurveTo',
|
||||
args: [1, 1, 4, 3, 5, 0]
|
||||
}, {
|
||||
name: 'bezierCurveTo',
|
||||
args: [6, 7, 14, 13, 15, -10]
|
||||
}, {
|
||||
name: 'bezierCurveTo',
|
||||
args: [16, 17, 18, 17, 19, -5]
|
||||
}, {
|
||||
name: 'stroke',
|
||||
args: [],
|
||||
}, {
|
||||
name: 'restore',
|
||||
args: []
|
||||
}];
|
||||
expect(mockContext.getCalls()).toEqual(expected)
|
||||
});
|
||||
});
|
190
test/element.point.tests.js
Normal file
190
test/element.point.tests.js
Normal file
@ -0,0 +1,190 @@
|
||||
// Test the point element
|
||||
|
||||
describe('Point element tests', function() {
|
||||
it ('Should be constructed', function() {
|
||||
var point = new Chart.elements.Point({
|
||||
_datasetIndex: 2,
|
||||
_index: 1
|
||||
});
|
||||
|
||||
expect(point).not.toBe(undefined);
|
||||
expect(point._datasetIndex).toBe(2);
|
||||
expect(point._index).toBe(1);
|
||||
});
|
||||
|
||||
it ('Should correctly identify as in range', function() {
|
||||
var point = new Chart.elements.Point({
|
||||
_datasetIndex: 2,
|
||||
_index: 1
|
||||
});
|
||||
|
||||
// Safely handles if these are called before the viewmodel is instantiated
|
||||
expect(point.inRange(5)).toBe(false);
|
||||
expect(point.inLabelRange(5)).toBe(false);
|
||||
|
||||
// Attach a view object as if we were the controller
|
||||
point._view = {
|
||||
radius: 2,
|
||||
hitRadius: 3,
|
||||
x: 10,
|
||||
y: 15
|
||||
};
|
||||
|
||||
expect(point.inRange(10, 15)).toBe(true);
|
||||
expect(point.inRange(10, 10)).toBe(false);
|
||||
expect(point.inRange(10, 5)).toBe(false);
|
||||
expect(point.inRange(5, 5)).toBe(false);
|
||||
|
||||
expect(point.inLabelRange(5)).toBe(false);
|
||||
expect(point.inLabelRange(7)).toBe(true);
|
||||
expect(point.inLabelRange(10)).toBe(true);
|
||||
expect(point.inLabelRange(12)).toBe(true);
|
||||
expect(point.inLabelRange(15)).toBe(false);
|
||||
expect(point.inLabelRange(20)).toBe(false);
|
||||
});
|
||||
|
||||
it ('should get the correct tooltip position', function() {
|
||||
var point = new Chart.elements.Point({
|
||||
_datasetIndex: 2,
|
||||
_index: 1
|
||||
});
|
||||
|
||||
// Attach a view object as if we were the controller
|
||||
point._view = {
|
||||
radius: 2,
|
||||
borderWidth: 6,
|
||||
x: 10,
|
||||
y: 15
|
||||
};
|
||||
|
||||
expect(point.tooltipPosition()).toEqual({
|
||||
x: 10,
|
||||
y: 15,
|
||||
padding: 8
|
||||
});
|
||||
});
|
||||
|
||||
it ('should draw correctly', function() {
|
||||
var mockContext = window.createMockContext();
|
||||
var point = new Chart.elements.Point({
|
||||
_datasetIndex: 2,
|
||||
_index: 1,
|
||||
_chart: {
|
||||
ctx: mockContext,
|
||||
}
|
||||
});
|
||||
|
||||
// Attach a view object as if we were the controller
|
||||
point._view = {
|
||||
radius: 2,
|
||||
hitRadius: 3,
|
||||
borderColor: 'rgba(1, 2, 3, 1)',
|
||||
borderWidth: 6,
|
||||
backgroundColor: 'rgba(0, 255, 0)',
|
||||
x: 10,
|
||||
y: 15,
|
||||
ctx: mockContext
|
||||
};
|
||||
|
||||
point.draw();
|
||||
|
||||
expect(mockContext.getCalls()).toEqual([{
|
||||
name: 'beginPath',
|
||||
args: []
|
||||
}, {
|
||||
name: 'arc',
|
||||
args: [10, 15, 2, 0, 2 * Math.PI]
|
||||
}, {
|
||||
name: 'closePath',
|
||||
args: [],
|
||||
}, {
|
||||
name: 'setStrokeStyle',
|
||||
args: ['rgba(1, 2, 3, 1)']
|
||||
}, {
|
||||
name: 'setLineWidth',
|
||||
args: [6]
|
||||
}, {
|
||||
name: 'setFillStyle',
|
||||
args: ['rgba(0, 255, 0)']
|
||||
}, {
|
||||
name: 'fill',
|
||||
args: [],
|
||||
}, {
|
||||
name: 'stroke',
|
||||
args: []
|
||||
}]);
|
||||
});
|
||||
|
||||
it ('should draw correctly with default settings if necessary', function() {
|
||||
var mockContext = window.createMockContext();
|
||||
var point = new Chart.elements.Point({
|
||||
_datasetIndex: 2,
|
||||
_index: 1,
|
||||
_chart: {
|
||||
ctx: mockContext,
|
||||
}
|
||||
});
|
||||
|
||||
// Attach a view object as if we were the controller
|
||||
point._view = {
|
||||
radius: 2,
|
||||
hitRadius: 3,
|
||||
x: 10,
|
||||
y: 15,
|
||||
ctx: mockContext
|
||||
};
|
||||
|
||||
point.draw();
|
||||
|
||||
expect(mockContext.getCalls()).toEqual([{
|
||||
name: 'beginPath',
|
||||
args: []
|
||||
}, {
|
||||
name: 'arc',
|
||||
args: [10, 15, 2, 0, 2 * Math.PI]
|
||||
}, {
|
||||
name: 'closePath',
|
||||
args: [],
|
||||
}, {
|
||||
name: 'setStrokeStyle',
|
||||
args: ['rgba(0,0,0,0.1)']
|
||||
}, {
|
||||
name: 'setLineWidth',
|
||||
args: [1]
|
||||
}, {
|
||||
name: 'setFillStyle',
|
||||
args: ['rgba(0,0,0,0.1)']
|
||||
}, {
|
||||
name: 'fill',
|
||||
args: [],
|
||||
}, {
|
||||
name: 'stroke',
|
||||
args: []
|
||||
}]);
|
||||
});
|
||||
|
||||
it ('should not draw if skipped', function() {
|
||||
var mockContext = window.createMockContext();
|
||||
var point = new Chart.elements.Point({
|
||||
_datasetIndex: 2,
|
||||
_index: 1,
|
||||
_chart: {
|
||||
ctx: mockContext,
|
||||
}
|
||||
});
|
||||
|
||||
// Attach a view object as if we were the controller
|
||||
point._view = {
|
||||
radius: 2,
|
||||
hitRadius: 3,
|
||||
x: 10,
|
||||
y: 15,
|
||||
ctx: mockContext,
|
||||
skip: true
|
||||
};
|
||||
|
||||
point.draw();
|
||||
|
||||
expect(mockContext.getCalls()).toEqual([]);
|
||||
});
|
||||
});
|
246
test/element.rectangle.tests.js
Normal file
246
test/element.rectangle.tests.js
Normal file
@ -0,0 +1,246 @@
|
||||
// Test the rectangle element
|
||||
|
||||
describe('Rectangle element tests', function() {
|
||||
it ('Should be constructed', function() {
|
||||
var rectangle = new Chart.elements.Rectangle({
|
||||
_datasetIndex: 2,
|
||||
_index: 1
|
||||
});
|
||||
|
||||
expect(rectangle).not.toBe(undefined);
|
||||
expect(rectangle._datasetIndex).toBe(2);
|
||||
expect(rectangle._index).toBe(1);
|
||||
});
|
||||
|
||||
it ('Should correctly identify as in range', function() {
|
||||
var rectangle = new Chart.elements.Rectangle({
|
||||
_datasetIndex: 2,
|
||||
_index: 1
|
||||
});
|
||||
|
||||
// Safely handles if these are called before the viewmodel is instantiated
|
||||
expect(rectangle.inRange(5)).toBe(false);
|
||||
expect(rectangle.inLabelRange(5)).toBe(false);
|
||||
|
||||
// Attach a view object as if we were the controller
|
||||
rectangle._view = {
|
||||
base: 0,
|
||||
width: 4,
|
||||
x: 10,
|
||||
y: 15
|
||||
};
|
||||
|
||||
expect(rectangle.inRange(10, 15)).toBe(true);
|
||||
expect(rectangle.inRange(10, 10)).toBe(true);
|
||||
expect(rectangle.inRange(10, 16)).toBe(false);
|
||||
expect(rectangle.inRange(5, 5)).toBe(false);
|
||||
|
||||
expect(rectangle.inLabelRange(5)).toBe(false);
|
||||
expect(rectangle.inLabelRange(7)).toBe(false);
|
||||
expect(rectangle.inLabelRange(10)).toBe(true);
|
||||
expect(rectangle.inLabelRange(12)).toBe(true);
|
||||
expect(rectangle.inLabelRange(15)).toBe(false);
|
||||
expect(rectangle.inLabelRange(20)).toBe(false);
|
||||
|
||||
// Test when the y is below the base (negative bar)
|
||||
var negativeRectangle = new Chart.elements.Rectangle({
|
||||
_datasetIndex: 2,
|
||||
_index: 1
|
||||
});
|
||||
|
||||
// Attach a view object as if we were the controller
|
||||
negativeRectangle._view = {
|
||||
base: 0,
|
||||
width: 4,
|
||||
x: 10,
|
||||
y: -15
|
||||
};
|
||||
|
||||
expect(negativeRectangle.inRange(10, -16)).toBe(false);
|
||||
expect(negativeRectangle.inRange(10, 1)).toBe(false);
|
||||
expect(negativeRectangle.inRange(10, -5)).toBe(true);
|
||||
});
|
||||
|
||||
it ('should get the correct height', function() {
|
||||
var rectangle = new Chart.elements.Rectangle({
|
||||
_datasetIndex: 2,
|
||||
_index: 1
|
||||
});
|
||||
|
||||
// Attach a view object as if we were the controller
|
||||
rectangle._view = {
|
||||
base: 0,
|
||||
width: 4,
|
||||
x: 10,
|
||||
y: 15
|
||||
};
|
||||
|
||||
expect(rectangle.height()).toBe(-15);
|
||||
|
||||
// Test when the y is below the base (negative bar)
|
||||
var negativeRectangle = new Chart.elements.Rectangle({
|
||||
_datasetIndex: 2,
|
||||
_index: 1
|
||||
});
|
||||
|
||||
// Attach a view object as if we were the controller
|
||||
negativeRectangle._view = {
|
||||
base: -10,
|
||||
width: 4,
|
||||
x: 10,
|
||||
y: -15
|
||||
};
|
||||
expect(negativeRectangle.height()).toBe(5);
|
||||
});
|
||||
|
||||
it ('should get the correct tooltip position', function() {
|
||||
var rectangle = new Chart.elements.Rectangle({
|
||||
_datasetIndex: 2,
|
||||
_index: 1
|
||||
});
|
||||
|
||||
// Attach a view object as if we were the controller
|
||||
rectangle._view = {
|
||||
base: 0,
|
||||
width: 4,
|
||||
x: 10,
|
||||
y: 15
|
||||
};
|
||||
|
||||
expect(rectangle.tooltipPosition()).toEqual({
|
||||
x: 10,
|
||||
y: 0,
|
||||
});
|
||||
|
||||
// Test when the y is below the base (negative bar)
|
||||
var negativeRectangle = new Chart.elements.Rectangle({
|
||||
_datasetIndex: 2,
|
||||
_index: 1
|
||||
});
|
||||
|
||||
// Attach a view object as if we were the controller
|
||||
negativeRectangle._view = {
|
||||
base: -10,
|
||||
width: 4,
|
||||
x: 10,
|
||||
y: -15
|
||||
};
|
||||
|
||||
expect(negativeRectangle.tooltipPosition()).toEqual({
|
||||
x: 10,
|
||||
y: -15,
|
||||
});
|
||||
});
|
||||
|
||||
it ('should draw correctly', function() {
|
||||
var mockContext = window.createMockContext();
|
||||
var rectangle = new Chart.elements.Rectangle({
|
||||
_datasetIndex: 2,
|
||||
_index: 1,
|
||||
_chart: {
|
||||
ctx: mockContext,
|
||||
}
|
||||
});
|
||||
|
||||
// Attach a view object as if we were the controller
|
||||
rectangle._view = {
|
||||
backgroundColor: 'rgb(255, 0, 0)',
|
||||
base: 0,
|
||||
borderColor: 'rgb(0, 0, 255)',
|
||||
borderWidth: 1,
|
||||
ctx: mockContext,
|
||||
width: 4,
|
||||
x: 10,
|
||||
y: 15,
|
||||
};
|
||||
|
||||
rectangle.draw();
|
||||
|
||||
expect(mockContext.getCalls()).toEqual([{
|
||||
name: 'beginPath',
|
||||
args: [],
|
||||
}, {
|
||||
name: 'setFillStyle',
|
||||
args: ['rgb(255, 0, 0)']
|
||||
}, {
|
||||
name: 'setStrokeStyle',
|
||||
args: ['rgb(0, 0, 255)'],
|
||||
}, {
|
||||
name: 'setLineWidth',
|
||||
args: [1]
|
||||
}, {
|
||||
name: 'moveTo',
|
||||
args: [8.5, 0]
|
||||
}, {
|
||||
name: 'lineTo',
|
||||
args: [8.5, 15.5]
|
||||
}, {
|
||||
name: 'lineTo',
|
||||
args: [11.5, 15.5]
|
||||
}, {
|
||||
name: 'lineTo',
|
||||
args: [11.5, 0]
|
||||
}, {
|
||||
name: 'fill',
|
||||
args: [],
|
||||
}, {
|
||||
name: 'stroke',
|
||||
args: []
|
||||
}]);
|
||||
});
|
||||
|
||||
it ('should draw correctly with no stroke', function() {
|
||||
var mockContext = window.createMockContext();
|
||||
var rectangle = new Chart.elements.Rectangle({
|
||||
_datasetIndex: 2,
|
||||
_index: 1,
|
||||
_chart: {
|
||||
ctx: mockContext,
|
||||
}
|
||||
});
|
||||
|
||||
// Attach a view object as if we were the controller
|
||||
rectangle._view = {
|
||||
backgroundColor: 'rgb(255, 0, 0)',
|
||||
base: 0,
|
||||
borderColor: 'rgb(0, 0, 255)',
|
||||
ctx: mockContext,
|
||||
width: 4,
|
||||
x: 10,
|
||||
y: 15,
|
||||
};
|
||||
|
||||
rectangle.draw();
|
||||
|
||||
expect(mockContext.getCalls()).toEqual([{
|
||||
name: 'beginPath',
|
||||
args: [],
|
||||
}, {
|
||||
name: 'setFillStyle',
|
||||
args: ['rgb(255, 0, 0)']
|
||||
}, {
|
||||
name: 'setStrokeStyle',
|
||||
args: ['rgb(0, 0, 255)'],
|
||||
}, {
|
||||
name: 'setLineWidth',
|
||||
args: [undefined]
|
||||
}, {
|
||||
name: 'moveTo',
|
||||
args: [8, 0]
|
||||
}, {
|
||||
name: 'lineTo',
|
||||
args: [8, 15]
|
||||
}, {
|
||||
name: 'lineTo',
|
||||
args: [12, 15]
|
||||
}, {
|
||||
name: 'lineTo',
|
||||
args: [12, 0]
|
||||
}, {
|
||||
name: 'fill',
|
||||
args: [],
|
||||
}]);
|
||||
});
|
||||
|
||||
|
||||
});
|
108
test/mockContext.js
Normal file
108
test/mockContext.js
Normal file
@ -0,0 +1,108 @@
|
||||
(function() {
|
||||
// Code from http://stackoverflow.com/questions/4406864/html-canvas-unit-testing
|
||||
var Context = function() {
|
||||
this._calls = []; // names/args of recorded calls
|
||||
this._initMethods();
|
||||
|
||||
this._fillStyle = null;
|
||||
this._lineCap = null;
|
||||
this._lineDashOffset = null;
|
||||
this._lineJoin = null;
|
||||
this._lineWidth = null;
|
||||
this._strokeStyle = null;
|
||||
|
||||
// Define properties here so that we can record each time they are set
|
||||
Object.defineProperties(this, {
|
||||
"fillStyle": {
|
||||
'get': function() { return this._fillStyle; },
|
||||
'set': function(style) {
|
||||
this._fillStyle = style;
|
||||
this.record('setFillStyle', [style]);
|
||||
}
|
||||
},
|
||||
'lineCap': {
|
||||
'get': function() { return this._lineCap; },
|
||||
'set': function(cap) {
|
||||
this._lineCap = cap;
|
||||
this.record('setLineCap', [cap]);
|
||||
}
|
||||
},
|
||||
'lineDashOffset': {
|
||||
'get': function() { return this._lineDashOffset; },
|
||||
'set': function(offset) {
|
||||
this._lineDashOffset = offset;
|
||||
this.record('setLineDashOffset', [offset]);
|
||||
}
|
||||
},
|
||||
'lineJoin': {
|
||||
'get': function() { return this._lineJoin; },
|
||||
'set': function(join) {
|
||||
this._lineJoin = join;
|
||||
this.record('setLineJoin', [join]);
|
||||
}
|
||||
},
|
||||
'lineWidth': {
|
||||
'get': function() { return this._lineWidth; },
|
||||
'set': function (width) {
|
||||
this._lineWidth = width;
|
||||
this.record('setLineWidth', [width]);
|
||||
}
|
||||
},
|
||||
'strokeStyle': {
|
||||
'get': function() { return this._strokeStyle; },
|
||||
'set': function(style) {
|
||||
this._strokeStyle = style;
|
||||
this.record('setStrokeStyle', [style]);
|
||||
}
|
||||
},
|
||||
});
|
||||
};
|
||||
|
||||
Context.prototype._initMethods = function() {
|
||||
// define methods to test here
|
||||
// no way to introspect so we have to do some extra work :(
|
||||
var methods = {
|
||||
arc: function() {},
|
||||
beginPath: function() {},
|
||||
bezierCurveTo: function() {},
|
||||
closePath: function() {},
|
||||
fill: function() {},
|
||||
lineTo: function(x, y) {},
|
||||
moveTo: function(x, y) {},
|
||||
restore: function() {},
|
||||
save: function() {},
|
||||
setLineDash: function() {},
|
||||
stroke: function() {}
|
||||
};
|
||||
|
||||
// attach methods to the class itself
|
||||
var scope = this;
|
||||
var addMethod = function(name, method) {
|
||||
scope[methodName] = function() {
|
||||
scope.record(name, arguments);
|
||||
method.apply(scope, arguments);
|
||||
};
|
||||
}
|
||||
|
||||
for (var methodName in methods) {
|
||||
var method = methods[methodName];
|
||||
|
||||
addMethod(methodName, method);
|
||||
}
|
||||
};
|
||||
|
||||
Context.prototype.record = function(methodName, args) {
|
||||
this._calls.push({
|
||||
name: methodName,
|
||||
args: Array.prototype.slice.call(args)
|
||||
});
|
||||
},
|
||||
|
||||
Context.prototype.getCalls = function() {
|
||||
return this._calls;
|
||||
}
|
||||
|
||||
window.createMockContext = function() {
|
||||
return new Context();
|
||||
};
|
||||
})();
|
Loading…
Reference in New Issue
Block a user