1
0
mirror of https://gitlab.com/JKANetwork/CheckServer.git synced 2026-02-20 12:11:34 +01:00

Start again

This commit is contained in:
2020-10-04 17:14:00 +02:00
parent c0d3912413
commit 091f119048
4382 changed files with 1762543 additions and 9606 deletions

View File

@@ -0,0 +1,440 @@
// Test the bar controller
describe('Bar controller tests', function() {
beforeEach(function() {
window.addDefaultMatchers(jasmine);
});
afterEach(function() {
window.releaseAllCharts();
});
it('should be constructed', function() {
var chart = window.acquireChart({
type: 'bar',
data: {
datasets: [
{ data: [] },
{ data: [] }
],
labels: []
}
});
var meta = chart.getDatasetMeta(1);
expect(meta.type).toEqual('bar');
expect(meta.data).toEqual([]);
expect(meta.hidden).toBe(null);
expect(meta.controller).not.toBe(undefined);
expect(meta.controller.index).toBe(1);
expect(meta.xAxisID).not.toBe(null);
expect(meta.yAxisID).not.toBe(null);
meta.controller.updateIndex(0);
expect(meta.controller.index).toBe(0);
});
it('should use the first scale IDs if the dataset does not specify them', function() {
var chart = window.acquireChart({
type: 'bar',
data: {
datasets: [
{ data: [] },
{ data: [] }
],
labels: []
},
options: {
scales: {
xAxes: [{
id: 'firstXScaleID'
}],
yAxes: [{
id: 'firstYScaleID'
}]
}
}
});
var meta = chart.getDatasetMeta(1);
expect(meta.xAxisID).toBe('firstXScaleID');
expect(meta.yAxisID).toBe('firstYScaleID');
});
it('should correctly count the number of bar datasets', function() {
var chart = window.acquireChart({
type: 'bar',
data: {
datasets: [
{ data: [], type: 'line' },
{ data: [], hidden: true },
{ data: [] },
{ data: [] }
],
labels: []
}
});
var meta = chart.getDatasetMeta(1);
expect(meta.controller.getBarCount()).toBe(2);
});
it('should correctly get the bar index accounting for hidden datasets', function() {
var chart = window.acquireChart({
type: 'bar',
data: {
datasets: [
{ data: [] },
{ data: [], hidden: true },
{ data: [], type: 'line' },
{ data: [] }
],
labels: []
}
});
var meta = chart.getDatasetMeta(1);
expect(meta.controller.getBarIndex(0)).toBe(0);
expect(meta.controller.getBarIndex(3)).toBe(1);
});
it('should create rectangle elements for each data item during initialization', function() {
var chart = window.acquireChart({
type: 'bar',
data: {
datasets: [
{ data: [] },
{ data: [10, 15, 0, -4] }
],
labels: []
}
});
var meta = chart.getDatasetMeta(1);
expect(meta.data.length).toBe(4); // 4 rectangles created
expect(meta.data[0] instanceof Chart.elements.Rectangle).toBe(true);
expect(meta.data[1] instanceof Chart.elements.Rectangle).toBe(true);
expect(meta.data[2] instanceof Chart.elements.Rectangle).toBe(true);
expect(meta.data[3] instanceof Chart.elements.Rectangle).toBe(true);
});
it('should update elements when modifying data', function() {
var chart = window.acquireChart({
type: 'bar',
data: {
datasets: [{
data: [1, 2],
label: 'dataset1'
}, {
data: [10, 15, 0, -4],
label: 'dataset2',
borderColor: 'blue'
}],
labels: ['label1', 'label2', 'label3', 'label4']
},
options: {
elements: {
rectangle: {
backgroundColor: 'red',
borderSkipped: 'top',
borderColor: 'green',
borderWidth: 2,
}
},
scales: {
xAxes: [{
id: 'firstXScaleID',
type: 'category'
}],
yAxes: [{
id: 'firstYScaleID',
type: 'linear'
}]
}
}
});
var meta = chart.getDatasetMeta(1);
expect(meta.data.length).toBe(4);
chart.data.datasets[1].data = [1, 2]; // remove 2 items
chart.data.datasets[1].borderWidth = 1;
chart.update();
expect(meta.data.length).toBe(2);
[ { x: 122, y: 484 },
{ x: 234, y: 32 }
].forEach(function(expected, i) {
expect(meta.data[i]._datasetIndex).toBe(1);
expect(meta.data[i]._index).toBe(i);
expect(meta.data[i]._xScale).toBe(chart.scales.firstXScaleID);
expect(meta.data[i]._yScale).toBe(chart.scales.firstYScaleID);
expect(meta.data[i]._model.x).toBeCloseToPixel(expected.x);
expect(meta.data[i]._model.y).toBeCloseToPixel(expected.y);
expect(meta.data[i]._model.base).toBeCloseToPixel(484);
expect(meta.data[i]._model.width).toBeCloseToPixel(40);
expect(meta.data[i]._model).toEqual(jasmine.objectContaining({
datasetLabel: chart.config.data.datasets[1].label,
label: chart.config.data.labels[i],
backgroundColor: 'red',
borderSkipped: 'top',
borderColor: 'blue',
borderWidth: 1
}));
});
chart.data.datasets[1].data = [1, 2, 3]; // add 1 items
chart.update();
expect(meta.data.length).toBe(3); // should add a new meta data item
});
it('should get the correct bar points when datasets of different types exist', function() {
var chart = window.acquireChart({
type: 'bar',
data: {
datasets: [{
data: [1, 2],
label: 'dataset1'
}, {
type: 'line',
data: [4, 6],
label: 'dataset2'
}, {
data: [8, 10],
label: 'dataset3'
}],
labels: ['label1', 'label2']
},
options: {
scales: {
xAxes: [{
type: 'category'
}],
yAxes: [{
type: 'linear'
}]
}
}
});
var meta = chart.getDatasetMeta(2);
expect(meta.data.length).toBe(2);
var bar1 = meta.data[0];
var bar2 = meta.data[1];
expect(bar1._model.x).toBeCloseToPixel(194);
expect(bar1._model.y).toBeCloseToPixel(132);
expect(bar2._model.x).toBeCloseToPixel(424);
expect(bar2._model.y).toBeCloseToPixel(32);
});
it('should update elements when the scales are stacked', function() {
var chart = window.acquireChart({
type: 'bar',
data: {
datasets: [{
data: [10, -10, 10, -10],
label: 'dataset1'
}, {
data: [10, 15, 0, -4],
label: 'dataset2'
}],
labels: ['label1', 'label2', 'label3', 'label4']
},
options: {
scales: {
xAxes: [{
type: 'category',
stacked: true
}],
yAxes: [{
type: 'linear',
stacked: true
}]
}
}
});
var meta0 = chart.getDatasetMeta(0);
[ { b: 290, w: 91, x: 95, y: 161 },
{ b: 290, w: 91, x: 209, y: 419 },
{ b: 290, w: 91, x: 322, y: 161 },
{ b: 290, w: 91, x: 436, y: 419 }
].forEach(function(values, i) {
expect(meta0.data[i]._model.base).toBeCloseToPixel(values.b);
expect(meta0.data[i]._model.width).toBeCloseToPixel(values.w);
expect(meta0.data[i]._model.x).toBeCloseToPixel(values.x);
expect(meta0.data[i]._model.y).toBeCloseToPixel(values.y);
});
var meta1 = chart.getDatasetMeta(1);
[ { b: 161, w: 91, x: 95, y: 32 },
{ b: 290, w: 91, x: 209, y: 97 },
{ b: 161, w: 91, x: 322, y: 161 },
{ b: 419, w: 91, x: 436, y: 471 }
].forEach(function(values, i) {
expect(meta1.data[i]._model.base).toBeCloseToPixel(values.b);
expect(meta1.data[i]._model.width).toBeCloseToPixel(values.w);
expect(meta1.data[i]._model.x).toBeCloseToPixel(values.x);
expect(meta1.data[i]._model.y).toBeCloseToPixel(values.y);
});
});
it('should draw all bars', function() {
var chart = window.acquireChart({
type: 'bar',
data: {
datasets: [{
data: [],
}, {
data: [10, 15, 0, -4],
label: 'dataset2'
}],
labels: ['label1', 'label2', 'label3', 'label4']
}
});
var meta = chart.getDatasetMeta(1);
spyOn(meta.data[0], 'draw');
spyOn(meta.data[1], 'draw');
spyOn(meta.data[2], 'draw');
spyOn(meta.data[3], 'draw');
chart.update();
expect(meta.data[0].draw.calls.count()).toBe(1);
expect(meta.data[1].draw.calls.count()).toBe(1);
expect(meta.data[2].draw.calls.count()).toBe(1);
expect(meta.data[3].draw.calls.count()).toBe(1);
});
it('should set hover styles on rectangles', function() {
var chart = window.acquireChart({
type: 'bar',
data: {
datasets: [{
data: [],
}, {
data: [10, 15, 0, -4],
label: 'dataset2'
}],
labels: ['label1', 'label2', 'label3', 'label4']
},
options: {
elements: {
rectangle: {
backgroundColor: 'rgb(255, 0, 0)',
borderColor: 'rgb(0, 0, 255)',
borderWidth: 2,
}
}
}
});
var meta = chart.getDatasetMeta(1);
var bar = meta.data[0];
meta.controller.setHoverStyle(bar);
expect(bar._model.backgroundColor).toBe('rgb(230, 0, 0)');
expect(bar._model.borderColor).toBe('rgb(0, 0, 230)');
expect(bar._model.borderWidth).toBe(2);
// Set a dataset style
chart.data.datasets[1].hoverBackgroundColor = 'rgb(128, 128, 128)';
chart.data.datasets[1].hoverBorderColor = 'rgb(0, 0, 0)';
chart.data.datasets[1].hoverBorderWidth = 5;
meta.controller.setHoverStyle(bar);
expect(bar._model.backgroundColor).toBe('rgb(128, 128, 128)');
expect(bar._model.borderColor).toBe('rgb(0, 0, 0)');
expect(bar._model.borderWidth).toBe(5);
// Should work with array styles so that we can set per bar
chart.data.datasets[1].hoverBackgroundColor = ['rgb(255, 255, 255)', 'rgb(128, 128, 128)'];
chart.data.datasets[1].hoverBorderColor = ['rgb(9, 9, 9)', 'rgb(0, 0, 0)'];
chart.data.datasets[1].hoverBorderWidth = [2.5, 5];
meta.controller.setHoverStyle(bar);
expect(bar._model.backgroundColor).toBe('rgb(255, 255, 255)');
expect(bar._model.borderColor).toBe('rgb(9, 9, 9)');
expect(bar._model.borderWidth).toBe(2.5);
// Should allow a custom style
bar.custom = {
hoverBackgroundColor: 'rgb(255, 0, 0)',
hoverBorderColor: 'rgb(0, 255, 0)',
hoverBorderWidth: 1.5
};
meta.controller.setHoverStyle(bar);
expect(bar._model.backgroundColor).toBe('rgb(255, 0, 0)');
expect(bar._model.borderColor).toBe('rgb(0, 255, 0)');
expect(bar._model.borderWidth).toBe(1.5);
});
it('should remove a hover style from a bar', function() {
var chart = window.acquireChart({
type: 'bar',
data: {
datasets: [{
data: [],
}, {
data: [10, 15, 0, -4],
label: 'dataset2'
}],
labels: ['label1', 'label2', 'label3', 'label4']
},
options: {
elements: {
rectangle: {
backgroundColor: 'rgb(255, 0, 0)',
borderColor: 'rgb(0, 0, 255)',
borderWidth: 2,
}
}
}
});
var meta = chart.getDatasetMeta(1);
var bar = meta.data[0];
// Change default
chart.options.elements.rectangle.backgroundColor = 'rgb(128, 128, 128)';
chart.options.elements.rectangle.borderColor = 'rgb(15, 15, 15)';
chart.options.elements.rectangle.borderWidth = 3.14;
// Remove to defaults
meta.controller.removeHoverStyle(bar);
expect(bar._model.backgroundColor).toBe('rgb(128, 128, 128)');
expect(bar._model.borderColor).toBe('rgb(15, 15, 15)');
expect(bar._model.borderWidth).toBe(3.14);
// Should work with array styles so that we can set per bar
chart.data.datasets[1].backgroundColor = ['rgb(255, 255, 255)', 'rgb(128, 128, 128)'];
chart.data.datasets[1].borderColor = ['rgb(9, 9, 9)', 'rgb(0, 0, 0)'];
chart.data.datasets[1].borderWidth = [2.5, 5];
meta.controller.removeHoverStyle(bar);
expect(bar._model.backgroundColor).toBe('rgb(255, 255, 255)');
expect(bar._model.borderColor).toBe('rgb(9, 9, 9)');
expect(bar._model.borderWidth).toBe(2.5);
// Should allow a custom style
bar.custom = {
backgroundColor: 'rgb(255, 0, 0)',
borderColor: 'rgb(0, 255, 0)',
borderWidth: 1.5
};
meta.controller.removeHoverStyle(bar);
expect(bar._model.backgroundColor).toBe('rgb(255, 0, 0)');
expect(bar._model.borderColor).toBe('rgb(0, 255, 0)');
expect(bar._model.borderWidth).toBe(1.5);
});
});

View File

@@ -0,0 +1,440 @@
// Test the bubble controller
describe('Bubble controller tests', function() {
beforeEach(function() {
window.addDefaultMatchers(jasmine);
});
afterEach(function() {
window.releaseAllCharts();
});
it('should be constructed', function() {
var chart = window.acquireChart({
type: 'bubble',
data: {
datasets: [{
data: []
}]
}
});
var meta = chart.getDatasetMeta(0);
expect(meta.type).toBe('bubble');
expect(meta.controller).not.toBe(undefined);
expect(meta.controller.index).toBe(0);
expect(meta.data).toEqual([]);
meta.controller.updateIndex(1);
expect(meta.controller.index).toBe(1);
});
it('should use the first scale IDs if the dataset does not specify them', function() {
var chart = window.acquireChart({
type: 'bubble',
data: {
datasets: [{
data: []
}]
},
options: {
scales: {
xAxes: [{
id: 'firstXScaleID'
}],
yAxes: [{
id: 'firstYScaleID'
}]
}
}
});
var meta = chart.getDatasetMeta(0);
expect(meta.xAxisID).toBe('firstXScaleID');
expect(meta.yAxisID).toBe('firstYScaleID');
});
it('should create point elements for each data item during initialization', function() {
var chart = window.acquireChart({
type: 'bubble',
data: {
datasets: [{
data: [10, 15, 0, -4]
}]
}
});
var meta = chart.getDatasetMeta(0);
expect(meta.data.length).toBe(4); // 4 points created
expect(meta.data[0] instanceof Chart.elements.Point).toBe(true);
expect(meta.data[1] instanceof Chart.elements.Point).toBe(true);
expect(meta.data[2] instanceof Chart.elements.Point).toBe(true);
expect(meta.data[3] instanceof Chart.elements.Point).toBe(true);
});
it('should draw all elements', function() {
var chart = window.acquireChart({
type: 'bubble',
data: {
datasets: [{
data: [10, 15, 0, -4]
}]
},
options: {
animation: false,
showLines: true
}
});
var meta = chart.getDatasetMeta(0);
spyOn(meta.data[0], 'draw');
spyOn(meta.data[1], 'draw');
spyOn(meta.data[2], 'draw');
spyOn(meta.data[3], 'draw');
chart.update();
expect(meta.data[0].draw.calls.count()).toBe(1);
expect(meta.data[1].draw.calls.count()).toBe(1);
expect(meta.data[2].draw.calls.count()).toBe(1);
expect(meta.data[3].draw.calls.count()).toBe(1);
});
it('should update elements when modifying style', function() {
var chart = window.acquireChart({
type: 'bubble',
data: {
datasets: [{
data: [{
x: 10,
y: 10,
r: 5
}, {
x: -15,
y: -10,
r: 1
}, {
x: 0,
y: -9,
r: 2
}, {
x: -4,
y: 10,
r: 1
}]
}],
labels: ['label1', 'label2', 'label3', 'label4']
},
options: {
scales: {
xAxes: [{
type: 'category'
}],
yAxes: [{
type: 'linear'
}]
}
}
});
var meta = chart.getDatasetMeta(0);
[ { r: 5, x: 38, y: 32 },
{ r: 1, x: 189, y: 484 },
{ r: 2, x: 341, y: 461 },
{ r: 1, x: 492, y: 32 }
].forEach(function(expected, i) {
expect(meta.data[i]._model.radius).toBe(expected.r);
expect(meta.data[i]._model.x).toBeCloseToPixel(expected.x);
expect(meta.data[i]._model.y).toBeCloseToPixel(expected.y);
expect(meta.data[i]._model).toEqual(jasmine.objectContaining({
backgroundColor: Chart.defaults.global.defaultColor,
borderColor: Chart.defaults.global.defaultColor,
borderWidth: 1,
hitRadius: 1,
skip: false
}));
});
// Use dataset level styles for lines & points
chart.data.datasets[0].backgroundColor = 'rgb(98, 98, 98)';
chart.data.datasets[0].borderColor = 'rgb(8, 8, 8)';
chart.data.datasets[0].borderWidth = 0.55;
// point styles
chart.data.datasets[0].radius = 22;
chart.data.datasets[0].hitRadius = 3.3;
chart.update();
for (var i=0; i<4; ++i) {
expect(meta.data[i]._model).toEqual(jasmine.objectContaining({
backgroundColor: 'rgb(98, 98, 98)',
borderColor: 'rgb(8, 8, 8)',
borderWidth: 0.55,
hitRadius: 3.3,
skip: false
}));
}
// point styles
meta.data[0].custom = {
radius: 2.2,
backgroundColor: 'rgb(0, 1, 3)',
borderColor: 'rgb(4, 6, 8)',
borderWidth: 0.787,
tension: 0.15,
hitRadius: 5,
skip: true
};
chart.update();
expect(meta.data[0]._model).toEqual(jasmine.objectContaining({
backgroundColor: 'rgb(0, 1, 3)',
borderColor: 'rgb(4, 6, 8)',
borderWidth: 0.787,
hitRadius: 5,
skip: true
}));
});
it('should handle number of data point changes in update', function() {
var chart = window.acquireChart({
type: 'bubble',
data: {
datasets: [{
data: [{
x: 10,
y: 10,
r: 5
}, {
x: -15,
y: -10,
r: 1
}, {
x: 0,
y: -9,
r: 2
}, {
x: -4,
y: 10,
r: 1
}]
}],
labels: ['label1', 'label2', 'label3', 'label4']
}
});
var meta = chart.getDatasetMeta(0);
expect(meta.data.length).toBe(4);
chart.data.datasets[0].data = [{
x: 1,
y: 1,
r: 10
}, {
x: 10,
y: 5,
r: 2
}]; // remove 2 items
chart.update();
expect(meta.data.length).toBe(2);
expect(meta.data[0] instanceof Chart.elements.Point).toBe(true);
expect(meta.data[1] instanceof Chart.elements.Point).toBe(true);
chart.data.datasets[0].data = [{
x: 10,
y: 10,
r: 5
}, {
x: -15,
y: -10,
r: 1
}, {
x: 0,
y: -9,
r: 2
}, {
x: -4,
y: 10,
r: 1
}, {
x: -5,
y: 0,
r: 3
}]; // add 3 items
chart.update();
expect(meta.data.length).toBe(5);
expect(meta.data[0] instanceof Chart.elements.Point).toBe(true);
expect(meta.data[1] instanceof Chart.elements.Point).toBe(true);
expect(meta.data[2] instanceof Chart.elements.Point).toBe(true);
expect(meta.data[3] instanceof Chart.elements.Point).toBe(true);
expect(meta.data[4] instanceof Chart.elements.Point).toBe(true);
});
it('should set hover styles', function() {
var chart = window.acquireChart({
type: 'bubble',
data: {
datasets: [{
data: [{
x: 10,
y: 10,
r: 5
}, {
x: -15,
y: -10,
r: 1
}, {
x: 0,
y: -9,
r: 2
}, {
x: -4,
y: 10,
r: 1
}]
}],
labels: ['label1', 'label2', 'label3', 'label4']
},
options: {
elements: {
point: {
backgroundColor: 'rgb(255, 255, 0)',
borderWidth: 1,
borderColor: 'rgb(255, 255, 255)',
hitRadius: 1,
hoverRadius: 4,
hoverBorderWidth: 1,
radius: 3
}
}
}
});
var meta = chart.getDatasetMeta(0);
var point = meta.data[0];
meta.controller.setHoverStyle(point);
expect(point._model.backgroundColor).toBe('rgb(229, 230, 0)');
expect(point._model.borderColor).toBe('rgb(230, 230, 230)');
expect(point._model.borderWidth).toBe(1);
expect(point._model.radius).toBe(9);
// Can set hover style per dataset
chart.data.datasets[0].hoverRadius = 3.3;
chart.data.datasets[0].hoverBackgroundColor = 'rgb(77, 79, 81)';
chart.data.datasets[0].hoverBorderColor = 'rgb(123, 125, 127)';
chart.data.datasets[0].hoverBorderWidth = 2.1;
meta.controller.setHoverStyle(point);
expect(point._model.backgroundColor).toBe('rgb(77, 79, 81)');
expect(point._model.borderColor).toBe('rgb(123, 125, 127)');
expect(point._model.borderWidth).toBe(2.1);
expect(point._model.radius).toBe(8.3);
// Custom style
point.custom = {
hoverRadius: 4.4,
hoverBorderWidth: 5.5,
hoverBackgroundColor: 'rgb(0, 0, 0)',
hoverBorderColor: 'rgb(10, 10, 10)'
};
meta.controller.setHoverStyle(point);
expect(point._model.backgroundColor).toBe('rgb(0, 0, 0)');
expect(point._model.borderColor).toBe('rgb(10, 10, 10)');
expect(point._model.borderWidth).toBe(5.5);
expect(point._model.radius).toBe(4.4);
});
it('should remove hover styles', function() {
var chart = window.acquireChart({
type: 'bubble',
data: {
datasets: [{
data: [{
x: 10,
y: 10,
r: 5
}, {
x: -15,
y: -10,
r: 1
}, {
x: 0,
y: -9,
r: 2
}, {
x: -4,
y: 10,
r: 1
}]
}],
labels: ['label1', 'label2', 'label3', 'label4']
},
options: {
elements: {
point: {
backgroundColor: 'rgb(255, 255, 0)',
borderWidth: 1,
borderColor: 'rgb(255, 255, 255)',
hitRadius: 1,
hoverRadius: 4,
hoverBorderWidth: 1,
radius: 3
}
}
}
});
var meta = chart.getDatasetMeta(0);
var point = meta.data[0];
chart.options.elements.point.backgroundColor = 'rgb(45, 46, 47)';
chart.options.elements.point.borderColor = 'rgb(50, 51, 52)';
chart.options.elements.point.borderWidth = 10.1;
chart.options.elements.point.radius = 1.01;
meta.controller.removeHoverStyle(point);
expect(point._model.backgroundColor).toBe('rgb(45, 46, 47)');
expect(point._model.borderColor).toBe('rgb(50, 51, 52)');
expect(point._model.borderWidth).toBe(10.1);
expect(point._model.radius).toBe(5);
// Can set hover style per dataset
chart.data.datasets[0].radius = 3.3;
chart.data.datasets[0].backgroundColor = 'rgb(77, 79, 81)';
chart.data.datasets[0].borderColor = 'rgb(123, 125, 127)';
chart.data.datasets[0].borderWidth = 2.1;
meta.controller.removeHoverStyle(point);
expect(point._model.backgroundColor).toBe('rgb(77, 79, 81)');
expect(point._model.borderColor).toBe('rgb(123, 125, 127)');
expect(point._model.borderWidth).toBe(2.1);
expect(point._model.radius).toBe(5);
// Custom style
point.custom = {
radius: 4.4,
borderWidth: 5.5,
backgroundColor: 'rgb(0, 0, 0)',
borderColor: 'rgb(10, 10, 10)'
};
meta.controller.removeHoverStyle(point);
expect(point._model.backgroundColor).toBe('rgb(0, 0, 0)');
expect(point._model.borderColor).toBe('rgb(10, 10, 10)');
expect(point._model.borderWidth).toBe(5.5);
expect(point._model.radius).toBe(4.4);
});
});

View File

@@ -0,0 +1,348 @@
// Test the bar controller
describe('Doughnut controller tests', function() {
beforeEach(function() {
window.addDefaultMatchers(jasmine);
});
afterEach(function() {
window.releaseAllCharts();
});
it('should be constructed', function() {
var chart = window.acquireChart({
type: 'doughnut',
data: {
datasets: [{
data: []
}],
labels: []
}
});
var meta = chart.getDatasetMeta(0);
expect(meta.type).toBe('doughnut');
expect(meta.controller).not.toBe(undefined);
expect(meta.controller.index).toBe(0);
expect(meta.data).toEqual([]);
meta.controller.updateIndex(1);
expect(meta.controller.index).toBe(1);
});
it('should create arc elements for each data item during initialization', function() {
var chart = window.acquireChart({
type: 'doughnut',
data: {
datasets: [{
data: [10, 15, 0, 4]
}],
labels: []
}
});
var meta = chart.getDatasetMeta(0);
expect(meta.data.length).toBe(4); // 4 rectangles created
expect(meta.data[0] instanceof Chart.elements.Arc).toBe(true);
expect(meta.data[1] instanceof Chart.elements.Arc).toBe(true);
expect(meta.data[2] instanceof Chart.elements.Arc).toBe(true);
expect(meta.data[3] instanceof Chart.elements.Arc).toBe(true);
});
it ('should reset and update elements', function() {
var chart = window.acquireChart({
type: 'doughnut',
data: {
datasets: [{
data: [1, 2, 3, 4],
hidden: true
}, {
data: [5, 6, 0, 7]
}, {
data: [8, 9, 10, 11]
}],
labels: ['label0', 'label1', 'label2', 'label3']
},
options: {
animation: {
animateRotate: true,
animateScale: false
},
cutoutPercentage: 50,
rotation: Math.PI * -0.5,
circumference: Math.PI * 2.0,
elements: {
arc: {
backgroundColor: 'rgb(255, 0, 0)',
borderColor: 'rgb(0, 0, 255)',
borderWidth: 2,
hoverBackgroundColor: 'rgb(255, 255, 255)'
}
}
}
});
var meta = chart.getDatasetMeta(1);
meta.controller.reset(); // reset first
expect(meta.data.length).toBe(4);
[ { c: 0 },
{ c: 0 },
{ c: 0, },
{ c: 0 }
].forEach(function(expected, i) {
expect(meta.data[i]._model.x).toBeCloseToPixel(256);
expect(meta.data[i]._model.y).toBeCloseToPixel(272);
expect(meta.data[i]._model.outerRadius).toBeCloseToPixel(239);
expect(meta.data[i]._model.innerRadius).toBeCloseToPixel(179);
expect(meta.data[i]._model.circumference).toBeCloseTo(expected.c, 8);
expect(meta.data[i]._model).toEqual(jasmine.objectContaining({
startAngle: Math.PI * -0.5,
endAngle: Math.PI * -0.5,
label: chart.data.labels[i],
hoverBackgroundColor: 'rgb(255, 255, 255)',
backgroundColor: 'rgb(255, 0, 0)',
borderColor: 'rgb(0, 0, 255)',
borderWidth: 2
}));
})
chart.update();
[ { c: 1.7453292519, s: -1.5707963267, e: 0.1745329251 },
{ c: 2.0943951023, s: 0.1745329251, e: 2.2689280275 },
{ c: 0, s: 2.2689280275, e: 2.2689280275 },
{ c: 2.4434609527, s: 2.2689280275, e: 4.7123889803 }
].forEach(function(expected, i) {
expect(meta.data[i]._model.x).toBeCloseToPixel(256);
expect(meta.data[i]._model.y).toBeCloseToPixel(272);
expect(meta.data[i]._model.outerRadius).toBeCloseToPixel(239);
expect(meta.data[i]._model.innerRadius).toBeCloseToPixel(179);
expect(meta.data[i]._model.circumference).toBeCloseTo(expected.c, 8);
expect(meta.data[i]._model.startAngle).toBeCloseTo(expected.s, 8);
expect(meta.data[i]._model.endAngle).toBeCloseTo(expected.e, 8);
expect(meta.data[i]._model).toEqual(jasmine.objectContaining({
label: chart.data.labels[i],
hoverBackgroundColor: 'rgb(255, 255, 255)',
backgroundColor: 'rgb(255, 0, 0)',
borderColor: 'rgb(0, 0, 255)',
borderWidth: 2
}));
})
// Change the amount of data and ensure that arcs are updated accordingly
chart.data.datasets[1].data = [1, 2]; // remove 2 elements from dataset 0
chart.update();
expect(meta.data.length).toBe(2);
expect(meta.data[0] instanceof Chart.elements.Arc).toBe(true);
expect(meta.data[1] instanceof Chart.elements.Arc).toBe(true);
// Add data
chart.data.datasets[1].data = [1, 2, 3, 4];
chart.update();
expect(meta.data.length).toBe(4);
expect(meta.data[0] instanceof Chart.elements.Arc).toBe(true);
expect(meta.data[1] instanceof Chart.elements.Arc).toBe(true);
expect(meta.data[2] instanceof Chart.elements.Arc).toBe(true);
expect(meta.data[3] instanceof Chart.elements.Arc).toBe(true);
});
it ('should rotate and limit circumference', function() {
var chart = window.acquireChart({
type: 'doughnut',
data: {
datasets: [{
data: [2, 4],
hidden: true
}, {
data: [1, 3]
}, {
data: [1, 0]
}],
labels: ['label0', 'label1']
},
options: {
cutoutPercentage: 50,
rotation: Math.PI,
circumference: Math.PI * 0.5,
elements: {
arc: {
backgroundColor: 'rgb(255, 0, 0)',
borderColor: 'rgb(0, 0, 255)',
borderWidth: 2,
hoverBackgroundColor: 'rgb(255, 255, 255)'
}
}
}
});
var meta = chart.getDatasetMeta(1);
expect(meta.data.length).toBe(2);
// Only startAngle, endAngle and circumference should be different.
[ { c: Math.PI / 8, s: Math.PI, e: Math.PI + Math.PI / 8 },
{ c: 3 * Math.PI / 8, s: Math.PI + Math.PI / 8, e: Math.PI + Math.PI / 2 }
].forEach(function(expected, i) {
expect(meta.data[i]._model.x).toBeCloseToPixel(495);
expect(meta.data[i]._model.y).toBeCloseToPixel(511);
expect(meta.data[i]._model.outerRadius).toBeCloseToPixel(478);
expect(meta.data[i]._model.innerRadius).toBeCloseToPixel(359);
expect(meta.data[i]._model.circumference).toBeCloseTo(expected.c,8);
expect(meta.data[i]._model.startAngle).toBeCloseTo(expected.s, 8);
expect(meta.data[i]._model.endAngle).toBeCloseTo(expected.e, 8);
})
});
it ('should draw all arcs', function() {
var chart = window.acquireChart({
type: 'doughnut',
data: {
datasets: [{
data: [10, 15, 0, 4]
}],
labels: ['label0', 'label1', 'label2', 'label3']
}
});
var meta = chart.getDatasetMeta(0);
spyOn(meta.data[0], 'draw');
spyOn(meta.data[1], 'draw');
spyOn(meta.data[2], 'draw');
spyOn(meta.data[3], 'draw');
chart.update();
expect(meta.data[0].draw.calls.count()).toBe(1);
expect(meta.data[1].draw.calls.count()).toBe(1);
expect(meta.data[2].draw.calls.count()).toBe(1);
expect(meta.data[3].draw.calls.count()).toBe(1);
});
it ('should set the hover style of an arc', function() {
var chart = window.acquireChart({
type: 'doughnut',
data: {
datasets: [{
data: [10, 15, 0, 4]
}],
labels: ['label0', 'label1', 'label2', 'label3']
},
options: {
elements: {
arc: {
backgroundColor: 'rgb(255, 0, 0)',
borderColor: 'rgb(0, 0, 255)',
borderWidth: 2,
}
}
}
});
var meta = chart.getDatasetMeta(0);
var arc = meta.data[0];
meta.controller.setHoverStyle(arc);
expect(arc._model.backgroundColor).toBe('rgb(230, 0, 0)');
expect(arc._model.borderColor).toBe('rgb(0, 0, 230)');
expect(arc._model.borderWidth).toBe(2);
// Set a dataset style to take precedence
chart.data.datasets[0].hoverBackgroundColor = 'rgb(9, 9, 9)';
chart.data.datasets[0].hoverBorderColor = 'rgb(18, 18, 18)';
chart.data.datasets[0].hoverBorderWidth = 1.56;
meta.controller.setHoverStyle(arc);
expect(arc._model.backgroundColor).toBe('rgb(9, 9, 9)');
expect(arc._model.borderColor).toBe('rgb(18, 18, 18)');
expect(arc._model.borderWidth).toBe(1.56);
// Dataset styles can be an array
chart.data.datasets[0].hoverBackgroundColor = ['rgb(255, 255, 255)', 'rgb(9, 9, 9)'];
chart.data.datasets[0].hoverBorderColor = ['rgb(18, 18, 18)'];
chart.data.datasets[0].hoverBorderWidth = [0.1, 1.56];
meta.controller.setHoverStyle(arc);
expect(arc._model.backgroundColor).toBe('rgb(255, 255, 255)');
expect(arc._model.borderColor).toBe('rgb(18, 18, 18)');
expect(arc._model.borderWidth).toBe(0.1);
// Element custom styles also work
arc.custom = {
hoverBackgroundColor: 'rgb(7, 7, 7)',
hoverBorderColor: 'rgb(17, 17, 17)',
hoverBorderWidth: 3.14159,
};
meta.controller.setHoverStyle(arc);
expect(arc._model.backgroundColor).toBe('rgb(7, 7, 7)');
expect(arc._model.borderColor).toBe('rgb(17, 17, 17)');
expect(arc._model.borderWidth).toBe(3.14159);
});
it ('should unset the hover style of an arc', function() {
var chart = window.acquireChart({
type: 'doughnut',
data: {
datasets: [{
data: [10, 15, 0, 4]
}],
labels: ['label0', 'label1', 'label2', 'label3']
},
options: {
elements: {
arc: {
backgroundColor: 'rgb(255, 0, 0)',
borderColor: 'rgb(0, 0, 255)',
borderWidth: 2,
}
}
}
});
var meta = chart.getDatasetMeta(0);
var arc = meta.data[0];
meta.controller.removeHoverStyle(arc);
expect(arc._model.backgroundColor).toBe('rgb(255, 0, 0)');
expect(arc._model.borderColor).toBe('rgb(0, 0, 255)');
expect(arc._model.borderWidth).toBe(2);
// Set a dataset style to take precedence
chart.data.datasets[0].backgroundColor = 'rgb(9, 9, 9)';
chart.data.datasets[0].borderColor = 'rgb(18, 18, 18)';
chart.data.datasets[0].borderWidth = 1.56;
meta.controller.removeHoverStyle(arc);
expect(arc._model.backgroundColor).toBe('rgb(9, 9, 9)');
expect(arc._model.borderColor).toBe('rgb(18, 18, 18)');
expect(arc._model.borderWidth).toBe(1.56);
// Dataset styles can be an array
chart.data.datasets[0].backgroundColor = ['rgb(255, 255, 255)', 'rgb(9, 9, 9)'];
chart.data.datasets[0].borderColor = ['rgb(18, 18, 18)'];
chart.data.datasets[0].borderWidth = [0.1, 1.56];
meta.controller.removeHoverStyle(arc);
expect(arc._model.backgroundColor).toBe('rgb(255, 255, 255)');
expect(arc._model.borderColor).toBe('rgb(18, 18, 18)');
expect(arc._model.borderWidth).toBe(0.1);
// Element custom styles also work
arc.custom = {
backgroundColor: 'rgb(7, 7, 7)',
borderColor: 'rgb(17, 17, 17)',
borderWidth: 3.14159,
};
meta.controller.removeHoverStyle(arc);
expect(arc._model.backgroundColor).toBe('rgb(7, 7, 7)');
expect(arc._model.borderColor).toBe('rgb(17, 17, 17)');
expect(arc._model.borderWidth).toBe(3.14159);
});
});

View File

@@ -0,0 +1,493 @@
// Test the line controller
describe('Line controller tests', function() {
beforeEach(function() {
window.addDefaultMatchers(jasmine);
});
afterEach(function() {
window.releaseAllCharts();
});
it('should be constructed', function() {
var chart = window.acquireChart({
type: 'line',
data: {
datasets: [{
data: []
}],
labels: []
}
});
var meta = chart.getDatasetMeta(0);
expect(meta.type).toBe('line');
expect(meta.controller).not.toBe(undefined);
expect(meta.controller.index).toBe(0);
expect(meta.data).toEqual([]);
meta.controller.updateIndex(1);
expect(meta.controller.index).toBe(1);
});
it('Should use the first scale IDs if the dataset does not specify them', function() {
var chart = window.acquireChart({
type: 'line',
data: {
datasets: [{
data: []
}],
labels: []
},
options: {
scales: {
xAxes: [{
id: 'firstXScaleID'
}],
yAxes: [{
id: 'firstYScaleID'
}]
}
}
});
var meta = chart.getDatasetMeta(0);
expect(meta.xAxisID).toBe('firstXScaleID');
expect(meta.yAxisID).toBe('firstYScaleID');
});
it('Should create line elements and point elements for each data item during initialization', function() {
var chart = window.acquireChart({
type: 'line',
data: {
datasets: [{
data: [10, 15, 0, -4],
label: 'dataset1'
}],
labels: ['label1', 'label2', 'label3', 'label4']
}
});
var meta = chart.getDatasetMeta(0);
expect(meta.data.length).toBe(4); // 4 points created
expect(meta.data[0] instanceof Chart.elements.Point).toBe(true);
expect(meta.data[1] instanceof Chart.elements.Point).toBe(true);
expect(meta.data[2] instanceof Chart.elements.Point).toBe(true);
expect(meta.data[3] instanceof Chart.elements.Point).toBe(true);
expect(meta.dataset instanceof Chart.elements.Line).toBe(true); // 1 line element
});
it('should draw all elements', function() {
var chart = window.acquireChart({
type: 'line',
data: {
datasets: [{
data: [10, 15, 0, -4],
label: 'dataset1'
}],
labels: ['label1', 'label2', 'label3', 'label4']
},
options: {
showLines: true
}
});
var meta = chart.getDatasetMeta(0);
spyOn(meta.dataset, 'draw');
spyOn(meta.data[0], 'draw');
spyOn(meta.data[1], 'draw');
spyOn(meta.data[2], 'draw');
spyOn(meta.data[3], 'draw');
chart.update();
expect(meta.data[0].draw.calls.count()).toBe(1);
expect(meta.data[1].draw.calls.count()).toBe(1);
expect(meta.data[2].draw.calls.count()).toBe(1);
expect(meta.data[3].draw.calls.count()).toBe(1);
});
it('should draw all elements except lines', function() {
var chart = window.acquireChart({
type: 'line',
data: {
datasets: [{
data: [10, 15, 0, -4],
label: 'dataset1'
}],
labels: ['label1', 'label2', 'label3', 'label4']
},
options: {
showLines: false
}
});
var meta = chart.getDatasetMeta(0);
spyOn(meta.dataset, 'draw');
spyOn(meta.data[0], 'draw');
spyOn(meta.data[1], 'draw');
spyOn(meta.data[2], 'draw');
spyOn(meta.data[3], 'draw');
chart.update();
expect(meta.dataset.draw.calls.count()).toBe(0);
expect(meta.data[0].draw.calls.count()).toBe(1);
expect(meta.data[1].draw.calls.count()).toBe(1);
expect(meta.data[2].draw.calls.count()).toBe(1);
expect(meta.data[3].draw.calls.count()).toBe(1);
});
it('should update elements when modifying data', function() {
var chart = window.acquireChart({
type: 'line',
data: {
datasets: [{
data: [10, 15, 0, -4],
label: 'dataset',
xAxisID: 'firstXScaleID',
yAxisID: 'firstYScaleID'
}],
labels: ['label1', 'label2', 'label3', 'label4']
},
options: {
showLines: true,
elements: {
point: {
backgroundColor: 'red',
borderColor: 'blue',
}
},
scales: {
xAxes: [{
id: 'firstXScaleID'
}],
yAxes: [{
id: 'firstYScaleID'
}]
}
},
});
var meta = chart.getDatasetMeta(0);
expect(meta.data.length).toBe(4);
chart.data.datasets[0].data = [1, 2]; // remove 2 items
chart.data.datasets[0].borderWidth = 1;
chart.update();
expect(meta.data.length).toBe(2);
[ { x: 44, y: 484 },
{ x: 193, y: 32 }
].forEach(function(expected, i) {
expect(meta.data[i]._datasetIndex).toBe(0);
expect(meta.data[i]._index).toBe(i);
expect(meta.data[i]._xScale).toBe(chart.scales.firstXScaleID);
expect(meta.data[i]._yScale).toBe(chart.scales.firstYScaleID);
expect(meta.data[i]._model.x).toBeCloseToPixel(expected.x);
expect(meta.data[i]._model.y).toBeCloseToPixel(expected.y);
expect(meta.data[i]._model).toEqual(jasmine.objectContaining({
backgroundColor: 'red',
borderColor: 'blue',
}));
});
chart.data.datasets[0].data = [1, 2, 3]; // add 1 items
chart.update();
expect(meta.data.length).toBe(3); // should add a new meta data item
});
it('should update elements when the y scale is stacked', function() {
var chart = window.acquireChart({
type: 'line',
data: {
datasets: [{
data: [10, -10, 10, -10],
label: 'dataset1'
}, {
data: [10, 15, 0, -4],
label: 'dataset2'
}],
labels: ['label1', 'label2', 'label3', 'label4']
},
options: {
scales: {
yAxes: [{
stacked: true
}]
}
}
});
var meta0 = chart.getDatasetMeta(0);
[ { x: 38, y: 161 },
{ x: 189, y: 419 },
{ x: 341, y: 161 },
{ x: 492, y: 419 }
].forEach(function(values, i) {
expect(meta0.data[i]._model.x).toBeCloseToPixel(values.x);
expect(meta0.data[i]._model.y).toBeCloseToPixel(values.y);
});
var meta1 = chart.getDatasetMeta(1);
[ { x: 38, y: 32 },
{ x: 189, y: 97 },
{ x: 341, y: 161 },
{ x: 492, y: 471 }
].forEach(function(values, i) {
expect(meta1.data[i]._model.x).toBeCloseToPixel(values.x);
expect(meta1.data[i]._model.y).toBeCloseToPixel(values.y);
});
});
it('should find the correct scale zero when the data is all positive', function() {
var chart = window.acquireChart({
type: 'line',
data: {
datasets: [{
data: [10, 15, 20, 20],
label: 'dataset1',
}],
labels: ['label1', 'label2', 'label3', 'label4']
},
});
var meta = chart.getDatasetMeta(0);
expect(meta.dataset._model).toEqual(jasmine.objectContaining({
scaleTop: 32,
scaleBottom: 484,
scaleZero: 484,
}));
});
it('should find the correct scale zero when the data is all negative', function() {
var chart = window.acquireChart({
type: 'line',
data: {
datasets: [{
data: [-10, -15, -20, -20],
label: 'dataset1',
}],
labels: ['label1', 'label2', 'label3', 'label4']
},
});
var meta = chart.getDatasetMeta(0);
expect(meta.dataset._model).toEqual(jasmine.objectContaining({
scaleTop: 32,
scaleBottom: 484,
scaleZero: 32,
}));
});
it('should fall back to the line styles for points', function() {
var chart = window.acquireChart({
type: 'line',
data: {
datasets: [{
data: [0, 0],
label: 'dataset1',
// line styles
backgroundColor: 'rgb(98, 98, 98)',
borderColor: 'rgb(8, 8, 8)',
borderWidth: 0.55,
}],
labels: ['label1', 'label2']
}
});
var meta = chart.getDatasetMeta(0);
expect(meta.dataset._model.backgroundColor).toBe('rgb(98, 98, 98)');
expect(meta.dataset._model.borderColor).toBe('rgb(8, 8, 8)');
expect(meta.dataset._model.borderWidth).toBe(0.55);
});
it('should handle number of data point changes in update', function() {
var chart = window.acquireChart({
type: 'line',
data: {
datasets: [{
data: [10, 15, 0, -4],
label: 'dataset1',
}],
labels: ['label1', 'label2', 'label3', 'label4']
}
});
var meta = chart.getDatasetMeta(0);
chart.data.datasets[0].data = [1, 2]; // remove 2 items
chart.update();
expect(meta.data.length).toBe(2);
expect(meta.data[0] instanceof Chart.elements.Point).toBe(true);
expect(meta.data[1] instanceof Chart.elements.Point).toBe(true);
chart.data.datasets[0].data = [1, 2, 3, 4, 5]; // add 3 items
chart.update();
expect(meta.data.length).toBe(5);
expect(meta.data[0] instanceof Chart.elements.Point).toBe(true);
expect(meta.data[1] instanceof Chart.elements.Point).toBe(true);
expect(meta.data[2] instanceof Chart.elements.Point).toBe(true);
expect(meta.data[3] instanceof Chart.elements.Point).toBe(true);
expect(meta.data[4] instanceof Chart.elements.Point).toBe(true);
});
it('should set point hover styles', function() {
var chart = window.acquireChart({
type: 'line',
data: {
datasets: [{
data: [10, 15, 0, -4],
label: 'dataset1',
}],
labels: ['label1', 'label2', 'label3', 'label4']
},
options: {
elements: {
point: {
backgroundColor: 'rgb(255, 255, 0)',
borderWidth: 1,
borderColor: 'rgb(255, 255, 255)',
hitRadius: 1,
hoverRadius: 4,
hoverBorderWidth: 1,
radius: 3,
}
}
}
});
var meta = chart.getDatasetMeta(0);
var point = meta.data[0];
meta.controller.setHoverStyle(point);
expect(point._model.backgroundColor).toBe('rgb(229, 230, 0)');
expect(point._model.borderColor).toBe('rgb(230, 230, 230)');
expect(point._model.borderWidth).toBe(1);
expect(point._model.radius).toBe(4);
// Can set hover style per dataset
chart.data.datasets[0].pointHoverRadius = 3.3;
chart.data.datasets[0].pointHoverBackgroundColor = 'rgb(77, 79, 81)';
chart.data.datasets[0].pointHoverBorderColor = 'rgb(123, 125, 127)';
chart.data.datasets[0].pointHoverBorderWidth = 2.1;
meta.controller.setHoverStyle(point);
expect(point._model.backgroundColor).toBe('rgb(77, 79, 81)');
expect(point._model.borderColor).toBe('rgb(123, 125, 127)');
expect(point._model.borderWidth).toBe(2.1);
expect(point._model.radius).toBe(3.3);
// Use the consistent name "pointRadius", setting but overwriting
// another value in "radius"
chart.data.datasets[0].pointRadius = 250;
chart.data.datasets[0].radius = 20;
meta.controller.setHoverStyle(point);
expect(point._model.backgroundColor).toBe('rgb(77, 79, 81)');
expect(point._model.borderColor).toBe('rgb(123, 125, 127)');
expect(point._model.borderWidth).toBe(2.1);
expect(point._model.radius).toBe(3.3);
// Custom style
point.custom = {
hoverRadius: 4.4,
hoverBorderWidth: 5.5,
hoverBackgroundColor: 'rgb(0, 0, 0)',
hoverBorderColor: 'rgb(10, 10, 10)'
};
meta.controller.setHoverStyle(point);
expect(point._model.backgroundColor).toBe('rgb(0, 0, 0)');
expect(point._model.borderColor).toBe('rgb(10, 10, 10)');
expect(point._model.borderWidth).toBe(5.5);
expect(point._model.radius).toBe(4.4);
});
it('should remove hover styles', function() {
var chart = window.acquireChart({
type: 'line',
data: {
datasets: [{
data: [10, 15, 0, -4],
label: 'dataset1',
}],
labels: ['label1', 'label2', 'label3', 'label4']
},
options: {
elements: {
point: {
backgroundColor: 'rgb(255, 255, 0)',
borderWidth: 1,
borderColor: 'rgb(255, 255, 255)',
hitRadius: 1,
hoverRadius: 4,
hoverBorderWidth: 1,
radius: 3,
}
}
}
});
var meta = chart.getDatasetMeta(0);
var point = meta.data[0];
chart.options.elements.point.backgroundColor = 'rgb(45, 46, 47)';
chart.options.elements.point.borderColor = 'rgb(50, 51, 52)';
chart.options.elements.point.borderWidth = 10.1;
chart.options.elements.point.radius = 1.01;
meta.controller.removeHoverStyle(point);
expect(point._model.backgroundColor).toBe('rgb(45, 46, 47)');
expect(point._model.borderColor).toBe('rgb(50, 51, 52)');
expect(point._model.borderWidth).toBe(10.1);
expect(point._model.radius).toBe(1.01);
// Can set hover style per dataset
chart.data.datasets[0].radius = 3.3;
chart.data.datasets[0].pointBackgroundColor = 'rgb(77, 79, 81)';
chart.data.datasets[0].pointBorderColor = 'rgb(123, 125, 127)';
chart.data.datasets[0].pointBorderWidth = 2.1;
meta.controller.removeHoverStyle(point);
expect(point._model.backgroundColor).toBe('rgb(77, 79, 81)');
expect(point._model.borderColor).toBe('rgb(123, 125, 127)');
expect(point._model.borderWidth).toBe(2.1);
expect(point._model.radius).toBe(3.3);
// Use the consistent name "pointRadius", setting but overwriting
// another value in "radius"
chart.data.datasets[0].pointRadius = 250;
chart.data.datasets[0].radius = 20;
meta.controller.removeHoverStyle(point);
expect(point._model.backgroundColor).toBe('rgb(77, 79, 81)');
expect(point._model.borderColor).toBe('rgb(123, 125, 127)');
expect(point._model.borderWidth).toBe(2.1);
expect(point._model.radius).toBe(250);
// Custom style
point.custom = {
radius: 4.4,
borderWidth: 5.5,
backgroundColor: 'rgb(0, 0, 0)',
borderColor: 'rgb(10, 10, 10)'
};
meta.controller.removeHoverStyle(point);
expect(point._model.backgroundColor).toBe('rgb(0, 0, 0)');
expect(point._model.borderColor).toBe('rgb(10, 10, 10)');
expect(point._model.borderWidth).toBe(5.5);
expect(point._model.radius).toBe(4.4);
});
});

View File

@@ -0,0 +1,318 @@
// Test the polar area controller
describe('Polar area controller tests', function() {
beforeEach(function() {
window.addDefaultMatchers(jasmine);
});
afterEach(function() {
window.releaseAllCharts();
});
it('should be constructed', function() {
var chart = window.acquireChart({
type: 'polarArea',
data: {
datasets: [
{ data: [] },
{ data: [] }
],
labels: []
}
});
var meta = chart.getDatasetMeta(1);
expect(meta.type).toEqual('polarArea');
expect(meta.data).toEqual([]);
expect(meta.hidden).toBe(null);
expect(meta.controller).not.toBe(undefined);
expect(meta.controller.index).toBe(1);
meta.controller.updateIndex(0);
expect(meta.controller.index).toBe(0);
});
it('should create arc elements for each data item during initialization', function() {
var chart = window.acquireChart({
type: 'polarArea',
data: {
datasets: [
{ data: [] },
{ data: [10, 15, 0, -4] }
],
labels: []
}
});
var meta = chart.getDatasetMeta(1);
expect(meta.data.length).toBe(4); // 4 arcs created
expect(meta.data[0] instanceof Chart.elements.Arc).toBe(true);
expect(meta.data[1] instanceof Chart.elements.Arc).toBe(true);
expect(meta.data[2] instanceof Chart.elements.Arc).toBe(true);
expect(meta.data[3] instanceof Chart.elements.Arc).toBe(true);
});
it('should draw all elements', function() {
var chart = window.acquireChart({
type: 'polarArea',
data: {
datasets: [{
data: [10, 15, 0, -4],
label: 'dataset2'
}],
labels: ['label1', 'label2', 'label3', 'label4']
}
});
var meta = chart.getDatasetMeta(0);
spyOn(meta.data[0], 'draw');
spyOn(meta.data[1], 'draw');
spyOn(meta.data[2], 'draw');
spyOn(meta.data[3], 'draw');
chart.update();
expect(meta.data[0].draw.calls.count()).toBe(1);
expect(meta.data[1].draw.calls.count()).toBe(1);
expect(meta.data[2].draw.calls.count()).toBe(1);
expect(meta.data[3].draw.calls.count()).toBe(1);
});
it('should update elements when modifying data', function() {
var chart = window.acquireChart({
type: 'polarArea',
data: {
datasets: [{
data: [10, 15, 0, -4],
label: 'dataset2'
}],
labels: ['label1', 'label2', 'label3', 'label4']
},
options: {
showLines: true,
elements: {
arc: {
backgroundColor: 'rgb(255, 0, 0)',
borderColor: 'rgb(0, 255, 0)',
borderWidth: 1.2
}
}
}
});
var meta = chart.getDatasetMeta(0);
expect(meta.data.length).toBe(4);
[ { o: 156, s: -0.5 * Math.PI, e: 0 },
{ o: 211, s: 0, e: 0.5 * Math.PI },
{ o: 45, s: 0.5 * Math.PI, e: Math.PI },
{ o: 0, s: Math.PI, e: 1.5 * Math.PI }
].forEach(function(expected, i) {
expect(meta.data[i]._model.x).toBeCloseToPixel(256);
expect(meta.data[i]._model.y).toBeCloseToPixel(272);
expect(meta.data[i]._model.innerRadius).toBeCloseToPixel(0);
expect(meta.data[i]._model.outerRadius).toBeCloseToPixel(expected.o);
expect(meta.data[i]._model.startAngle).toBe(expected.s);
expect(meta.data[i]._model.endAngle).toBe(expected.e);
expect(meta.data[i]._model).toEqual(jasmine.objectContaining({
backgroundColor: 'rgb(255, 0, 0)',
borderColor: 'rgb(0, 255, 0)',
borderWidth: 1.2,
label: chart.data.labels[i]
}));
});
// arc styles
chart.data.datasets[0].backgroundColor = 'rgb(128, 129, 130)';
chart.data.datasets[0].borderColor = 'rgb(56, 57, 58)';
chart.data.datasets[0].borderWidth = 1.123;
chart.update();
for (var i = 0; i < 4; ++i) {
expect(meta.data[i]._model.backgroundColor).toBe('rgb(128, 129, 130)');
expect(meta.data[i]._model.borderColor).toBe('rgb(56, 57, 58)');
expect(meta.data[i]._model.borderWidth).toBe(1.123);
}
// arc styles
meta.data[0].custom = {
backgroundColor: 'rgb(0, 1, 3)',
borderColor: 'rgb(4, 6, 8)',
borderWidth: 0.787
};
chart.update();
expect(meta.data[0]._model.x).toBeCloseToPixel(256);
expect(meta.data[0]._model.y).toBeCloseToPixel(272);
expect(meta.data[0]._model.innerRadius).toBeCloseToPixel(0);
expect(meta.data[0]._model.outerRadius).toBeCloseToPixel(156);
expect(meta.data[0]._model).toEqual(jasmine.objectContaining({
startAngle: -0.5 * Math.PI,
endAngle: 0,
backgroundColor: 'rgb(0, 1, 3)',
borderWidth: 0.787,
borderColor: 'rgb(4, 6, 8)',
label: 'label1'
}));
});
it('should handle number of data point changes in update', function() {
var chart = window.acquireChart({
type: 'polarArea',
data: {
datasets: [{
data: [10, 15, 0, -4],
label: 'dataset2'
}],
labels: ['label1', 'label2', 'label3', 'label4']
},
options: {
showLines: true,
elements: {
arc: {
backgroundColor: 'rgb(255, 0, 0)',
borderColor: 'rgb(0, 255, 0)',
borderWidth: 1.2
}
}
}
});
var meta = chart.getDatasetMeta(0);
expect(meta.data.length).toBe(4);
// remove 2 items
chart.data.labels = ['label1', 'label2'];
chart.data.datasets[0].data = [1, 2];
chart.update();
expect(meta.data.length).toBe(2);
expect(meta.data[0] instanceof Chart.elements.Arc).toBe(true);
expect(meta.data[1] instanceof Chart.elements.Arc).toBe(true);
// add 3 items
chart.data.labels = ['label1', 'label2', 'label3', 'label4', 'label5'];
chart.data.datasets[0].data = [1, 2, 3, 4, 5];
chart.update();
expect(meta.data.length).toBe(5);
expect(meta.data[0] instanceof Chart.elements.Arc).toBe(true);
expect(meta.data[1] instanceof Chart.elements.Arc).toBe(true);
expect(meta.data[2] instanceof Chart.elements.Arc).toBe(true);
expect(meta.data[3] instanceof Chart.elements.Arc).toBe(true);
expect(meta.data[4] instanceof Chart.elements.Arc).toBe(true);
});
it('should set arc hover styles', function() {
var chart = window.acquireChart({
type: 'polarArea',
data: {
datasets: [{
data: [10, 15, 0, -4],
label: 'dataset2'
}],
labels: ['label1', 'label2', 'label3', 'label4']
},
options: {
showLines: true,
elements: {
arc: {
backgroundColor: 'rgb(255, 0, 0)',
borderColor: 'rgb(0, 255, 0)',
borderWidth: 1.2
}
}
}
});
var meta = chart.getDatasetMeta(0);
var arc = meta.data[0];
meta.controller.setHoverStyle(arc);
expect(arc._model.backgroundColor).toBe('rgb(230, 0, 0)');
expect(arc._model.borderColor).toBe('rgb(0, 230, 0)');
expect(arc._model.borderWidth).toBe(1.2);
// Can set hover style per dataset
chart.data.datasets[0].hoverBackgroundColor = 'rgb(77, 79, 81)';
chart.data.datasets[0].hoverBorderColor = 'rgb(123, 125, 127)';
chart.data.datasets[0].hoverBorderWidth = 2.1;
meta.controller.setHoverStyle(arc);
expect(arc._model.backgroundColor).toBe('rgb(77, 79, 81)');
expect(arc._model.borderColor).toBe('rgb(123, 125, 127)');
expect(arc._model.borderWidth).toBe(2.1);
// Custom style
arc.custom = {
hoverBorderWidth: 5.5,
hoverBackgroundColor: 'rgb(0, 0, 0)',
hoverBorderColor: 'rgb(10, 10, 10)'
};
meta.controller.setHoverStyle(arc);
expect(arc._model.backgroundColor).toBe('rgb(0, 0, 0)');
expect(arc._model.borderColor).toBe('rgb(10, 10, 10)');
expect(arc._model.borderWidth).toBe(5.5);
});
it('should remove hover styles', function() {
var chart = window.acquireChart({
type: 'polarArea',
data: {
datasets: [{
data: [10, 15, 0, -4],
label: 'dataset2'
}],
labels: ['label1', 'label2', 'label3', 'label4']
},
options: {
showLines: true,
elements: {
arc: {
backgroundColor: 'rgb(255, 0, 0)',
borderColor: 'rgb(0, 255, 0)',
borderWidth: 1.2
}
}
}
});
var meta = chart.getDatasetMeta(0);
var arc = meta.data[0];
chart.options.elements.arc.backgroundColor = 'rgb(45, 46, 47)';
chart.options.elements.arc.borderColor = 'rgb(50, 51, 52)';
chart.options.elements.arc.borderWidth = 10.1;
meta.controller.removeHoverStyle(arc);
expect(arc._model.backgroundColor).toBe('rgb(45, 46, 47)');
expect(arc._model.borderColor).toBe('rgb(50, 51, 52)');
expect(arc._model.borderWidth).toBe(10.1);
// Can set hover style per dataset
chart.data.datasets[0].backgroundColor = 'rgb(77, 79, 81)';
chart.data.datasets[0].borderColor = 'rgb(123, 125, 127)';
chart.data.datasets[0].borderWidth = 2.1;
meta.controller.removeHoverStyle(arc);
expect(arc._model.backgroundColor).toBe('rgb(77, 79, 81)');
expect(arc._model.borderColor).toBe('rgb(123, 125, 127)');
expect(arc._model.borderWidth).toBe(2.1);
// Custom style
arc.custom = {
borderWidth: 5.5,
backgroundColor: 'rgb(0, 0, 0)',
borderColor: 'rgb(10, 10, 10)'
};
meta.controller.removeHoverStyle(arc);
expect(arc._model.backgroundColor).toBe('rgb(0, 0, 0)');
expect(arc._model.borderColor).toBe('rgb(10, 10, 10)');
expect(arc._model.borderWidth).toBe(5.5);
});
});

View File

@@ -0,0 +1,465 @@
// Test the polar area controller
describe('Radar controller tests', function() {
beforeEach(function() {
window.addDefaultMatchers(jasmine);
});
afterEach(function() {
window.releaseAllCharts();
});
it('Should be constructed', function() {
var chart = window.acquireChart({
type: 'radar',
data: {
datasets: [{
data: []
}],
labels: []
}
});
var meta = chart.getDatasetMeta(0);
expect(meta.type).toBe('radar');
expect(meta.controller).not.toBe(undefined);
expect(meta.controller.index).toBe(0);
expect(meta.data).toEqual([]);
meta.controller.updateIndex(1);
expect(meta.controller.index).toBe(1);
});
it('Should create arc elements for each data item during initialization', function() {
var chart = window.acquireChart({
type: 'radar',
data: {
datasets: [{
data: [10, 15, 0, 4]
}],
labels: ['label1', 'label2', 'label3', 'label4']
}
});
var controller = new Chart.controllers.radar(chart, 0);
var meta = chart.getDatasetMeta(0);
expect(meta.dataset instanceof Chart.elements.Line).toBe(true); // line element
expect(meta.data.length).toBe(4); // 4 points created
expect(meta.data[0] instanceof Chart.elements.Point).toBe(true);
expect(meta.data[1] instanceof Chart.elements.Point).toBe(true);
expect(meta.data[2] instanceof Chart.elements.Point).toBe(true);
expect(meta.data[3] instanceof Chart.elements.Point).toBe(true);
});
it('should draw all elements', function() {
var chart = window.acquireChart({
type: 'radar',
data: {
datasets: [{
data: [10, 15, 0, 4]
}],
labels: ['label1', 'label2', 'label3', 'label4']
}
});
var meta = chart.getDatasetMeta(0);
spyOn(meta.dataset, 'draw');
spyOn(meta.data[0], 'draw');
spyOn(meta.data[1], 'draw');
spyOn(meta.data[2], 'draw');
spyOn(meta.data[3], 'draw');
chart.update();
expect(meta.dataset.draw.calls.count()).toBe(1);
expect(meta.data[0].draw.calls.count()).toBe(1);
expect(meta.data[1].draw.calls.count()).toBe(1);
expect(meta.data[2].draw.calls.count()).toBe(1);
expect(meta.data[3].draw.calls.count()).toBe(1);
});
it('should update elements', function() {
var chart = window.acquireChart({
type: 'radar',
data: {
datasets: [{
data: [10, 15, 0, 4]
}],
labels: ['label1', 'label2', 'label3', 'label4']
},
options: {
showLines: true,
elements: {
line: {
backgroundColor: 'rgb(255, 0, 0)',
borderCapStyle: 'round',
borderColor: 'rgb(0, 255, 0)',
borderDash: [],
borderDashOffset: 0.1,
borderJoinStyle: 'bevel',
borderWidth: 1.2,
fill: true,
tension: 0.1,
},
point: {
backgroundColor: Chart.defaults.global.defaultColor,
borderWidth: 1,
borderColor: Chart.defaults.global.defaultColor,
hitRadius: 1,
hoverRadius: 4,
hoverBorderWidth: 1,
radius: 3,
pointStyle: 'circle'
}
}
}
});
var meta = chart.getDatasetMeta(0);
meta.controller.reset(); // reset first
// Line element
expect(meta.dataset._model.scaleTop).toBeCloseToPixel(32);
expect(meta.dataset._model.scaleBottom).toBeCloseToPixel(512);
expect(meta.dataset._model.scaleZero.x).toBeCloseToPixel(256);
expect(meta.dataset._model.scaleZero.y).toBeCloseToPixel(272);
expect(meta.dataset._model).toEqual(jasmine.objectContaining({
backgroundColor: 'rgb(255, 0, 0)',
borderCapStyle: 'round',
borderColor: 'rgb(0, 255, 0)',
borderDash: [],
borderDashOffset: 0.1,
borderJoinStyle: 'bevel',
borderWidth: 1.2,
fill: true,
tension: 0.1,
}));
[
{ x: 256, y: 272, cppx: 256, cppy: 272, cpnx: 256, cpny: 272},
{ x: 256, y: 272, cppx: 256, cppy: 272, cpnx: 256, cpny: 272},
{ x: 256, y: 272, cppx: 256, cppy: 272, cpnx: 256, cpny: 272},
{ x: 256, y: 272, cppx: 256, cppy: 272, cpnx: 256, cpny: 272},
].forEach(function(expected, i) {
expect(meta.data[i]._model.x).toBeCloseToPixel(expected.x);
expect(meta.data[i]._model.y).toBeCloseToPixel(expected.y);
expect(meta.data[i]._model.controlPointPreviousX).toBeCloseToPixel(expected.cppx);
expect(meta.data[i]._model.controlPointPreviousY).toBeCloseToPixel(expected.cppy);
expect(meta.data[i]._model.controlPointNextX).toBeCloseToPixel(expected.cpnx);
expect(meta.data[i]._model.controlPointNextY).toBeCloseToPixel(expected.cpny);
expect(meta.data[i]._model).toEqual(jasmine.objectContaining({
backgroundColor: Chart.defaults.global.defaultColor,
borderWidth: 1,
borderColor: Chart.defaults.global.defaultColor,
hitRadius: 1,
radius: 3,
pointStyle: 'circle',
skip: false,
tension: 0.1,
}));
});
// Now update controller and ensure proper updates
meta.controller.update();
[
{ x: 256, y: 133, cppx: 246, cppy: 133, cpnx: 272, cpny: 133 },
{ x: 464, y: 272, cppx: 464, cppy: 264, cpnx: 464, cpny: 278 },
{ x: 256, y: 272, cppx: 276.9, cppy: 272, cpnx: 250.4, cpny: 272 },
{ x: 200, y: 272, cppx: 200, cppy: 275, cpnx: 200, cpny: 261 },
].forEach(function(expected, i) {
expect(meta.data[i]._model.x).toBeCloseToPixel(expected.x);
expect(meta.data[i]._model.y).toBeCloseToPixel(expected.y);
expect(meta.data[i]._model.controlPointPreviousX).toBeCloseToPixel(expected.cppx);
expect(meta.data[i]._model.controlPointPreviousY).toBeCloseToPixel(expected.cppy);
expect(meta.data[i]._model.controlPointNextX).toBeCloseToPixel(expected.cpnx);
expect(meta.data[i]._model.controlPointNextY).toBeCloseToPixel(expected.cpny);
expect(meta.data[i]._model).toEqual(jasmine.objectContaining({
backgroundColor: Chart.defaults.global.defaultColor,
borderWidth: 1,
borderColor: Chart.defaults.global.defaultColor,
hitRadius: 1,
radius: 3,
pointStyle: 'circle',
skip: false,
tension: 0.1,
}));
});
// Use dataset level styles for lines & points
chart.data.datasets[0].tension = 0;
chart.data.datasets[0].backgroundColor = 'rgb(98, 98, 98)';
chart.data.datasets[0].borderColor = 'rgb(8, 8, 8)';
chart.data.datasets[0].borderWidth = 0.55;
chart.data.datasets[0].borderCapStyle = 'butt';
chart.data.datasets[0].borderDash = [2, 3];
chart.data.datasets[0].borderDashOffset = 7;
chart.data.datasets[0].borderJoinStyle = 'miter';
chart.data.datasets[0].fill = false;
// point styles
chart.data.datasets[0].pointRadius = 22;
chart.data.datasets[0].hitRadius = 3.3;
chart.data.datasets[0].pointBackgroundColor = 'rgb(128, 129, 130)';
chart.data.datasets[0].pointBorderColor = 'rgb(56, 57, 58)';
chart.data.datasets[0].pointBorderWidth = 1.123;
meta.controller.update();
expect(meta.dataset._model.scaleTop).toBeCloseToPixel(32);
expect(meta.dataset._model.scaleBottom).toBeCloseToPixel(512);
expect(meta.dataset._model.scaleZero.x).toBeCloseToPixel(256);
expect(meta.dataset._model.scaleZero.y).toBeCloseToPixel(272);
expect(meta.dataset._model).toEqual(jasmine.objectContaining({
backgroundColor: 'rgb(98, 98, 98)',
borderCapStyle: 'butt',
borderColor: 'rgb(8, 8, 8)',
borderDash: [2, 3],
borderDashOffset: 7,
borderJoinStyle: 'miter',
borderWidth: 0.55,
fill: false,
tension: 0,
}));
// Since tension is now 0, we don't care about the control points
[
{ x: 256, y: 133 },
{ x: 464, y: 272 },
{ x: 256, y: 272 },
{ x: 200, y: 272 },
].forEach(function(expected, i) {
expect(meta.data[i]._model.x).toBeCloseToPixel(expected.x);
expect(meta.data[i]._model.y).toBeCloseToPixel(expected.y);
expect(meta.data[i]._model).toEqual(jasmine.objectContaining({
backgroundColor: 'rgb(128, 129, 130)',
borderWidth: 1.123,
borderColor: 'rgb(56, 57, 58)',
hitRadius: 3.3,
radius: 22,
pointStyle: 'circle',
skip: false,
tension: 0,
}));
});
// Use custom styles for lines & first point
meta.dataset.custom = {
tension: 0.25,
backgroundColor: 'rgb(55, 55, 54)',
borderColor: 'rgb(8, 7, 6)',
borderWidth: 0.3,
borderCapStyle: 'square',
borderDash: [4, 3],
borderDashOffset: 4.4,
borderJoinStyle: 'round',
fill: true,
};
// point styles
meta.data[0].custom = {
radius: 2.2,
backgroundColor: 'rgb(0, 1, 3)',
borderColor: 'rgb(4, 6, 8)',
borderWidth: 0.787,
tension: 0.15,
skip: true,
hitRadius: 5,
};
meta.controller.update();
expect(meta.dataset._model.scaleTop).toBeCloseToPixel(32);
expect(meta.dataset._model.scaleBottom).toBeCloseToPixel(512);
expect(meta.dataset._model.scaleZero.x).toBeCloseToPixel(256);
expect(meta.dataset._model.scaleZero.y).toBeCloseToPixel(272);
expect(meta.dataset._model).toEqual(jasmine.objectContaining({
backgroundColor: 'rgb(55, 55, 54)',
borderCapStyle: 'square',
borderColor: 'rgb(8, 7, 6)',
borderDash: [4, 3],
borderDashOffset: 4.4,
borderJoinStyle: 'round',
borderWidth: 0.3,
fill: true,
tension: 0.25,
}));
expect(meta.data[0]._model.x).toBeCloseToPixel(256);
expect(meta.data[0]._model.y).toBeCloseToPixel(133);
expect(meta.data[0]._model.controlPointPreviousX).toBeCloseToPixel(241);
expect(meta.data[0]._model.controlPointPreviousY).toBeCloseToPixel(133);
expect(meta.data[0]._model.controlPointNextX).toBeCloseToPixel(281);
expect(meta.data[0]._model.controlPointNextY).toBeCloseToPixel(133);
expect(meta.data[0]._model).toEqual(jasmine.objectContaining({
radius: 2.2,
backgroundColor: 'rgb(0, 1, 3)',
borderColor: 'rgb(4, 6, 8)',
borderWidth: 0.787,
tension: 0.15,
skip: true,
hitRadius: 5,
}));
});
it('should set point hover styles', function() {
var chart = window.acquireChart({
type: 'radar',
data: {
datasets: [{
data: [10, 15, 0, 4]
}],
labels: ['label1', 'label2', 'label3', 'label4']
},
options: {
showLines: true,
elements: {
line: {
backgroundColor: 'rgb(255, 0, 0)',
borderCapStyle: 'round',
borderColor: 'rgb(0, 255, 0)',
borderDash: [],
borderDashOffset: 0.1,
borderJoinStyle: 'bevel',
borderWidth: 1.2,
fill: true,
skipNull: true,
tension: 0.1,
},
point: {
backgroundColor: 'rgb(255, 255, 0)',
borderWidth: 1,
borderColor: 'rgb(255, 255, 255)',
hitRadius: 1,
hoverRadius: 4,
hoverBorderWidth: 1,
radius: 3,
}
}
}
});
var meta = chart.getDatasetMeta(0);
meta.controller.update(); // reset first
var point = meta.data[0];
meta.controller.setHoverStyle(point);
expect(point._model.backgroundColor).toBe('rgb(229, 230, 0)');
expect(point._model.borderColor).toBe('rgb(230, 230, 230)');
expect(point._model.borderWidth).toBe(1);
expect(point._model.radius).toBe(4);
// Can set hover style per dataset
chart.data.datasets[0].pointHoverRadius = 3.3;
chart.data.datasets[0].pointHoverBackgroundColor = 'rgb(77, 79, 81)';
chart.data.datasets[0].pointHoverBorderColor = 'rgb(123, 125, 127)';
chart.data.datasets[0].pointHoverBorderWidth = 2.1;
meta.controller.setHoverStyle(point);
expect(point._model.backgroundColor).toBe('rgb(77, 79, 81)');
expect(point._model.borderColor).toBe('rgb(123, 125, 127)');
expect(point._model.borderWidth).toBe(2.1);
expect(point._model.radius).toBe(3.3);
// Custom style
point.custom = {
hoverRadius: 4.4,
hoverBorderWidth: 5.5,
hoverBackgroundColor: 'rgb(0, 0, 0)',
hoverBorderColor: 'rgb(10, 10, 10)'
};
meta.controller.setHoverStyle(point);
expect(point._model.backgroundColor).toBe('rgb(0, 0, 0)');
expect(point._model.borderColor).toBe('rgb(10, 10, 10)');
expect(point._model.borderWidth).toBe(5.5);
expect(point._model.radius).toBe(4.4);
});
it('should remove hover styles', function() {
var chart = window.acquireChart({
type: 'radar',
data: {
datasets: [{
data: [10, 15, 0, 4]
}],
labels: ['label1', 'label2', 'label3', 'label4']
},
options: {
showLines: true,
elements: {
line: {
backgroundColor: 'rgb(255, 0, 0)',
borderCapStyle: 'round',
borderColor: 'rgb(0, 255, 0)',
borderDash: [],
borderDashOffset: 0.1,
borderJoinStyle: 'bevel',
borderWidth: 1.2,
fill: true,
skipNull: true,
tension: 0.1,
},
point: {
backgroundColor: 'rgb(255, 255, 0)',
borderWidth: 1,
borderColor: 'rgb(255, 255, 255)',
hitRadius: 1,
hoverRadius: 4,
hoverBorderWidth: 1,
radius: 3,
}
}
}
});
var meta = chart.getDatasetMeta(0);
meta.controller.update(); // reset first
var point = meta.data[0];
chart.options.elements.point.backgroundColor = 'rgb(45, 46, 47)';
chart.options.elements.point.borderColor = 'rgb(50, 51, 52)';
chart.options.elements.point.borderWidth = 10.1;
chart.options.elements.point.radius = 1.01;
meta.controller.removeHoverStyle(point);
expect(point._model.backgroundColor).toBe('rgb(45, 46, 47)');
expect(point._model.borderColor).toBe('rgb(50, 51, 52)');
expect(point._model.borderWidth).toBe(10.1);
expect(point._model.radius).toBe(1.01);
// Can set hover style per dataset
chart.data.datasets[0].radius = 3.3;
chart.data.datasets[0].pointBackgroundColor = 'rgb(77, 79, 81)';
chart.data.datasets[0].pointBorderColor = 'rgb(123, 125, 127)';
chart.data.datasets[0].pointBorderWidth = 2.1;
meta.controller.removeHoverStyle(point);
expect(point._model.backgroundColor).toBe('rgb(77, 79, 81)');
expect(point._model.borderColor).toBe('rgb(123, 125, 127)');
expect(point._model.borderWidth).toBe(2.1);
expect(point._model.radius).toBe(3.3);
// Custom style
point.custom = {
radius: 4.4,
borderWidth: 5.5,
backgroundColor: 'rgb(0, 0, 0)',
borderColor: 'rgb(10, 10, 10)'
};
meta.controller.removeHoverStyle(point);
expect(point._model.backgroundColor).toBe('rgb(0, 0, 0)');
expect(point._model.borderColor).toBe('rgb(10, 10, 10)');
expect(point._model.borderWidth).toBe(5.5);
expect(point._model.radius).toBe(4.4);
});
});

View File

@@ -0,0 +1,45 @@
// Test the core element functionality
describe('Core element tests', function() {
it ('should transition model properties', function() {
var element = new Chart.Element({
_model: {
numberProp: 0,
numberProp2: 100,
_underscoreProp: 0,
stringProp: 'abc',
objectProp: {
myObject: true
},
colorProp: 'rgb(0, 0, 0)'
}
});
// First transition clones model into view
element.transition(0.25);
expect(element._view).toEqual(element._model);
expect(element._start).toEqual(element._model); // also cloned
expect(element._view.objectProp).toBe(element._model.objectProp); // not cloned
expect(element._start.objectProp).toEqual(element._model.objectProp); // not cloned
element._model.numberProp = 100;
element._model.numberProp2 = 250;
element._model._underscoreProp = 200;
element._model.stringProp = 'def'
element._model.newStringProp = 'newString';
element._model.colorProp = 'rgb(255, 255, 0)'
element.transition(0.25);
expect(element._view).toEqual({
numberProp: 25,
numberProp2: 137.5,
_underscoreProp: 0, // underscore props are not transition to a new value
stringProp: 'def',
newStringProp: 'newString',
objectProp: {
myObject: true
},
colorProp: 'rgb(64, 64, 0)',
});
});
});

View File

@@ -0,0 +1,718 @@
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,
},
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,
},
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 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 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);
});
});
});

View File

@@ -0,0 +1,244 @@
// Tests of the scale service
describe('Test the layout service', function() {
beforeEach(function() {
window.addDefaultMatchers(jasmine);
});
afterEach(function() {
window.releaseAllCharts();
});
it('should fit a simple chart with 2 scales', function() {
var chart = window.acquireChart({
type: 'bar',
data: {
datasets: [
{ data: [10, 5, 0, 25, 78, -10] }
],
labels: ['tick1', 'tick2', 'tick3', 'tick4', 'tick5', 'tick6']
},
options: {
scales: {
xAxes: [{
id: 'xScale',
type: 'category'
}],
yAxes: [{
id: 'yScale',
type: 'linear'
}]
}
}
}, {
height: '150px',
width: '250px'
});
expect(chart.chartArea.bottom).toBeCloseToPixel(112);
expect(chart.chartArea.left).toBeCloseToPixel(41);
expect(chart.chartArea.right).toBeCloseToPixel(250);
expect(chart.chartArea.top).toBeCloseToPixel(32);
// Is xScale at the right spot
expect(chart.scales.xScale.bottom).toBeCloseToPixel(150);
expect(chart.scales.xScale.left).toBeCloseToPixel(41);
expect(chart.scales.xScale.right).toBeCloseToPixel(250);
expect(chart.scales.xScale.top).toBeCloseToPixel(112);
expect(chart.scales.xScale.labelRotation).toBeCloseTo(25);
// Is yScale at the right spot
expect(chart.scales.yScale.bottom).toBeCloseToPixel(112);
expect(chart.scales.yScale.left).toBeCloseToPixel(0);
expect(chart.scales.yScale.right).toBeCloseToPixel(41);
expect(chart.scales.yScale.top).toBeCloseToPixel(32);
expect(chart.scales.yScale.labelRotation).toBeCloseTo(0);
});
it('should fit scales that are in the top and right positions', function() {
var chart = window.acquireChart({
type: 'bar',
data: {
datasets: [
{ data: [10, 5, 0, 25, 78, -10] }
],
labels: ['tick1', 'tick2', 'tick3', 'tick4', 'tick5', 'tick6']
},
options: {
scales: {
xAxes: [{
id: 'xScale',
type: 'category',
position: 'top'
}],
yAxes: [{
id: 'yScale',
type: 'linear',
position: 'right'
}]
}
}
}, {
height: '150px',
width: '250px'
});
expect(chart.chartArea.bottom).toBeCloseToPixel(150);
expect(chart.chartArea.left).toBeCloseToPixel(0);
expect(chart.chartArea.right).toBeCloseToPixel(209);
expect(chart.chartArea.top).toBeCloseToPixel(71);
// Is xScale at the right spot
expect(chart.scales.xScale.bottom).toBeCloseToPixel(71);
expect(chart.scales.xScale.left).toBeCloseToPixel(0);
expect(chart.scales.xScale.right).toBeCloseToPixel(209);
expect(chart.scales.xScale.top).toBeCloseToPixel(32);
expect(chart.scales.xScale.labelRotation).toBeCloseTo(25);
// Is yScale at the right spot
expect(chart.scales.yScale.bottom).toBeCloseToPixel(150);
expect(chart.scales.yScale.left).toBeCloseToPixel(209);
expect(chart.scales.yScale.right).toBeCloseToPixel(250);
expect(chart.scales.yScale.top).toBeCloseToPixel(71);
expect(chart.scales.yScale.labelRotation).toBeCloseTo(0);
});
it('should fit scales that overlap the chart area', function() {
var chart = window.acquireChart({
type: 'radar',
data: {
datasets: [{
data: [10, 5, 0, 25, 78, -10]
}, {
data: [-19, -20, 0, -99, -50, 0]
}],
labels: ['tick1', 'tick2', 'tick3', 'tick4', 'tick5', 'tick6']
}
});
expect(chart.chartArea.bottom).toBeCloseToPixel(512);
expect(chart.chartArea.left).toBeCloseToPixel(0);
expect(chart.chartArea.right).toBeCloseToPixel(512);
expect(chart.chartArea.top).toBeCloseToPixel(32);
expect(chart.scale.bottom).toBeCloseToPixel(512);
expect(chart.scale.left).toBeCloseToPixel(0);
expect(chart.scale.right).toBeCloseToPixel(512);
expect(chart.scale.top).toBeCloseToPixel(32);
expect(chart.scale.width).toBeCloseToPixel(512);
expect(chart.scale.height).toBeCloseToPixel(480)
});
it('should fit multiple axes in the same position', function() {
var chart = window.acquireChart({
type: 'bar',
data: {
datasets: [{
yAxisID: 'yScale1',
data: [10, 5, 0, 25, 78, -10]
}, {
yAxisID: 'yScale2',
data: [-19, -20, 0, -99, -50, 0]
}],
labels: ['tick1', 'tick2', 'tick3', 'tick4', 'tick5', 'tick6']
},
options: {
scales: {
xAxes: [{
id: 'xScale',
type: 'category'
}],
yAxes: [{
id: 'yScale1',
type: 'linear'
}, {
id: 'yScale2',
type: 'linear'
}]
}
}
}, {
height: '150px',
width: '250px'
});
expect(chart.chartArea.bottom).toBeCloseToPixel(102);
expect(chart.chartArea.left).toBeCloseToPixel(86);
expect(chart.chartArea.right).toBeCloseToPixel(250);
expect(chart.chartArea.top).toBeCloseToPixel(32);
// Is xScale at the right spot
expect(chart.scales.xScale.bottom).toBeCloseToPixel(150);
expect(chart.scales.xScale.left).toBeCloseToPixel(86);
expect(chart.scales.xScale.right).toBeCloseToPixel(250);
expect(chart.scales.xScale.top).toBeCloseToPixel(103);
expect(chart.scales.xScale.labelRotation).toBeCloseTo(50);
// Are yScales at the right spot
expect(chart.scales.yScale1.bottom).toBeCloseToPixel(102);
expect(chart.scales.yScale1.left).toBeCloseToPixel(0);
expect(chart.scales.yScale1.right).toBeCloseToPixel(41);
expect(chart.scales.yScale1.top).toBeCloseToPixel(32);
expect(chart.scales.yScale1.labelRotation).toBeCloseTo(0);
expect(chart.scales.yScale2.bottom).toBeCloseToPixel(102);
expect(chart.scales.yScale2.left).toBeCloseToPixel(41);
expect(chart.scales.yScale2.right).toBeCloseToPixel(86);
expect(chart.scales.yScale2.top).toBeCloseToPixel(32);
expect(chart.scales.yScale2.labelRotation).toBeCloseTo(0);
});
it ('should fix a full width box correctly', function() {
var chart = window.acquireChart({
type: 'bar',
data: {
datasets: [{
xAxisID: 'xScale1',
data: [10, 5, 0, 25, 78, -10]
}, {
xAxisID: 'xScale2',
data: [-19, -20, 0, -99, -50, 0]
}],
labels: ['tick1', 'tick2', 'tick3', 'tick4', 'tick5', 'tick6']
},
options: {
scales: {
xAxes: [{
id: 'xScale1',
type: 'category'
}, {
id: 'xScale2',
type: 'category',
position: 'top',
fullWidth: true
}],
yAxes: [{
id: 'yScale',
type: 'linear'
}]
}
}
});
expect(chart.chartArea.bottom).toBeCloseToPixel(484);
expect(chart.chartArea.left).toBeCloseToPixel(45);
expect(chart.chartArea.right).toBeCloseToPixel(512);
expect(chart.chartArea.top).toBeCloseToPixel(60);
// Are xScales at the right spot
expect(chart.scales.xScale1.bottom).toBeCloseToPixel(512);
expect(chart.scales.xScale1.left).toBeCloseToPixel(45);
expect(chart.scales.xScale1.right).toBeCloseToPixel(512);
expect(chart.scales.xScale1.top).toBeCloseToPixel(484);
expect(chart.scales.xScale2.bottom).toBeCloseToPixel(28);
expect(chart.scales.xScale2.left).toBeCloseToPixel(0);
expect(chart.scales.xScale2.right).toBeCloseToPixel(512);
expect(chart.scales.xScale2.top).toBeCloseToPixel(0);
// Is yScale at the right spot
expect(chart.scales.yScale.bottom).toBeCloseToPixel(484);
expect(chart.scales.yScale.left).toBeCloseToPixel(0);
expect(chart.scales.yScale.right).toBeCloseToPixel(45);
expect(chart.scales.yScale.top).toBeCloseToPixel(60);
});
});

View File

@@ -0,0 +1,308 @@
// Test the rectangle element
describe('Legend block tests', function() {
beforeEach(function() {
window.addDefaultMatchers(jasmine);
});
afterEach(function() {
window.releaseAllCharts();
});
it('Should be constructed', function() {
var legend = new Chart.Legend({});
expect(legend).not.toBe(undefined);
});
it('should have the correct default config', function() {
expect(Chart.defaults.global.legend).toEqual({
display: true,
position: 'top',
fullWidth: true, // marks that this box should take the full width of the canvas (pushing down other boxes)
reverse: false,
// a callback that will handle
onClick: jasmine.any(Function),
labels: {
boxWidth: 40,
padding: 10,
generateLabels: jasmine.any(Function)
}
});
});
it('should update correctly', function() {
var chart = window.acquireChart({
type: 'bar',
data: {
datasets: [{
label: 'dataset1',
backgroundColor: '#f31',
borderCapStyle: 'butt',
borderDash: [2, 2],
borderDashOffset: 5.5,
data: []
}, {
label: 'dataset2',
hidden: true,
borderJoinStyle: 'miter',
data: []
}, {
label: 'dataset3',
borderWidth: 10,
borderColor: 'green',
data: []
}],
labels: []
}
});
expect(chart.legend.legendItems).toEqual([{
text: 'dataset1',
fillStyle: '#f31',
hidden: false,
lineCap: 'butt',
lineDash: [2, 2],
lineDashOffset: 5.5,
lineJoin: undefined,
lineWidth: undefined,
strokeStyle: undefined,
datasetIndex: 0
}, {
text: 'dataset2',
fillStyle: undefined,
hidden: true,
lineCap: undefined,
lineDash: undefined,
lineDashOffset: undefined,
lineJoin: 'miter',
lineWidth: undefined,
strokeStyle: undefined,
datasetIndex: 1
}, {
text: 'dataset3',
fillStyle: undefined,
hidden: false,
lineCap: undefined,
lineDash: undefined,
lineDashOffset: undefined,
lineJoin: undefined,
lineWidth: 10,
strokeStyle: 'green',
datasetIndex: 2
}]);
});
it('should draw correctly', function() {
var chart = window.acquireChart({
type: 'bar',
data: {
datasets: [{
label: 'dataset1',
backgroundColor: '#f31',
borderCapStyle: 'butt',
borderDash: [2, 2],
borderDashOffset: 5.5,
data: []
}, {
label: 'dataset2',
hidden: true,
borderJoinStyle: 'miter',
data: []
}, {
label: 'dataset3',
borderWidth: 10,
borderColor: 'green',
data: []
}],
labels: []
}
});
expect(chart.legend.legendHitBoxes.length).toBe(3);
[ { h: 12, l: 101, t: 10, w: 93 },
{ h: 12, l: 205, t: 10, w: 93 },
{ h: 12, l: 308, t: 10, w: 93 }
].forEach(function(expected, i) {
expect(chart.legend.legendHitBoxes[i].height).toBeCloseToPixel(expected.h);
expect(chart.legend.legendHitBoxes[i].left).toBeCloseToPixel(expected.l);
expect(chart.legend.legendHitBoxes[i].top).toBeCloseToPixel(expected.t);
expect(chart.legend.legendHitBoxes[i].width).toBeCloseToPixel(expected.w);
})
// NOTE(SB) We should get ride of the following tests and use image diff instead.
// For now, as discussed with Evert Timberg, simply comment out.
// See http://humblesoftware.github.io/js-imagediff/test.html
/*chart.legend.ctx = window.createMockContext();
chart.update();
expect(chart.legend.ctx .getCalls()).toEqual([{
"name": "measureText",
"args": ["dataset1"]
}, {
"name": "measureText",
"args": ["dataset2"]
}, {
"name": "measureText",
"args": ["dataset3"]
}, {
"name": "measureText",
"args": ["dataset1"]
}, {
"name": "measureText",
"args": ["dataset2"]
}, {
"name": "measureText",
"args": ["dataset3"]
}, {
"name": "setLineWidth",
"args": [0.5]
}, {
"name": "setStrokeStyle",
"args": ["#666"]
}, {
"name": "setFillStyle",
"args": ["#666"]
}, {
"name": "measureText",
"args": ["dataset1"]
}, {
"name": "save",
"args": []
}, {
"name": "setFillStyle",
"args": ["#f31"]
}, {
"name": "setLineCap",
"args": ["butt"]
}, {
"name": "setLineDashOffset",
"args": [5.5]
}, {
"name": "setLineJoin",
"args": ["miter"]
}, {
"name": "setLineWidth",
"args": [3]
}, {
"name": "setStrokeStyle",
"args": ["rgba(0,0,0,0.1)"]
}, {
"name": "setLineDash",
"args": [
[2, 2]
]
}, {
"name": "strokeRect",
"args": [114, 110, 40, 12]
}, {
"name": "fillRect",
"args": [114, 110, 40, 12]
}, {
"name": "restore",
"args": []
}, {
"name": "fillText",
"args": ["dataset1", 160, 110]
}, {
"name": "measureText",
"args": ["dataset2"]
}, {
"name": "save",
"args": []
}, {
"name": "setFillStyle",
"args": ["rgba(0,0,0,0.1)"]
}, {
"name": "setLineCap",
"args": ["butt"]
}, {
"name": "setLineDashOffset",
"args": [0]
}, {
"name": "setLineJoin",
"args": ["miter"]
}, {
"name": "setLineWidth",
"args": [3]
}, {
"name": "setStrokeStyle",
"args": ["rgba(0,0,0,0.1)"]
}, {
"name": "setLineDash",
"args": [
[]
]
}, {
"name": "strokeRect",
"args": [250, 110, 40, 12]
}, {
"name": "fillRect",
"args": [250, 110, 40, 12]
}, {
"name": "restore",
"args": []
}, {
"name": "fillText",
"args": ["dataset2", 296, 110]
}, {
"name": "beginPath",
"args": []
}, {
"name": "setLineWidth",
"args": [2]
}, {
"name": "moveTo",
"args": [296, 116]
}, {
"name": "lineTo",
"args": [376, 116]
}, {
"name": "stroke",
"args": []
}, {
"name": "measureText",
"args": ["dataset3"]
}, {
"name": "save",
"args": []
}, {
"name": "setFillStyle",
"args": ["rgba(0,0,0,0.1)"]
}, {
"name": "setLineCap",
"args": ["butt"]
}, {
"name": "setLineDashOffset",
"args": [0]
}, {
"name": "setLineJoin",
"args": ["miter"]
}, {
"name": "setLineWidth",
"args": [10]
}, {
"name": "setStrokeStyle",
"args": ["green"]
}, {
"name": "setLineDash",
"args": [
[]
]
}, {
"name": "strokeRect",
"args": [182, 132, 40, 12]
}, {
"name": "fillRect",
"args": [182, 132, 40, 12]
}, {
"name": "restore",
"args": []
}, {
"name": "fillText",
"args": ["dataset3", 228, 132]
}]);*/
});
});

View File

@@ -0,0 +1,43 @@
// Plugin tests
describe('Test the plugin system', function() {
beforeEach(function() {
Chart.plugins = [];
});
it ('Should register plugins', function() {
var myplugin = {};
Chart.pluginService.register(myplugin);
expect(Chart.plugins.length).toBe(1);
// Should only add plugin once
Chart.pluginService.register(myplugin);
expect(Chart.plugins.length).toBe(1);
});
it ('Should allow unregistering plugins', function() {
var myplugin = {};
Chart.pluginService.register(myplugin);
expect(Chart.plugins.length).toBe(1);
// Should only add plugin once
Chart.pluginService.remove(myplugin);
expect(Chart.plugins.length).toBe(0);
// Removing a plugin that doesn't exist should not error
Chart.pluginService.remove(myplugin);
});
it ('Should allow notifying plugins', function() {
var myplugin = {
count: 0,
trigger: function(chart) {
myplugin.count = chart.count;
}
};
Chart.pluginService.register(myplugin);
Chart.pluginService.notifyPlugins('trigger', [{ count: 10 }]);
expect(myplugin.count).toBe(10);
});
});

View File

@@ -0,0 +1,29 @@
// Tests of the scale service
describe('Test the scale service', function() {
it('should update scale defaults', function() {
var defaults = {
testProp: true
};
var type = 'my_test_type';
var Constructor = function() {
this.initialized = true;
};
Chart.scaleService.registerScaleType(type, Constructor, defaults);
// Should equal defaults but not be an identical object
expect(Chart.scaleService.getScaleDefaults(type)).toEqual(jasmine.objectContaining({
testProp: true
}));
Chart.scaleService.updateScaleDefaults(type, {
testProp: 'red',
newProp: 42
});
expect(Chart.scaleService.getScaleDefaults(type)).toEqual(jasmine.objectContaining({
testProp: 'red',
newProp: 42
}));
});
});

View File

@@ -0,0 +1,210 @@
// Test the rectangle element
describe('Title block tests', function() {
it('Should be constructed', function() {
var title = new Chart.Title({});
expect(title).not.toBe(undefined);
});
it('Should have the correct default config', function() {
expect(Chart.defaults.global.title).toEqual({
display: false,
position: 'top',
fullWidth: true,
fontStyle: 'bold',
padding: 10,
text: ''
})
});
it('should update correctly', function() {
var chart = {};
var options = Chart.helpers.clone(Chart.defaults.global.title);
options.text = "My title";
var title = new Chart.Title({
chart: chart,
options: options
});
var minSize = title.update(400, 200);
expect(minSize).toEqual({
width: 400,
height: 0
});
// Now we have a height since we display
title.options.display = true;
minSize = title.update(400, 200);
expect(minSize).toEqual({
width: 400,
height: 32
});
});
it('should update correctly when vertical', function() {
var chart = {};
var options = Chart.helpers.clone(Chart.defaults.global.title);
options.text = "My title";
options.position = 'left';
var title = new Chart.Title({
chart: chart,
options: options
});
var minSize = title.update(200, 400);
expect(minSize).toEqual({
width: 0,
height: 400
});
// Now we have a height since we display
title.options.display = true;
minSize = title.update(200, 400);
expect(minSize).toEqual({
width: 32,
height: 400
});
});
it('should draw correctly horizontally', function() {
var chart = {};
var context = window.createMockContext();
var options = Chart.helpers.clone(Chart.defaults.global.title);
options.text = "My title";
var title = new Chart.Title({
chart: chart,
options: options,
ctx: context
});
title.update(400, 200);
title.draw();
expect(context.getCalls()).toEqual([]);
// Now we have a height since we display
title.options.display = true;
var minSize = title.update(400, 200);
title.top = 50;
title.left = 100;
title.bottom = title.top + minSize.height;
title.right = title.left + minSize.width;
title.draw();
expect(context.getCalls()).toEqual([{
name: 'setFillStyle',
args: ['#666']
}, {
name: 'save',
args: []
}, {
name: 'translate',
args: [300, 66]
}, {
name: 'rotate',
args: [0]
}, {
name: 'fillText',
args: ['My title', 0, 0]
}, {
name: 'restore',
args: []
}]);
});
it ('should draw correctly vertically', function() {
var chart = {};
var context = window.createMockContext();
var options = Chart.helpers.clone(Chart.defaults.global.title);
options.text = "My title";
options.position = 'left';
var title = new Chart.Title({
chart: chart,
options: options,
ctx: context
});
title.update(200, 400);
title.draw();
expect(context.getCalls()).toEqual([]);
// Now we have a height since we display
title.options.display = true;
var minSize = title.update(200, 400);
title.top = 50;
title.left = 100;
title.bottom = title.top + minSize.height;
title.right = title.left + minSize.width;
title.draw();
expect(context.getCalls()).toEqual([{
name: 'setFillStyle',
args: ['#666']
}, {
name: 'save',
args: []
}, {
name: 'translate',
args: [106, 250]
}, {
name: 'rotate',
args: [-0.5 * Math.PI]
}, {
name: 'fillText',
args: ['My title', 0, 0]
}, {
name: 'restore',
args: []
}]);
// Rotation is other way on right side
title.options.position = 'right';
// Reset call tracker
context.resetCalls();
minSize = title.update(200, 400);
title.top = 50;
title.left = 100;
title.bottom = title.top + minSize.height;
title.right = title.left + minSize.width;
title.draw();
expect(context.getCalls()).toEqual([{
name: 'setFillStyle',
args: ['#666']
}, {
name: 'save',
args: []
}, {
name: 'translate',
args: [126, 250]
}, {
name: 'rotate',
args: [0.5 * Math.PI]
}, {
name: 'fillText',
args: ['My title', 0, 0]
}, {
name: 'restore',
args: []
}]);
});
});

View File

@@ -0,0 +1,293 @@
// Test the bubble chart default config
describe("Test the bubble chart default config", function() {
it('should reutrn correct tooltip strings', function() {
var config = Chart.defaults.bubble;
// Title is always blank
expect(config.tooltips.callbacks.title()).toBe('');
// Item label
var data = {
datasets: [{
label: 'My dataset',
data: [{
x: 10,
y: 12,
r: 5
}]
}]
};
var tooltipItem = {
datasetIndex: 0,
index: 0
};
expect(config.tooltips.callbacks.label(tooltipItem, data)).toBe('My dataset: (10, 12, 5)');
});
});
describe('Test the doughnut chart default config', function() {
it('should return correct tooltip strings', function() {
var config = Chart.defaults.doughnut;
// Title is always blank
expect(config.tooltips.callbacks.title()).toBe('');
// Item label
var data = {
labels: ['label1', 'label2', 'label3'],
datasets: [{
data: [10, 20, 30],
}]
};
var tooltipItem = {
datasetIndex: 0,
index: 1
};
expect(config.tooltips.callbacks.label(tooltipItem, data)).toBe('label2: 20');
});
it('should return the correct html legend', function() {
var config = Chart.defaults.doughnut;
var chart = {
id: 'mychart',
data: {
labels: ['label1', 'label2'],
datasets: [{
data: [10, 20],
backgroundColor: ['red', 'green']
}]
}
};
var expectedLegend = '<ul class="mychart-legend"><li><span style="background-color:red"></span>label1</li><li><span style="background-color:green"></span>label2</li></ul>';
expect(config.legendCallback(chart)).toBe(expectedLegend);
});
it('should return correct legend label objects', function() {
var config = Chart.defaults.doughnut;
var data = {
labels: ['label1', 'label2', 'label3'],
datasets: [{
data: [10, 20, NaN],
backgroundColor: ['red', 'green', 'blue'],
metaData: [{}, {}, {}]
}]
};
var expected = [{
text: 'label1',
fillStyle: 'red',
hidden: false,
index: 0,
strokeStyle: '#000',
lineWidth: 2
}, {
text: 'label2',
fillStyle: 'green',
hidden: false,
index: 1,
strokeStyle: '#000',
lineWidth: 2
}, {
text: 'label3',
fillStyle: 'blue',
hidden: true,
index: 2,
strokeStyle: '#000',
lineWidth: 2
}];
var chart = {
data: data,
options: {
elements: {
arc: {
borderWidth: 2,
borderColor: '#000'
}
}
}
};
expect(config.legend.labels.generateLabels.call({ chart: chart }, data)).toEqual(expected);
});
it('should hide the correct arc when a legend item is clicked', function() {
var config = Chart.defaults.doughnut;
var legendItem = {
text: 'label1',
fillStyle: 'red',
hidden: false,
index: 0
};
var chart = {
data: {
labels: ['label1', 'label2', 'label3'],
datasets: [{
data: [10, 20, NaN],
backgroundColor: ['red', 'green', 'blue']
}]
},
update: function() {}
};
spyOn(chart, 'update');
var scope = {
chart: chart
};
config.legend.onClick.call(scope, null, legendItem);
expect(chart.data.datasets[0].metaHiddenData).toEqual([10]);
expect(chart.data.datasets[0].data).toEqual([NaN, 20, NaN]);
expect(chart.update).toHaveBeenCalled();
config.legend.onClick.call(scope, null, legendItem);
expect(chart.data.datasets[0].data).toEqual([10, 20, NaN]);
// Should not toggle index 2 since there was never data for it
legendItem.index = 2;
config.legend.onClick.call(scope, null, legendItem);
expect(chart.data.datasets[0].data).toEqual([10, 20, NaN]);
});
});
describe('Test the polar area chart default config', function() {
it('should return correct tooltip strings', function() {
var config = Chart.defaults.polarArea;
// Title is always blank
expect(config.tooltips.callbacks.title()).toBe('');
// Item label
var data = {
labels: ['label1', 'label2', 'label3'],
datasets: [{
data: [10, 20, 30],
}]
};
var tooltipItem = {
datasetIndex: 0,
index: 1,
yLabel: 20
};
expect(config.tooltips.callbacks.label(tooltipItem, data)).toBe('label2: 20');
});
it('should return the correct html legend', function() {
var config = Chart.defaults.polarArea;
var chart = {
id: 'mychart',
data: {
labels: ['label1', 'label2'],
datasets: [{
data: [10, 20],
backgroundColor: ['red', 'green']
}]
}
};
var expectedLegend = '<ul class="mychart-legend"><li><span style="background-color:red">label1</span></li><li><span style="background-color:green">label2</span></li></ul>';
expect(config.legendCallback(chart)).toBe(expectedLegend);
});
it('should return correct legend label objects', function() {
var config = Chart.defaults.polarArea;
var data = {
labels: ['label1', 'label2', 'label3'],
datasets: [{
data: [10, 20, NaN],
backgroundColor: ['red', 'green', 'blue'],
metaData: [{}, {}, {}]
}]
};
var expected = [{
text: 'label1',
fillStyle: 'red',
hidden: false,
index: 0,
strokeStyle: '#000',
lineWidth: 2
}, {
text: 'label2',
fillStyle: 'green',
hidden: false,
index: 1,
strokeStyle: '#000',
lineWidth: 2
}, {
text: 'label3',
fillStyle: 'blue',
hidden: true,
index: 2,
strokeStyle: '#000',
lineWidth: 2
}];
var chart = {
data: data,
options: {
elements: {
arc: {
borderWidth: 2,
borderColor: '#000'
}
}
}
};
expect(config.legend.labels.generateLabels.call({ chart: chart }, data)).toEqual(expected);
});
it('should hide the correct arc when a legend item is clicked', function() {
var config = Chart.defaults.polarArea;
var legendItem = {
text: 'label1',
fillStyle: 'red',
hidden: false,
index: 0
};
var chart = {
data: {
labels: ['label1', 'label2', 'label3'],
datasets: [{
data: [10, 20, NaN],
backgroundColor: ['red', 'green', 'blue']
}]
},
update: function() {}
};
spyOn(chart, 'update');
var scope = {
chart: chart
};
config.legend.onClick.call(scope, null, legendItem);
expect(chart.data.datasets[0].metaHiddenData).toEqual([10]);
expect(chart.data.datasets[0].data).toEqual([NaN, 20, NaN]);
expect(chart.update).toHaveBeenCalled();
config.legend.onClick.call(scope, null, legendItem);
expect(chart.data.datasets[0].data).toEqual([10, 20, NaN]);
// Should not toggle index 2 since there was never data for it
legendItem.index = 2;
config.legend.onClick.call(scope, null, legendItem);
expect(chart.data.datasets[0].data).toEqual([10, 20, NaN]);
});
});

View 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: []
}]);
});
});

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,465 @@
// 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,
pointStyle: 'circle',
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: 'setStrokeStyle',
args: ['rgba(1, 2, 3, 1)']
}, {
name: 'setLineWidth',
args: [6]
}, {
name: 'setFillStyle',
args: ['rgba(0, 255, 0)']
}, {
name: 'beginPath',
args: []
}, {
name: 'arc',
args: [10, 15, 2, 0, 2 * Math.PI]
}, {
name: 'closePath',
args: [],
}, {
name: 'fill',
args: [],
}, {
name: 'stroke',
args: []
}]);
mockContext.resetCalls();
point._view.pointStyle = 'triangle';
point.draw();
expect(mockContext.getCalls()).toEqual([{
name: 'setStrokeStyle',
args: ['rgba(1, 2, 3, 1)']
}, {
name: 'setLineWidth',
args: [6]
}, {
name: 'setFillStyle',
args: ['rgba(0, 255, 0)']
}, {
name: 'beginPath',
args: []
}, {
name: 'moveTo',
args: [10 - 3 * 2 / Math.sqrt(3) / 2, 15 + 3 * 2 / Math.sqrt(3) * Math.sqrt(3) / 2 / 3]
}, {
name: 'lineTo',
args: [10 + 3 * 2 / Math.sqrt(3) / 2, 15 + 3 * 2 / Math.sqrt(3) * Math.sqrt(3) / 2 / 3],
}, {
name: 'lineTo',
args: [10, 15 - 2 * 3 * 2 / Math.sqrt(3) * Math.sqrt(3) / 2 / 3],
}, {
name: 'closePath',
args: [],
}, {
name: 'fill',
args: [],
}, {
name: 'stroke',
args: []
}]);
mockContext.resetCalls();
point._view.pointStyle = 'rect';
point.draw();
expect(mockContext.getCalls()).toEqual([{
name: 'setStrokeStyle',
args: ['rgba(1, 2, 3, 1)']
}, {
name: 'setLineWidth',
args: [6]
}, {
name: 'setFillStyle',
args: ['rgba(0, 255, 0)']
}, {
name: 'fillRect',
args: [10 - 1 / Math.SQRT2 * 2, 15 - 1 / Math.SQRT2 * 2, 2 / Math.SQRT2 * 2, 2 / Math.SQRT2 * 2]
}, {
name: 'strokeRect',
args: [10 - 1 / Math.SQRT2 * 2, 15 - 1 / Math.SQRT2 * 2, 2 / Math.SQRT2 * 2, 2 / Math.SQRT2 * 2]
}, {
name: 'stroke',
args: []
}]);
mockContext.resetCalls();
point._view.pointStyle = 'rectRot';
point.draw();
expect(mockContext.getCalls()).toEqual([{
name: 'setStrokeStyle',
args: ['rgba(1, 2, 3, 1)']
}, {
name: 'setLineWidth',
args: [6]
}, {
name: 'setFillStyle',
args: ['rgba(0, 255, 0)']
}, {
name: 'translate',
args: [10, 15]
}, {
name: 'rotate',
args: [Math.PI / 4]
}, {
name: 'fillRect',
args: [-1 / Math.SQRT2 * 2, -1 / Math.SQRT2 * 2, 2 / Math.SQRT2 * 2, 2 / Math.SQRT2 * 2],
}, {
name: 'strokeRect',
args: [-1 / Math.SQRT2 * 2, -1 / Math.SQRT2 * 2, 2 / Math.SQRT2 * 2, 2 / Math.SQRT2 * 2],
}, {
name: 'setTransform',
args: [1, 0, 0, 1, 0, 0],
}, {
name: 'stroke',
args: []
}]);
mockContext.resetCalls();
point._view.pointStyle = 'cross';
point.draw();
expect(mockContext.getCalls()).toEqual([{
name: 'setStrokeStyle',
args: ['rgba(1, 2, 3, 1)']
}, {
name: 'setLineWidth',
args: [6]
}, {
name: 'setFillStyle',
args: ['rgba(0, 255, 0)']
}, {
name: 'beginPath',
args: []
}, {
name: 'moveTo',
args: [10, 17]
}, {
name: 'lineTo',
args: [10, 13],
}, {
name: 'moveTo',
args: [8, 15],
}, {
name: 'lineTo',
args: [12, 15],
},{
name: 'closePath',
args: [],
}, {
name: 'stroke',
args: []
}]);
mockContext.resetCalls();
point._view.pointStyle = 'crossRot';
point.draw();
expect(mockContext.getCalls()).toEqual([{
name: 'setStrokeStyle',
args: ['rgba(1, 2, 3, 1)']
}, {
name: 'setLineWidth',
args: [6]
}, {
name: 'setFillStyle',
args: ['rgba(0, 255, 0)']
}, {
name: 'beginPath',
args: []
}, {
name: 'moveTo',
args: [10 - Math.cos(Math.PI / 4) * 2, 15 - Math.sin(Math.PI / 4) * 2]
}, {
name: 'lineTo',
args: [10 + Math.cos(Math.PI / 4) * 2, 15 + Math.sin(Math.PI / 4) * 2],
}, {
name: 'moveTo',
args: [10 - Math.cos(Math.PI / 4) * 2, 15 + Math.sin(Math.PI / 4) * 2],
}, {
name: 'lineTo',
args: [10 + Math.cos(Math.PI / 4) * 2, 15 - Math.sin(Math.PI / 4) * 2],
}, {
name: 'closePath',
args: [],
}, {
name: 'stroke',
args: []
}]);
mockContext.resetCalls();
point._view.pointStyle = 'star';
point.draw();
expect(mockContext.getCalls()).toEqual([{
name: 'setStrokeStyle',
args: ['rgba(1, 2, 3, 1)']
}, {
name: 'setLineWidth',
args: [6]
}, {
name: 'setFillStyle',
args: ['rgba(0, 255, 0)']
}, {
name: 'beginPath',
args: []
}, {
name: 'moveTo',
args: [10, 17]
}, {
name: 'lineTo',
args: [10, 13],
}, {
name: 'moveTo',
args: [8, 15],
}, {
name: 'lineTo',
args: [12, 15],
},{
name: 'moveTo',
args: [10 - Math.cos(Math.PI / 4) * 2, 15 - Math.sin(Math.PI / 4) * 2]
}, {
name: 'lineTo',
args: [10 + Math.cos(Math.PI / 4) * 2, 15 + Math.sin(Math.PI / 4) * 2],
}, {
name: 'moveTo',
args: [10 - Math.cos(Math.PI / 4) * 2, 15 + Math.sin(Math.PI / 4) * 2],
}, {
name: 'lineTo',
args: [10 + Math.cos(Math.PI / 4) * 2, 15 - Math.sin(Math.PI / 4) * 2],
}, {
name: 'closePath',
args: [],
}, {
name: 'stroke',
args: []
}]);
mockContext.resetCalls();
point._view.pointStyle = 'line';
point.draw();
expect(mockContext.getCalls()).toEqual([{
name: 'setStrokeStyle',
args: ['rgba(1, 2, 3, 1)']
}, {
name: 'setLineWidth',
args: [6]
}, {
name: 'setFillStyle',
args: ['rgba(0, 255, 0)']
}, {
name: 'beginPath',
args: []
}, {
name: 'moveTo',
args: [8, 15]
}, {
name: 'lineTo',
args: [12, 15],
}, {
name: 'closePath',
args: [],
}, {
name: 'stroke',
args: []
}]);
mockContext.resetCalls();
point._view.pointStyle = 'dash';
point.draw();
expect(mockContext.getCalls()).toEqual([{
name: 'setStrokeStyle',
args: ['rgba(1, 2, 3, 1)']
}, {
name: 'setLineWidth',
args: [6]
}, {
name: 'setFillStyle',
args: ['rgba(0, 255, 0)']
}, {
name: 'beginPath',
args: []
}, {
name: 'moveTo',
args: [10, 15]
}, {
name: 'lineTo',
args: [12, 15],
}, {
name: 'closePath',
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: 'setStrokeStyle',
args: ['rgba(0,0,0,0.1)']
}, {
name: 'setLineWidth',
args: [1]
}, {
name: 'setFillStyle',
args: ['rgba(0,0,0,0.1)']
}, {
name: 'beginPath',
args: []
}, {
name: 'arc',
args: [10, 15, 2, 0, 2 * Math.PI]
}, {
name: 'closePath',
args: [],
}, {
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([]);
});
});

View File

@@ -0,0 +1,303 @@
// 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: 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.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: [],
}]);
});
function testBorderSkipped (borderSkipped, expectedDrawCalls) {
var mockContext = window.createMockContext();
var rectangle = new Chart.elements.Rectangle({
_chart: { ctx: mockContext }
});
// Attach a view object as if we were the controller
rectangle._view = {
borderSkipped: borderSkipped, // set tested 'borderSkipped' parameter
ctx: mockContext,
base: 0,
width: 4,
x: 10,
y: 15,
};
rectangle.draw();
var drawCalls = rectangle._view.ctx.getCalls().splice(4, 4);
expect(drawCalls).toEqual(expectedDrawCalls);
}
it ('should draw correctly respecting "borderSkipped" == "bottom"', function() {
testBorderSkipped ('bottom', [
{ name: 'moveTo', args: [8, 0] },
{ name: 'lineTo', args: [8, 15] },
{ name: 'lineTo', args: [12, 15] },
{ name: 'lineTo', args: [12, 0] },
]);
});
it ('should draw correctly respecting "borderSkipped" == "left"', function() {
testBorderSkipped ('left', [
{ name: 'moveTo', args: [8, 15] },
{ name: 'lineTo', args: [12, 15] },
{ name: 'lineTo', args: [12, 0] },
{ name: 'lineTo', args: [8, 0] },
]);
});
it ('should draw correctly respecting "borderSkipped" == "top"', function() {
testBorderSkipped ('top', [
{ name: 'moveTo', args: [12, 15] },
{ name: 'lineTo', args: [12, 0] },
{ name: 'lineTo', args: [8, 0] },
{ name: 'lineTo', args: [8, 15] },
]);
});
it ('should draw correctly respecting "borderSkipped" == "right"', function() {
testBorderSkipped ('right', [
{ name: 'moveTo', args: [12, 0] },
{ name: 'lineTo', args: [8, 0] },
{ name: 'lineTo', args: [8, 15] },
{ name: 'lineTo', args: [12, 15] },
]);
});
});

238
vendors/Chart.js/test/mockContext.js vendored Normal file
View File

@@ -0,0 +1,238 @@
(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() {},
clearRect: function() {},
closePath: function() {},
fill: function() {},
fillRect: function() {},
fillText: function() {},
lineTo: function(x, y) {},
measureText: function(text) {
// return the number of characters * fixed size
return text ? { width: text.length * 10 } : {width: 0};
},
moveTo: function(x, y) {},
quadraticCurveTo: function() {},
restore: function() {},
rotate: function() {},
save: function() {},
setLineDash: function() {},
stroke: function() {},
strokeRect: function(x, y, w, h) {},
setTransform: function(a, b, c, d, e, f) {},
translate: function(x, y) {},
};
// attach methods to the class itself
var scope = this;
var addMethod = function(name, method) {
scope[methodName] = function() {
scope.record(name, arguments);
return 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;
}
Context.prototype.resetCalls = function() {
this._calls = [];
};
window.createMockContext = function() {
return new Context();
};
// Custom matcher
function toBeCloseToPixel() {
return {
compare: function(actual, expected) {
var result = false;
if (!isNaN(actual) && !isNaN(expected)) {
var diff = Math.abs(actual - expected);
var A = Math.abs(actual);
var B = Math.abs(expected);
var percentDiff = 0.005; // 0.5% diff
result = (diff <= (A > B ? A : B) * percentDiff) || diff < 2; // 2 pixels is fine
}
return { pass: result };
}
}
};
function toEqualOneOf() {
return {
compare: function(actual, expecteds) {
var result = false;
for (var i = 0, l = expecteds.length; i < l; i++) {
if (actual === expecteds[i]) {
result = true;
break;
}
}
return {
pass: result
};
}
};
}
window.addDefaultMatchers = function(jasmine) {
jasmine.addMatchers({
toBeCloseToPixel: toBeCloseToPixel,
toEqualOneOf: toEqualOneOf
});
}
// Canvas injection helpers
var charts = {};
function acquireChart(config, style) {
var wrapper = document.createElement("div");
var canvas = document.createElement("canvas");
wrapper.className = 'chartjs-wrapper';
style = style || { height: '512px', width: '512px' };
for (var k in style) {
wrapper.style[k] = style[k];
canvas.style[k] = style[k];
}
canvas.height = canvas.style.height && parseInt(canvas.style.height);
canvas.width = canvas.style.width && parseInt(canvas.style.width);
// by default, remove chart animation and auto resize
var options = config.options = config.options || {};
options.animation = options.animation === undefined? false : options.animation;
options.responsive = options.responsive === undefined? false : options.responsive;
options.defaultFontFamily = options.defaultFontFamily || 'Arial';
wrapper.appendChild(canvas);
window.document.body.appendChild(wrapper);
var chart = new Chart(canvas.getContext("2d"), config);
charts[chart.id] = chart;
return chart;
}
function releaseChart(chart) {
chart.chart.canvas.parentNode.remove();
delete charts[chart.id];
delete chart;
}
function releaseAllCharts(scope) {
for (var id in charts) {
var chart = charts[id];
releaseChart(chart);
}
}
function injectCSS(css) {
// http://stackoverflow.com/q/3922139
var head = document.getElementsByTagName('head')[0];
var style = document.createElement('style');
style.setAttribute('type', 'text/css');
if (style.styleSheet) { // IE
style.styleSheet.cssText = css;
} else {
style.appendChild(document.createTextNode(css));
}
head.appendChild(style);
}
window.acquireChart = acquireChart;
window.releaseChart = releaseChart;
window.releaseAllCharts = releaseAllCharts;
// some style initialization to limit differences between browsers across different plateforms.
injectCSS(
'.chartjs-wrapper, .chartjs-wrapper canvas {' +
'border: 0;' +
'margin: 0;' +
'padding: 0;' +
'}' +
'.chartjs-wrapper {' +
'position: absolute' +
'}');
})();

View File

@@ -0,0 +1,360 @@
// Test the category scale
describe('Category scale tests', function() {
it('Should register the constructor with the scale service', function() {
var Constructor = Chart.scaleService.getScaleConstructor('category');
expect(Constructor).not.toBe(undefined);
expect(typeof Constructor).toBe('function');
});
it('Should have the correct default config', function() {
var defaultConfig = Chart.scaleService.getScaleDefaults('category');
expect(defaultConfig).toEqual({
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
},
position: "bottom",
scaleLabel: {
labelString: '',
display: false
},
ticks: {
beginAtZero: false,
minRotation: 0,
maxRotation: 50,
mirror: false,
padding: 10,
reverse: false,
display: true,
callback: defaultConfig.ticks.callback, // make this nicer, then check explicitly below
autoSkip: true,
autoSkipPadding: 0,
labelOffset: 0
}
});
// Is this actually a function
expect(defaultConfig.ticks.callback).toEqual(jasmine.any(Function));
});
it('Should generate ticks from the data labales', function() {
var scaleID = 'myScale';
var mockData = {
datasets: [{
yAxisID: scaleID,
data: [10, 5, 0, 25, 78]
}],
labels: ['tick1', 'tick2', 'tick3', 'tick4', 'tick5']
};
var config = Chart.helpers.clone(Chart.scaleService.getScaleDefaults('category'));
var Constructor = Chart.scaleService.getScaleConstructor('category');
var scale = new Constructor({
ctx: {},
options: config,
chart: {
data: mockData
},
id: scaleID
});
scale.determineDataLimits();
scale.buildTicks();
expect(scale.ticks).toEqual(mockData.labels);
});
it ('should get the correct label for the index', function() {
var scaleID = 'myScale';
var mockData = {
datasets: [{
yAxisID: scaleID,
data: [10, 5, 0, 25, 78]
}],
labels: ['tick1', 'tick2', 'tick3', 'tick4', 'tick5']
};
var config = Chart.helpers.clone(Chart.scaleService.getScaleDefaults('category'));
var Constructor = Chart.scaleService.getScaleConstructor('category');
var scale = new Constructor({
ctx: {},
options: config,
chart: {
data: mockData
},
id: scaleID
});
scale.determineDataLimits();
scale.buildTicks();
expect(scale.getLabelForIndex(1)).toBe('tick2');
});
it ('Should get the correct pixel for a value when horizontal', function() {
var scaleID = 'myScale';
var mockData = {
datasets: [{
yAxisID: scaleID,
data: [10, 5, 0, 25, 78]
}],
labels: ['tick1', 'tick2', 'tick3', 'tick4', 'tick_last']
};
var mockContext = window.createMockContext();
var config = Chart.helpers.clone(Chart.scaleService.getScaleDefaults('category'));
config.gridLines.offsetGridLines = true;
var Constructor = Chart.scaleService.getScaleConstructor('category');
var scale = new Constructor({
ctx: mockContext,
options: config,
chart: {
data: mockData
},
id: scaleID
});
var minSize = scale.update(600, 100);
expect(scale.width).toBe(600);
expect(scale.height).toBe(28);
expect(scale.paddingTop).toBe(0);
expect(scale.paddingBottom).toBe(0);
expect(scale.paddingLeft).toBe(28);
expect(scale.paddingRight).toBe(48);
expect(scale.labelRotation).toBe(0);
expect(minSize).toEqual({
width: 600,
height: 28,
});
scale.left = 5;
scale.top = 5;
scale.right = 605;
scale.bottom = 33;
expect(scale.getPixelForValue(0, 0, 0, false)).toBe(33);
expect(scale.getPixelForValue(0, 0, 0, true)).toBe(85);
expect(scale.getValueForPixel(33)).toBe(0);
expect(scale.getValueForPixel(85)).toBe(0);
expect(scale.getPixelForValue(0, 4, 0, false)).toBe(452);
expect(scale.getPixelForValue(0, 4, 0, true)).toBe(505);
expect(scale.getValueForPixel(452)).toBe(4);
expect(scale.getValueForPixel(505)).toBe(4);
config.gridLines.offsetGridLines = false;
expect(scale.getPixelForValue(0, 0, 0, false)).toBe(33);
expect(scale.getPixelForValue(0, 0, 0, true)).toBe(33);
expect(scale.getValueForPixel(33)).toBe(0);
expect(scale.getPixelForValue(0, 4, 0, false)).toBe(557);
expect(scale.getPixelForValue(0, 4, 0, true)).toBe(557);
expect(scale.getValueForPixel(557)).toBe(4);
});
it ('Should get the correct pixel for a value when horizontal and zoomed', function() {
var scaleID = 'myScale';
var mockData = {
datasets: [{
yAxisID: scaleID,
data: [10, 5, 0, 25, 78]
}],
labels: ['tick1', 'tick2', 'tick3', 'tick4', 'tick_last']
};
var mockContext = window.createMockContext();
var config = Chart.helpers.clone(Chart.scaleService.getScaleDefaults('category'));
config.gridLines.offsetGridLines = true;
config.ticks.min = "tick2";
config.ticks.max = "tick4";
var Constructor = Chart.scaleService.getScaleConstructor('category');
var scale = new Constructor({
ctx: mockContext,
options: config,
chart: {
data: mockData
},
id: scaleID
});
var minSize = scale.update(600, 100);
expect(scale.width).toBe(600);
expect(scale.height).toBe(28);
expect(scale.paddingTop).toBe(0);
expect(scale.paddingBottom).toBe(0);
expect(scale.paddingLeft).toBe(28);
expect(scale.paddingRight).toBe(28);
expect(scale.labelRotation).toBe(0);
expect(minSize).toEqual({
width: 600,
height: 28,
});
scale.left = 5;
scale.top = 5;
scale.right = 605;
scale.bottom = 33;
expect(scale.getPixelForValue(0, 1, 0, false)).toBe(33);
expect(scale.getPixelForValue(0, 1, 0, true)).toBe(124);
expect(scale.getPixelForValue(0, 3, 0, false)).toBe(396);
expect(scale.getPixelForValue(0, 3, 0, true)).toBe(486);
config.gridLines.offsetGridLines = false;
expect(scale.getPixelForValue(0, 1, 0, false)).toBe(33);
expect(scale.getPixelForValue(0, 1, 0, true)).toBe(33);
expect(scale.getPixelForValue(0, 3, 0, false)).toBe(577);
expect(scale.getPixelForValue(0, 3, 0, true)).toBe(577);
});
it ('should get the correct pixel for a value when vertical', function() {
var scaleID = 'myScale';
var mockData = {
datasets: [{
yAxisID: scaleID,
data: [10, 5, 0, 25, 78]
}],
labels: ['tick1', 'tick2', 'tick3', 'tick4', 'tick_last']
};
var mockContext = window.createMockContext();
var config = Chart.helpers.clone(Chart.scaleService.getScaleDefaults('category'));
config.gridLines.offsetGridLines = true;
config.position = "left";
var Constructor = Chart.scaleService.getScaleConstructor('category');
var scale = new Constructor({
ctx: mockContext,
options: config,
chart: {
data: mockData
},
id: scaleID
});
var minSize = scale.update(100, 200);
expect(scale.width).toBe(100);
expect(scale.height).toBe(200);
expect(scale.paddingTop).toBe(6);
expect(scale.paddingBottom).toBe(6);
expect(scale.paddingLeft).toBe(0);
expect(scale.paddingRight).toBe(0);
expect(scale.labelRotation).toBe(0);
expect(minSize).toEqual({
width: 100,
height: 200,
});
scale.left = 5;
scale.top = 5;
scale.right = 105;
scale.bottom = 205;
expect(scale.getPixelForValue(0, 0, 0, false)).toBe(11);
expect(scale.getPixelForValue(0, 0, 0, true)).toBe(30);
expect(scale.getValueForPixel(11)).toBe(0);
expect(scale.getValueForPixel(30)).toBe(0);
expect(scale.getPixelForValue(0, 4, 0, false)).toBe(161);
expect(scale.getPixelForValue(0, 4, 0, true)).toBe(180);
expect(scale.getValueForPixel(161)).toBe(4);
config.gridLines.offsetGridLines = false;
expect(scale.getPixelForValue(0, 0, 0, false)).toBe(11);
expect(scale.getPixelForValue(0, 0, 0, true)).toBe(11);
expect(scale.getValueForPixel(11)).toBe(0);
expect(scale.getPixelForValue(0, 4, 0, false)).toBe(199);
expect(scale.getPixelForValue(0, 4, 0, true)).toBe(199);
expect(scale.getValueForPixel(199)).toBe(4);
});
it ('should get the correct pixel for a value when vertical and zoomed', function() {
var scaleID = 'myScale';
var mockData = {
datasets: [{
yAxisID: scaleID,
data: [10, 5, 0, 25, 78]
}],
labels: ['tick1', 'tick2', 'tick3', 'tick4', 'tick_last']
};
var mockContext = window.createMockContext();
var config = Chart.helpers.clone(Chart.scaleService.getScaleDefaults('category'));
config.gridLines.offsetGridLines = true;
config.ticks.min = "tick2";
config.ticks.max = "tick4";
config.position = "left";
var Constructor = Chart.scaleService.getScaleConstructor('category');
var scale = new Constructor({
ctx: mockContext,
options: config,
chart: {
data: mockData
},
id: scaleID
});
var minSize = scale.update(100, 200);
expect(scale.width).toBe(70);
expect(scale.height).toBe(200);
expect(scale.paddingTop).toBe(6);
expect(scale.paddingBottom).toBe(6);
expect(scale.paddingLeft).toBe(0);
expect(scale.paddingRight).toBe(0);
expect(scale.labelRotation).toBe(0);
expect(minSize).toEqual({
width: 70,
height: 200,
});
scale.left = 5;
scale.top = 5;
scale.right = 75;
scale.bottom = 205;
expect(scale.getPixelForValue(0, 1, 0, false)).toBe(11);
expect(scale.getPixelForValue(0, 1, 0, true)).toBe(42);
expect(scale.getPixelForValue(0, 3, 0, false)).toBe(136);
expect(scale.getPixelForValue(0, 3, 0, true)).toBe(168);
config.gridLines.offsetGridLines = false;
expect(scale.getPixelForValue(0, 1, 0, false)).toBe(11);
expect(scale.getPixelForValue(0, 1, 0, true)).toBe(11);
expect(scale.getPixelForValue(0, 3, 0, false)).toBe(199);
expect(scale.getPixelForValue(0, 3, 0, true)).toBe(199);
});
});

View File

@@ -0,0 +1,731 @@
describe('Linear Scale', function() {
var chartInstance;
beforeEach(function() {
window.addDefaultMatchers(jasmine);
});
afterEach(function() {
if (chartInstance)
{
releaseChart(chartInstance);
}
});
it('Should register the constructor with the scale service', function() {
var Constructor = Chart.scaleService.getScaleConstructor('linear');
expect(Constructor).not.toBe(undefined);
expect(typeof Constructor).toBe('function');
});
it('Should have the correct default config', function() {
var defaultConfig = Chart.scaleService.getScaleDefaults('linear');
expect(defaultConfig).toEqual({
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,
},
position: "left",
scaleLabel: {
labelString: '',
display: false,
},
ticks: {
beginAtZero: false,
minRotation: 0,
maxRotation: 50,
mirror: false,
padding: 10,
reverse: false,
display: true,
callback: defaultConfig.ticks.callback, // make this work nicer, then check below
autoSkip: true,
autoSkipPadding: 0,
labelOffset: 0
}
});
expect(defaultConfig.ticks.callback).toEqual(jasmine.any(Function));
});
it('Should correctly determine the max & min data values', function() {
chartInstance = window.acquireChart({
type: 'bar',
data: {
datasets: [{
yAxisID: 'yScale0',
data: [10, 5, 0, -5, 78, -100]
}, {
yAxisID: 'yScale1',
data: [-1000, 1000],
}, {
yAxisID: 'yScale0',
data: [150]
}],
labels: ['a', 'b', 'c', 'd', 'e', 'f']
},
options: {
scales: {
yAxes: [{
id: 'yScale0',
type: 'linear'
}, {
id: 'yScale1',
type: 'linear'
}]
}
}
});
expect(chartInstance.scales.yScale0).not.toEqual(undefined); // must construct
expect(chartInstance.scales.yScale0.min).toBe(-100);
expect(chartInstance.scales.yScale0.max).toBe(150);
});
it('Should correctly determine the max & min of string data values', function() {
chartInstance = window.acquireChart({
type: 'bar',
data: {
datasets: [{
yAxisID: 'yScale0',
data: ['10', '5', '0', '-5', '78', '-100']
}, {
yAxisID: 'yScale1',
data: ['-1000', '1000'],
}, {
yAxisID: 'yScale0',
data: ['150']
}],
labels: ['a', 'b', 'c', 'd', 'e', 'f']
},
options: {
scales: {
yAxes: [{
id: 'yScale0',
type: 'linear'
}, {
id: 'yScale1',
type: 'linear'
}]
}
}
});
expect(chartInstance.scales.yScale0).not.toEqual(undefined); // must construct
expect(chartInstance.scales.yScale0.min).toBe(-100);
expect(chartInstance.scales.yScale0.max).toBe(150);
});
it('Should correctly determine the max & min data values ignoring hidden datasets', function() {
chartInstance = window.acquireChart({
type: 'bar',
data: {
datasets: [{
yAxisID: 'yScale0',
data: ['10', '5', '0', '-5', '78', '-100']
}, {
yAxisID: 'yScale1',
data: ['-1000', '1000'],
}, {
yAxisID: 'yScale0',
data: ['150'],
hidden: true
}],
labels: ['a', 'b', 'c', 'd', 'e', 'f']
},
options: {
scales: {
yAxes: [{
id: 'yScale0',
type: 'linear'
}, {
id: 'yScale1',
type: 'linear'
}]
}
}
});
expect(chartInstance.scales.yScale0).not.toEqual(undefined); // must construct
expect(chartInstance.scales.yScale0.min).toBe(-100);
expect(chartInstance.scales.yScale0.max).toBe(80);
});
it('Should correctly determine the max & min data values ignoring data that is NaN', function() {
chartInstance = window.acquireChart({
type: 'bar',
data: {
datasets: [{
yAxisID: 'yScale0',
data: [null, 90, NaN, undefined, 45, 30]
}],
labels: ['a', 'b', 'c', 'd', 'e', 'f']
},
options: {
scales: {
yAxes: [{
id: 'yScale0',
type: 'linear'
}]
}
}
});
expect(chartInstance.scales.yScale0.min).toBe(30);
expect(chartInstance.scales.yScale0.max).toBe(90);
// Scale is now stacked
chartInstance.scales.yScale0.options.stacked = true;
chartInstance.update();
expect(chartInstance.scales.yScale0.min).toBe(0);
expect(chartInstance.scales.yScale0.max).toBe(90);
});
it('Should correctly determine the max & min for scatter data', function() {
chartInstance = window.acquireChart({
type: 'line',
data: {
datasets: [{
xAxisID: 'xScale0',
yAxisID: 'yScale0',
data: [{
x: 10,
y: 100
}, {
x: -10,
y: 0
}, {
x: 0,
y: 0
}, {
x: 99,
y: 7
}]
}],
},
options: {
scales: {
xAxes: [{
id: 'xScale0',
type: 'linear',
position: 'bottom'
}],
yAxes: [{
id: 'yScale0',
type: 'linear'
}]
}
}
});
chartInstance.update();
expect(chartInstance.scales.xScale0.min).toBe(-20);
expect(chartInstance.scales.xScale0.max).toBe(100);
expect(chartInstance.scales.yScale0.min).toBe(0);
expect(chartInstance.scales.yScale0.max).toBe(100);
});
it('Should correctly get the label for the given index', function() {
chartInstance = window.acquireChart({
type: 'line',
data: {
datasets: [{
xAxisID: 'xScale0',
yAxisID: 'yScale0',
data: [{
x: 10,
y: 100
}, {
x: -10,
y: 0
}, {
x: 0,
y: 0
}, {
x: 99,
y: 7
}]
}],
},
options: {
scales: {
xAxes: [{
id: 'xScale0',
type: 'linear',
position: 'bottom'
}],
yAxes: [{
id: 'yScale0',
type: 'linear'
}]
}
}
});
chartInstance.update();
expect(chartInstance.scales.yScale0.getLabelForIndex(3, 0)).toBe(7);
});
it('Should correctly determine the min and max data values when stacked mode is turned on', function() {
chartInstance = window.acquireChart({
type: 'line',
data: {
datasets: [{
yAxisID: 'yScale0',
data: [10, 5, 0, -5, 78, -100],
type: 'bar'
}, {
yAxisID: 'yScale1',
data: [-1000, 1000],
}, {
yAxisID: 'yScale0',
data: [150, 0, 0, -100, -10, 9],
type: 'bar'
}, {
yAxisID: 'yScale0',
data: [10, 10, 10, 10, 10, 10],
type: 'line'
}],
labels: ['a', 'b', 'c', 'd', 'e', 'f']
},
options: {
scales: {
yAxes: [{
id: 'yScale0',
type: 'linear',
stacked: true
}, {
id: 'yScale1',
type: 'linear'
}]
}
}
});
chartInstance.update();
expect(chartInstance.scales.yScale0.min).toBe(-150);
expect(chartInstance.scales.yScale0.max).toBe(200);
});
it('Should correctly determine the min and max data values when stacked mode is turned on and there are hidden datasets', function() {
chartInstance = window.acquireChart({
type: 'bar',
data: {
datasets: [{
yAxisID: 'yScale0',
data: [10, 5, 0, -5, 78, -100],
}, {
yAxisID: 'yScale1',
data: [-1000, 1000],
}, {
yAxisID: 'yScale0',
data: [150, 0, 0, -100, -10, 9],
}, {
yAxisID: 'yScale0',
data: [10, 20, 30, 40, 50, 60],
hidden: true
}],
labels: ['a', 'b', 'c', 'd', 'e', 'f']
},
options: {
scales: {
yAxes: [{
id: 'yScale0',
type: 'linear',
stacked: true
}, {
id: 'yScale1',
type: 'linear'
}]
}
}
});
chartInstance.update();
expect(chartInstance.scales.yScale0.min).toBe(-150);
expect(chartInstance.scales.yScale0.max).toBe(200);
});
it('Should correctly determine the min and max data values when stacked mode is turned on there are multiple types of datasets', function() {
chartInstance = window.acquireChart({
type: 'bar',
data: {
datasets: [{
yAxisID: 'yScale0',
type: 'bar',
data: [10, 5, 0, -5, 78, -100]
}, {
type: 'line',
data: [10, 10, 10, 10, 10, 10],
}, {
type: 'bar',
data: [150, 0, 0, -100, -10, 9]
}],
labels: ['a', 'b', 'c', 'd', 'e', 'f']
},
options: {
scales: {
yAxes: [{
id: 'yScale0',
type: 'linear',
stacked: true
}]
}
}
});
chartInstance.scales.yScale0.determineDataLimits();
expect(chartInstance.scales.yScale0.min).toBe(-105);
expect(chartInstance.scales.yScale0.max).toBe(160);
});
it('Should ensure that the scale has a max and min that are not equal', function() {
chartInstance = window.acquireChart({
type: 'bar',
data: {
datasets: [],
labels: ['a', 'b', 'c', 'd', 'e', 'f']
},
options: {
scales: {
yAxes: [{
id: 'yScale0',
type: 'linear'
}]
}
}
});
expect(chartInstance.scales.yScale0).not.toEqual(undefined); // must construct
expect(chartInstance.scales.yScale0.min).toBe(-1);
expect(chartInstance.scales.yScale0.max).toBe(1);
});
it('Should ensure that the scale has a max and min that are not equal when beginAtZero is set', function() {
chartInstance = window.acquireChart({
type: 'bar',
data: {
datasets: [],
labels: ['a', 'b', 'c', 'd', 'e', 'f']
},
options: {
scales: {
yAxes: [{
id: 'yScale0',
type: 'linear',
ticks: {
beginAtZero: true
}
}]
}
}
});
expect(chartInstance.scales.yScale0).not.toEqual(undefined); // must construct
expect(chartInstance.scales.yScale0.min).toBe(0);
expect(chartInstance.scales.yScale0.max).toBe(1);
});
it('Should use the suggestedMin and suggestedMax options', function() {
chartInstance = window.acquireChart({
type: 'bar',
data: {
datasets: [{
yAxisID: 'yScale0',
data: [1, 1, 1, 2, 1, 0]
}],
labels: ['a', 'b', 'c', 'd', 'e', 'f']
},
options: {
scales: {
yAxes: [{
id: 'yScale0',
type: 'linear',
ticks: {
suggestedMax: 10,
suggestedMin: -10
}
}]
}
}
});
expect(chartInstance.scales.yScale0).not.toEqual(undefined); // must construct
expect(chartInstance.scales.yScale0.min).toBe(-10);
expect(chartInstance.scales.yScale0.max).toBe(10);
});
it('Should use the min and max options', function() {
chartInstance = window.acquireChart({
type: 'bar',
data: {
datasets: [{
yAxisID: 'yScale0',
data: [1, 1, 1, 2, 1, 0]
}],
labels: ['a', 'b', 'c', 'd', 'e', 'f']
},
options: {
scales: {
yAxes: [{
id: 'yScale0',
type: 'linear',
ticks: {
max: 1010,
min: -1010
}
}]
}
}
});
expect(chartInstance.scales.yScale0).not.toEqual(undefined); // must construct
expect(chartInstance.scales.yScale0.min).toBe(-1010);
expect(chartInstance.scales.yScale0.max).toBe(1010);
expect(chartInstance.scales.yScale0.ticks[0]).toBe('1010');
expect(chartInstance.scales.yScale0.ticks[chartInstance.scales.yScale0.ticks.length - 1]).toBe('-1010');
});
it('should forcibly include 0 in the range if the beginAtZero option is used', function() {
chartInstance = window.acquireChart({
type: 'bar',
data: {
datasets: [{
yAxisID: 'yScale0',
data: [20, 30, 40, 50]
}],
labels: ['a', 'b', 'c', 'd']
},
options: {
scales: {
yAxes: [{
id: 'yScale0',
type: 'linear',
}]
}
}
});
expect(chartInstance.scales.yScale0).not.toEqual(undefined); // must construct
expect(chartInstance.scales.yScale0.ticks).toEqual(['50', '45', '40', '35', '30', '25', '20']);
chartInstance.scales.yScale0.options.ticks.beginAtZero = true;
chartInstance.update();
expect(chartInstance.scales.yScale0.ticks).toEqual(['50', '45', '40', '35', '30', '25', '20', '15', '10', '5', '0']);
chartInstance.data.datasets[0].data = [-20, -30, -40, -50];
chartInstance.update();
expect(chartInstance.scales.yScale0.ticks).toEqual(['0', '-5', '-10', '-15', '-20', '-25', '-30', '-35', '-40', '-45', '-50']);
chartInstance.scales.yScale0.options.ticks.beginAtZero = false;
chartInstance.update();
expect(chartInstance.scales.yScale0.ticks).toEqual(['-20', '-25', '-30', '-35', '-40', '-45', '-50']);
});
it('Should generate tick marks in the correct order in reversed mode', function() {
chartInstance = window.acquireChart({
type: 'bar',
data: {
datasets: [{
yAxisID: 'yScale0',
data: [10, 5, 0, 25, 78]
}],
labels: ['a', 'b', 'c', 'd']
},
options: {
scales: {
yAxes: [{
id: 'yScale0',
type: 'linear',
ticks: {
reverse: true
}
}]
}
}
});
expect(chartInstance.scales.yScale0.ticks).toEqual(['0', '10', '20', '30', '40', '50', '60', '70', '80']);
expect(chartInstance.scales.yScale0.start).toBe(80);
expect(chartInstance.scales.yScale0.end).toBe(0);
});
it('should use the correct number of decimal places in the default format function', function() {
chartInstance = window.acquireChart({
type: 'bar',
data: {
datasets: [{
yAxisID: 'yScale0',
data: [0.06, 0.005, 0, 0.025, 0.0078]
}],
labels: ['a', 'b', 'c', 'd']
},
options: {
scales: {
yAxes: [{
id: 'yScale0',
type: 'linear',
}]
}
}
});
expect(chartInstance.scales.yScale0.ticks).toEqual(['0.06', '0.05', '0.04', '0.03', '0.02', '0.01', '0']);
});
it('Should build labels using the user supplied callback', function() {
chartInstance = window.acquireChart({
type: 'bar',
data: {
datasets: [{
yAxisID: 'yScale0',
data: [10, 5, 0, 25, 78]
}],
labels: ['a', 'b', 'c', 'd']
},
options: {
scales: {
yAxes: [{
id: 'yScale0',
type: 'linear',
ticks: {
callback: function(value, index) {
return index.toString();
}
}
}]
}
}
});
// Just the index
expect(chartInstance.scales.yScale0.ticks).toEqual(['0', '1', '2', '3', '4', '5', '6', '7', '8']);
});
it('Should get the correct pixel value for a point', function() {
chartInstance = window.acquireChart({
type: 'line',
data: {
datasets: [{
xAxisID: 'xScale0',
yAxisID: 'yScale0',
data: []
}],
},
options: {
scales: {
xAxes: [{
id: 'xScale0',
type: 'linear',
position: 'bottom'
}],
yAxes: [{
id: 'yScale0',
type: 'linear'
}]
}
}
});
var xScale = chartInstance.scales.xScale0;
expect(xScale.getPixelForValue(1, 0, 0)).toBeCloseToPixel(501); // right - paddingRight
expect(xScale.getPixelForValue(-1, 0, 0)).toBeCloseToPixel(41); // left + paddingLeft
expect(xScale.getPixelForValue(0, 0, 0)).toBeCloseToPixel(271); // halfway*/
expect(xScale.getValueForPixel(501)).toBeCloseTo(1, 1e-2);
expect(xScale.getValueForPixel(41)).toBeCloseTo(-1, 1e-2);
expect(xScale.getValueForPixel(271)).toBeCloseTo(0, 1e-2);
var yScale = chartInstance.scales.yScale0;
expect(yScale.getPixelForValue(1, 0, 0)).toBeCloseToPixel(32); // right - paddingRight
expect(yScale.getPixelForValue(-1, 0, 0)).toBeCloseToPixel(484); // left + paddingLeft
expect(yScale.getPixelForValue(0, 0, 0)).toBeCloseToPixel(258); // halfway*/
expect(yScale.getValueForPixel(32)).toBe(1);
expect(yScale.getValueForPixel(484)).toBe(-1);
expect(yScale.getValueForPixel(258)).toBe(0);
});
it('should fit correctly', function() {
chartInstance = window.acquireChart({
type: 'line',
data: {
datasets: [{
xAxisID: 'xScale0',
yAxisID: 'yScale0',
data: [{
x: 10,
y: 100
}, {
x: -10,
y: 0
}, {
x: 0,
y: 0
}, {
x: 99,
y: 7
}]
}],
},
options: {
scales: {
xAxes: [{
id: 'xScale0',
type: 'linear',
position: 'bottom'
}],
yAxes: [{
id: 'yScale0',
type: 'linear'
}]
}
}
});
var xScale = chartInstance.scales.xScale0;
expect(xScale.paddingTop).toBeCloseToPixel(0);
expect(xScale.paddingBottom).toBeCloseToPixel(0);
expect(xScale.paddingLeft).toBeCloseToPixel(0);
expect(xScale.paddingRight).toBeCloseToPixel(13.5);
expect(xScale.width).toBeCloseToPixel(471);
expect(xScale.height).toBeCloseToPixel(28);
var yScale = chartInstance.scales.yScale0;
expect(yScale.paddingTop).toBeCloseToPixel(0);
expect(yScale.paddingBottom).toBeCloseToPixel(0);
expect(yScale.paddingLeft).toBeCloseToPixel(0);
expect(yScale.paddingRight).toBeCloseToPixel(0);
expect(yScale.width).toBeCloseToPixel(41);
expect(yScale.height).toBeCloseToPixel(452);
// Extra size when scale label showing
xScale.options.scaleLabel.display = true;
yScale.options.scaleLabel.display = true;
chartInstance.update();
expect(xScale.paddingTop).toBeCloseToPixel(0);
expect(xScale.paddingBottom).toBeCloseToPixel(0);
expect(xScale.paddingLeft).toBeCloseToPixel(0);
expect(xScale.paddingRight).toBeCloseToPixel(13.5);
expect(xScale.width).toBeCloseToPixel(453);
expect(xScale.height).toBeCloseToPixel(46);
expect(yScale.paddingTop).toBeCloseToPixel(0);
expect(yScale.paddingBottom).toBeCloseToPixel(0);
expect(yScale.paddingLeft).toBeCloseToPixel(0);
expect(yScale.paddingRight).toBeCloseToPixel(0);
expect(yScale.width).toBeCloseToPixel(59);
expect(yScale.height).toBeCloseToPixel(434);
});
});

View File

@@ -0,0 +1,573 @@
describe('Logarithmic Scale tests', function() {
beforeEach(function() {
window.addDefaultMatchers(jasmine);
});
afterEach(function() {
window.releaseAllCharts();
});
it('should register the constructor with the scale service', function() {
var Constructor = Chart.scaleService.getScaleConstructor('logarithmic');
expect(Constructor).not.toBe(undefined);
expect(typeof Constructor).toBe('function');
});
it('should have the correct default config', function() {
var defaultConfig = Chart.scaleService.getScaleDefaults('logarithmic');
expect(defaultConfig).toEqual({
display: true,
gridLines: {
color: "rgba(0, 0, 0, 0.1)",
drawBorder: true,
drawOnChartArea: true,
drawTicks: true,
tickMarkLength: 10,
lineWidth: 1,
offsetGridLines: false,
display: true,
zeroLineColor: "rgba(0,0,0,0.25)",
zeroLineWidth: 1,
},
position: "left",
scaleLabel: {
labelString: '',
display: false,
},
ticks: {
beginAtZero: false,
minRotation: 0,
maxRotation: 50,
mirror: false,
padding: 10,
reverse: false,
display: true,
callback: defaultConfig.ticks.callback, // make this nicer, then check explicitly below
autoSkip: true,
autoSkipPadding: 0,
labelOffset: 0
},
});
// Is this actually a function
expect(defaultConfig.ticks.callback).toEqual(jasmine.any(Function));
});
it('should correctly determine the max & min data values', function() {
var chart = window.acquireChart({
type: 'bar',
data: {
datasets: [{
yAxisID: 'yScale0',
data: [42, 1000, 64, 100],
}, {
yAxisID: 'yScale1',
data: [10, 5, 5000, 78, 450]
}, {
yAxisID: 'yScale1',
data: [150]
}],
labels: ['a', 'b', 'c', 'd', 'e']
},
options: {
scales: {
yAxes: [{
id: 'yScale0',
type: 'logarithmic'
}, {
id: 'yScale1',
type: 'logarithmic'
}]
}
}
});
expect(chart.scales.yScale0).not.toEqual(undefined); // must construct
expect(chart.scales.yScale0.min).toBe(10);
expect(chart.scales.yScale0.max).toBe(1000);
expect(chart.scales.yScale1).not.toEqual(undefined); // must construct
expect(chart.scales.yScale1.min).toBe(1);
expect(chart.scales.yScale1.max).toBe(5000);
});
it('should correctly determine the max & min of string data values', function() {
var chart = window.acquireChart({
type: 'line',
data: {
datasets: [{
yAxisID: 'yScale0',
data: ['42', '1000', '64', '100'],
}, {
yAxisID: 'yScale1',
data: ['10', '5', '5000', '78', '450']
}, {
yAxisID: 'yScale1',
data: ['150']
}],
labels: ['a', 'b', 'c', 'd', 'e']
},
options: {
scales: {
yAxes: [{
id: 'yScale0',
type: 'logarithmic'
}, {
id: 'yScale1',
type: 'logarithmic'
}]
}
}
});
expect(chart.scales.yScale0).not.toEqual(undefined); // must construct
expect(chart.scales.yScale0.min).toBe(10);
expect(chart.scales.yScale0.max).toBe(1000);
expect(chart.scales.yScale1).not.toEqual(undefined); // must construct
expect(chart.scales.yScale1.min).toBe(1);
expect(chart.scales.yScale1.max).toBe(5000);
});
it('should correctly determine the max & min data values when there are hidden datasets', function() {
var chart = window.acquireChart({
type: 'line',
data: {
datasets: [{
yAxisID: 'yScale1',
data: [10, 5, 5000, 78, 450]
}, {
yAxisID: 'yScale0',
data: [42, 1000, 64, 100],
}, {
yAxisID: 'yScale1',
data: [50000],
hidden: true
}],
labels: ['a', 'b', 'c', 'd', 'e']
},
options: {
scales: {
yAxes: [{
id: 'yScale0',
type: 'logarithmic'
}, {
id: 'yScale1',
type: 'logarithmic'
}]
}
}
});
expect(chart.scales.yScale1).not.toEqual(undefined); // must construct
expect(chart.scales.yScale1.min).toBe(1);
expect(chart.scales.yScale1.max).toBe(5000);
});
it('should correctly determine the max & min data values when there is NaN data', function() {
var chart = window.acquireChart({
type: 'bar',
data: {
datasets: [{
data: [undefined, 10, null, 5, 5000, NaN, 78, 450]
}, {
data: [undefined, 28, null, 1000, 500, NaN, 50, 42]
}],
labels: ['a', 'b', 'c', 'd', 'e', 'f' ,'g']
},
options: {
scales: {
yAxes: [{
id: 'yScale',
type: 'logarithmic'
}]
}
}
});
expect(chart.scales.yScale).not.toEqual(undefined); // must construct
expect(chart.scales.yScale.min).toBe(1);
expect(chart.scales.yScale.max).toBe(5000);
// Turn on stacked mode since it uses it's own
chart.options.scales.yAxes[0].stacked = true;
chart.update();
expect(chart.scales.yScale.min).toBe(10);
expect(chart.scales.yScale.max).toBe(6000);
});
it('should correctly determine the max & min for scatter data', function() {
var chart = window.acquireChart({
type: 'line',
data: {
datasets: [{
data: [
{ x: 10, y: 100 },
{ x: 2, y: 6 },
{ x: 65, y: 121 },
{ x: 99, y: 7 }
]
}]
},
options: {
scales: {
xAxes: [{
id: 'xScale',
type: 'logarithmic',
position: 'bottom'
}],
yAxes: [{
id: 'yScale',
type: 'logarithmic'
}]
}
}
});
expect(chart.scales.xScale.min).toBe(1);
expect(chart.scales.xScale.max).toBe(100);
expect(chart.scales.yScale.min).toBe(1);
expect(chart.scales.yScale.max).toBe(200);
});
it('should correctly determine the min and max data values when stacked mode is turned on', function() {
var chart = window.acquireChart({
type: 'bar',
data: {
datasets: [{
type: 'bar',
yAxisID: 'yScale0',
data: [10, 5, 1, 5, 78, 100]
}, {
yAxisID: 'yScale1',
data: [-1000, 1000],
}, {
type: 'bar',
yAxisID: 'yScale0',
data: [150, 10, 10, 100, 10, 9]
}, {
type: 'line',
yAxisID: 'yScale0',
data: [100, 100, 100, 100, 100, 100]
}],
labels: ['a', 'b', 'c', 'd', 'e', 'f']
},
options: {
scales: {
yAxes: [{
id: 'yScale0',
type: 'logarithmic',
stacked: true
}, {
id: 'yScale1',
type: 'logarithmic'
}]
}
}
});
expect(chart.scales.yScale0.min).toBe(10);
expect(chart.scales.yScale0.max).toBe(200);
});
it('should correctly determine the min and max data values when stacked mode is turned on ignoring hidden datasets', function() {
var chart = window.acquireChart({
type: 'bar',
data: {
datasets: [{
yAxisID: 'yScale0',
data: [10, 5, 1, 5, 78, 100],
type: 'bar'
}, {
yAxisID: 'yScale1',
data: [-1000, 1000],
type: 'bar'
}, {
yAxisID: 'yScale0',
data: [150, 10, 10, 100, 10, 9],
type: 'bar'
}, {
yAxisID: 'yScale0',
data: [10000, 10000, 10000, 10000, 10000, 10000],
hidden: true,
type: 'bar'
}],
labels: ['a', 'b', 'c', 'd', 'e', 'f']
},
options: {
scales: {
yAxes: [{
id: 'yScale0',
type: 'logarithmic',
stacked: true
}, {
id: 'yScale1',
type: 'logarithmic'
}]
}
}
});
expect(chart.scales.yScale0.min).toBe(10);
expect(chart.scales.yScale0.max).toBe(200);
});
it('should ensure that the scale has a max and min that are not equal', function() {
var chart = window.acquireChart({
type: 'bar',
data: {
datasets: [{
data: []
}],
labels: []
},
options: {
scales: {
yAxes: [{
id: 'yScale',
type: 'logarithmic'
}]
}
}
});
expect(chart.scales.yScale.min).toBe(1);
expect(chart.scales.yScale.max).toBe(10);
chart.data.datasets[0].data = [0.15, 0.15];
chart.update();
expect(chart.scales.yScale.min).toBe(0.01);
expect(chart.scales.yScale.max).toBe(1);
});
it('should use the min and max options', function() {
var chart = window.acquireChart({
type: 'bar',
data: {
datasets: [{
data: [1, 1, 1, 2, 1, 0]
}],
labels: []
},
options: {
scales: {
yAxes: [{
id: 'yScale',
type: 'logarithmic',
ticks: {
min: 10,
max: 1010,
callback: function(value) {
return value;
}
}
}]
}
}
});
var yScale = chart.scales.yScale;
var tickCount = yScale.ticks.length;
expect(yScale.min).toBe(10);
expect(yScale.max).toBe(1010);
expect(yScale.ticks[0]).toBe(1010);
expect(yScale.ticks[tickCount - 1]).toBe(10);
});
it('should generate tick marks', function() {
var chart = window.acquireChart({
type: 'bar',
data: {
datasets: [{
data: [10, 5, 1, 25, 78]
}],
labels: []
},
options: {
scales: {
yAxes: [{
id: 'yScale',
type: 'logarithmic',
ticks: {
callback: function(value) {
return value;
}
}
}]
}
}
});
// Counts down because the lines are drawn top to bottom
expect(chart.scales.yScale).toEqual(jasmine.objectContaining({
ticks: [80, 70, 60, 50, 40, 30, 20, 10, 9, 8, 7, 6, 5, 4, 3, 2, 1],
start: 1,
end: 80
}));
});
it('should generate tick marks in the correct order in reversed mode', function() {
var chart = window.acquireChart({
type: 'line',
data: {
datasets: [{
data: [10, 5, 1, 25, 78]
}],
labels: []
},
options: {
scales: {
yAxes: [{
id: 'yScale',
type: 'logarithmic',
ticks: {
reverse: true,
callback: function(value) {
return value;
}
}
}]
}
}
});
// Counts down because the lines are drawn top to bottom
expect(chart.scales.yScale).toEqual(jasmine.objectContaining({
ticks: [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 20, 30, 40, 50, 60, 70, 80],
start: 80,
end: 1
}));
});
it('should build labels using the default template', function() {
var chart = window.acquireChart({
type: 'line',
data: {
datasets: [{
data: [10, 5, 1, 25, 78]
}],
labels: []
},
options: {
scales: {
yAxes: [{
id: 'yScale',
type: 'logarithmic'
}]
}
}
});
expect(chart.scales.yScale.ticks).toEqual(['8e+1', '', '', '5e+1', '', '', '2e+1', '1e+1', '', '', '', '', '5e+0', '', '', '2e+0', '1e+0']);
});
it('should build labels using the user supplied callback', function() {
var chart = window.acquireChart({
type: 'bar',
data: {
datasets: [{
data: [10, 5, 1, 25, 78]
}],
labels: []
},
options: {
scales: {
yAxes: [{
id: 'yScale',
type: 'logarithmic',
ticks: {
callback: function(value, index) {
return index.toString();
}
}
}]
}
}
});
// Just the index
expect(chart.scales.yScale.ticks).toEqual(['0', '1', '2', '3', '4', '5', '6', '7', '8', '9', '10', '11', '12', '13', '14', '15', '16']);
});
it('should correctly get the correct label for a data item', function() {
var chart = window.acquireChart({
type: 'bar',
data: {
datasets: [{
yAxisID: 'yScale0',
data: [10, 5, 5000, 78, 450]
}, {
yAxisID: 'yScale1',
data: [1, 1000, 10, 100],
}, {
yAxisID: 'yScale0',
data: [150]
}],
labels: []
},
options: {
scales: {
yAxes: [{
id: 'yScale0',
type: 'logarithmic'
}, {
id: 'yScale1',
type: 'logarithmic'
}]
}
}
});
expect(chart.scales.yScale1.getLabelForIndex(0, 2)).toBe(150);
});
it('should get the correct pixel value for a point', function() {
var chart = window.acquireChart({
type: 'bar',
data: {
datasets: [{
xAxisID: 'xScale', // for the horizontal scale
yAxisID: 'yScale',
data: [10, 5, 1, 25, 78]
}],
labels: []
},
options: {
scales: {
yAxes: [{
id: 'xScale',
type: 'logarithmic',
position: 'bottom'
}, {
id: 'yScale',
type: 'logarithmic'
}]
}
}
});
var xScale = chart.scales.xScale;
expect(xScale.getPixelForValue(80, 0, 0)).toBeCloseToPixel(495); // right - paddingRight
expect(xScale.getPixelForValue( 1, 0, 0)).toBeCloseToPixel(48); // left + paddingLeft
expect(xScale.getPixelForValue(10, 0, 0)).toBeCloseToPixel(283); // halfway
expect(xScale.getPixelForValue( 0, 0, 0)).toBeCloseToPixel(48); // 0 is invalid, put it on the left.
expect(xScale.getValueForPixel(495)).toBeCloseTo(80, 1e-4);
expect(xScale.getValueForPixel(48)).toBeCloseTo(1, 1e-4);
expect(xScale.getValueForPixel(283)).toBeCloseTo(10, 1e-4);
var yScale = chart.scales.yScale;
expect(yScale.getPixelForValue(80, 0, 0)).toBeCloseToPixel(32); // top + paddingTop
expect(yScale.getPixelForValue( 1, 0, 0)).toBeCloseToPixel(456); // bottom - paddingBottom
expect(yScale.getPixelForValue(10, 0, 0)).toBeCloseToPixel(234); // halfway
expect(yScale.getPixelForValue( 0, 0, 0)).toBeCloseToPixel(32); // 0 is invalid. force it on top
expect(yScale.getValueForPixel(32)).toBeCloseTo(80, 1e-4);
expect(yScale.getValueForPixel(456)).toBeCloseTo(1, 1e-4);
expect(yScale.getValueForPixel(234)).toBeCloseTo(10, 1e-4);
});
});

View File

@@ -0,0 +1,423 @@
// Tests for the radial linear scale used by the polar area and radar charts
describe('Test the radial linear scale', function() {
var chartInstance;
beforeEach(function() {
window.addDefaultMatchers(jasmine);
});
afterEach(function() {
if (chartInstance) {
releaseChart(chartInstance);
}
});
it('Should register the constructor with the scale service', function() {
var Constructor = Chart.scaleService.getScaleConstructor('radialLinear');
expect(Constructor).not.toBe(undefined);
expect(typeof Constructor).toBe('function');
});
it('Should have the correct default config', function() {
var defaultConfig = Chart.scaleService.getScaleDefaults('radialLinear');
expect(defaultConfig).toEqual({
angleLines: {
display: true,
color: "rgba(0, 0, 0, 0.1)",
lineWidth: 1
},
animate: true,
display: true,
gridLines: {
color: "rgba(0, 0, 0, 0.1)",
drawBorder: true,
drawOnChartArea: true,
drawTicks: true,
tickMarkLength: 10,
lineWidth: 1,
offsetGridLines: false,
display: true,
zeroLineColor: "rgba(0,0,0,0.25)",
zeroLineWidth: 1,
},
lineArc: false,
pointLabels: {
fontSize: 10,
callback: defaultConfig.pointLabels.callback, // make this nicer, then check explicitly below
},
position: "chartArea",
scaleLabel: {
labelString: '',
display: false,
},
ticks: {
backdropColor: "rgba(255,255,255,0.75)",
backdropPaddingY: 2,
backdropPaddingX: 2,
beginAtZero: false,
minRotation: 0,
maxRotation: 50,
mirror: false,
padding: 10,
reverse: false,
showLabelBackdrop: true,
display: true,
callback: defaultConfig.ticks.callback, // make this nicer, then check explicitly below
autoSkip: true,
autoSkipPadding: 0,
labelOffset: 0
},
});
// Is this actually a function
expect(defaultConfig.ticks.callback).toEqual(jasmine.any(Function));
expect(defaultConfig.pointLabels.callback).toEqual(jasmine.any(Function));
});
it('Should correctly determine the max & min data values', function() {
chartInstance = window.acquireChart({
type: 'radar',
data: {
datasets: [{
data: [10, 5, 0, -5, 78, -100]
}, {
data: [150]
}],
labels: ['lablel1', 'label2', 'label3', 'label4', 'label5', 'label6']
},
options: {
scales: {
}
}
});
expect(chartInstance.scale.min).toBe(-100);
expect(chartInstance.scale.max).toBe(150);
});
it('Should correctly determine the max & min of string data values', function() {
chartInstance = window.acquireChart({
type: 'radar',
data: {
datasets: [{
data: ['10', '5', '0', '-5', '78', '-100']
}, {
data: ['150']
}],
labels: ['lablel1', 'label2', 'label3', 'label4', 'label5', 'label6']
},
options: {
scales: {
}
}
});
expect(chartInstance.scale.min).toBe(-100);
expect(chartInstance.scale.max).toBe(150);
});
it('Should correctly determine the max & min data values when there are hidden datasets', function() {
chartInstance = window.acquireChart({
type: 'radar',
data: {
datasets: [{
data: ['10', '5', '0', '-5', '78', '-100']
}, {
data: ['150']
}, {
data: [1000],
hidden: true
}],
labels: ['lablel1', 'label2', 'label3', 'label4', 'label5', 'label6']
},
options: {
scales: {
}
}
});
expect(chartInstance.scale.min).toBe(-100);
expect(chartInstance.scale.max).toBe(150);
});
it('Should correctly determine the max & min data values when there is NaN data', function() {
chartInstance = window.acquireChart({
type: 'radar',
data: {
datasets: [{
data: [50, 60, NaN, 70, null, undefined]
}],
labels: ['lablel1', 'label2', 'label3', 'label4', 'label5', 'label6']
},
options: {
scales: {
}
}
});
expect(chartInstance.scale.min).toBe(50);
expect(chartInstance.scale.max).toBe(70);
});
it('Should ensure that the scale has a max and min that are not equal', function() {
var scaleID = 'myScale';
var mockData = {
datasets: [],
labels: []
};
var mockContext = window.createMockContext();
var Constructor = Chart.scaleService.getScaleConstructor('radialLinear');
var scale = new Constructor({
ctx: mockContext,
options: Chart.scaleService.getScaleDefaults('radialLinear'), // use default config for scale
chart: {
data: mockData
},
id: scaleID,
});
scale.update(200, 300);
expect(scale.min).toBe(-1);
expect(scale.max).toBe(1);
});
it('Should use the suggestedMin and suggestedMax options', function() {
chartInstance = window.acquireChart({
type: 'radar',
data: {
datasets: [{
data: [1, 1, 1, 2, 1, 0]
}],
labels: ['lablel1', 'label2', 'label3', 'label4', 'label5', 'label6']
},
options: {
scale: {
ticks: {
suggestedMin: -10,
suggestedMax: 10
}
}
}
});
expect(chartInstance.scale.min).toBe(-10);
expect(chartInstance.scale.max).toBe(10);
});
it('Should use the min and max options', function() {
chartInstance = window.acquireChart({
type: 'radar',
data: {
datasets: [{
data: [1, 1, 1, 2, 1, 0]
}],
labels: ['lablel1', 'label2', 'label3', 'label4', 'label5', 'label6']
},
options: {
scale: {
ticks: {
min: -1010,
max: 1010
}
}
}
});
expect(chartInstance.scale.min).toBe(-1010);
expect(chartInstance.scale.max).toBe(1010);
expect(chartInstance.scale.ticks).toEqual(['-1010', '-1000', '-500', '0', '500', '1000', '1010']);
});
it('should forcibly include 0 in the range if the beginAtZero option is used', function() {
chartInstance = window.acquireChart({
type: 'radar',
data: {
datasets: [{
data: [20, 30, 40, 50]
}],
labels: ['lablel1', 'label2', 'label3', 'label4']
},
options: {
scale: {
ticks: {
beginAtZero: false
}
}
}
});
expect(chartInstance.scale.ticks).toEqual(['20', '25', '30', '35', '40', '45', '50']);
chartInstance.scale.options.ticks.beginAtZero = true;
chartInstance.update();
expect(chartInstance.scale.ticks).toEqual(['0', '5', '10', '15', '20', '25', '30', '35', '40', '45', '50']);
chartInstance.data.datasets[0].data = [-20, -30, -40, -50];
chartInstance.update();
expect(chartInstance.scale.ticks).toEqual(['-50', '-45', '-40', '-35', '-30', '-25', '-20', '-15', '-10', '-5', '0']);
chartInstance.scale.options.ticks.beginAtZero = false;
chartInstance.update();
expect(chartInstance.scale.ticks).toEqual(['-50', '-45', '-40', '-35', '-30', '-25', '-20']);
});
it('Should generate tick marks in the correct order in reversed mode', function() {
chartInstance = window.acquireChart({
type: 'radar',
data: {
datasets: [{
data: [10, 5, 0, 25, 78]
}],
labels: ['lablel1', 'label2', 'label3', 'label4', 'label5']
},
options: {
scale: {
ticks: {
reverse: true
}
}
}
});
expect(chartInstance.scale.ticks).toEqual(['80', '70', '60', '50', '40', '30', '20', '10', '0']);
expect(chartInstance.scale.start).toBe(80);
expect(chartInstance.scale.end).toBe(0);
});
it('Should build labels using the user supplied callback', function() {
chartInstance = window.acquireChart({
type: 'radar',
data: {
datasets: [{
data: [10, 5, 0, 25, 78]
}],
labels: ['label1', 'label2', 'label3', 'label4', 'label5']
},
options: {
scale: {
ticks: {
callback: function(value, index) {
return index.toString();
}
}
}
}
});
expect(chartInstance.scale.ticks).toEqual(['0', '1', '2', '3', '4', '5', '6', '7', '8']);
expect(chartInstance.scale.pointLabels).toEqual(['label1', 'label2', 'label3', 'label4', 'label5']);
});
it('Should build point labels using the user supplied callback', function() {
chartInstance = window.acquireChart({
type: 'radar',
data: {
datasets: [{
data: [10, 5, 0, 25, 78]
}],
labels: ['label1', 'label2', 'label3', 'label4', 'label5']
},
options: {
scale: {
pointLabels: {
callback: function(value, index) {
return index.toString();
}
}
}
}
});
expect(chartInstance.scale.pointLabels).toEqual(['0', '1', '2', '3', '4']);
});
it('should correctly set the center point', function() {
chartInstance = window.acquireChart({
type: 'radar',
data: {
datasets: [{
data: [10, 5, 0, 25, 78]
}],
labels: ['label1', 'label2', 'label3', 'label4', 'label5']
},
options: {
scale: {
pointLabels: {
callback: function(value, index) {
return index.toString();
}
}
}
}
});
expect(chartInstance.scale.drawingArea).toBe(225);
expect(chartInstance.scale.xCenter).toBe(256);
expect(chartInstance.scale.yCenter).toBe(272);
});
it('should correctly get the label for a given data index', function() {
chartInstance = window.acquireChart({
type: 'radar',
data: {
datasets: [{
data: [10, 5, 0, 25, 78]
}],
labels: ['label1', 'label2', 'label3', 'label4', 'label5']
},
options: {
scale: {
pointLabels: {
callback: function(value, index) {
return index.toString();
}
}
}
}
});
expect(chartInstance.scale.getLabelForIndex(1, 0)).toBe(5);
});
it('should get the correct distance from the center point', function() {
chartInstance = window.acquireChart({
type: 'radar',
data: {
datasets: [{
data: [10, 5, 0, 25, 78]
}],
labels: ['label1', 'label2', 'label3', 'label4', 'label5']
},
options: {
scale: {
pointLabels: {
callback: function(value, index) {
return index.toString();
}
}
}
}
});
expect(chartInstance.scale.getDistanceFromCenterForValue(chartInstance.scale.min)).toBe(0);
expect(chartInstance.scale.getDistanceFromCenterForValue(chartInstance.scale.max)).toBe(225);
expect(chartInstance.scale.getPointPositionForValue(1, 5)).toEqual({
x: 269,
y: 268,
});
chartInstance.scale.options.reverse = true;
chartInstance.update();
expect(chartInstance.scale.getDistanceFromCenterForValue(chartInstance.scale.min)).toBe(225);
expect(chartInstance.scale.getDistanceFromCenterForValue(chartInstance.scale.max)).toBe(0);
});
});

View File

@@ -0,0 +1,438 @@
// Time scale tests
describe('Time scale tests', function() {
var chartInstance;
beforeEach(function() {
window.addDefaultMatchers(jasmine);
// Need a time matcher for getValueFromPixel
jasmine.addMatchers({
toBeCloseToTime: function() {
return {
compare: function(actual, expected) {
var result = false;
var diff = actual.diff(expected.value, expected.unit, true);
result = Math.abs(diff) < (expected.threshold !== undefined ? expected.threshold : 0.5);
return {
pass: result
};
}
}
}
});
});
afterEach(function() {
if (chartInstance)
{
releaseChart(chartInstance);
}
});
it('Should load moment.js as a dependency', function() {
expect(window.moment).not.toBe(undefined);
});
it('Should register the constructor with the scale service', function() {
var Constructor = Chart.scaleService.getScaleConstructor('time');
expect(Constructor).not.toBe(undefined);
expect(typeof Constructor).toBe('function');
});
it('Should have the correct default config', function() {
var defaultConfig = Chart.scaleService.getScaleDefaults('time');
expect(defaultConfig).toEqual({
display: true,
gridLines: {
color: "rgba(0, 0, 0, 0.1)",
drawBorder: true,
drawOnChartArea: true,
drawTicks: true,
tickMarkLength: 10,
lineWidth: 1,
offsetGridLines: false,
display: true,
zeroLineColor: "rgba(0,0,0,0.25)",
zeroLineWidth: 1
},
position: "bottom",
scaleLabel: {
labelString: '',
display: false
},
ticks: {
beginAtZero: false,
minRotation: 0,
maxRotation: 50,
mirror: false,
padding: 10,
reverse: false,
display: true,
callback: defaultConfig.ticks.callback, // make this nicer, then check explicitly below,
autoSkip: false,
autoSkipPadding: 0,
labelOffset: 0
},
time: {
parser: false,
format: false,
unit: false,
round: false,
isoWeekday: false,
displayFormat: false,
displayFormats: {
'millisecond': 'h:mm:ss.SSS a', // 11:20:01.123 AM
'second': 'h:mm:ss a', // 11:20:01 AM
'minute': 'h:mm:ss a', // 11:20:01 AM
'hour': 'MMM D, hA', // Sept 4, 5PM
'day': 'll', // Sep 4 2015
'week': 'll', // Week 46, or maybe "[W]WW - YYYY" ?
'month': 'MMM YYYY', // Sept 2015
'quarter': '[Q]Q - YYYY', // Q3
'year': 'YYYY' // 2015
}
}
});
// Is this actually a function
expect(defaultConfig.ticks.callback).toEqual(jasmine.any(Function));
});
it('should build ticks using days', function() {
var scaleID = 'myScale';
var mockData = {
labels: ["2015-01-01T20:00:00", "2015-01-02T21:00:00", "2015-01-03T22:00:00", "2015-01-05T23:00:00", "2015-01-07T03:00", "2015-01-08T10:00", "2015-01-10T12:00"], // days
};
var mockContext = window.createMockContext();
var Constructor = Chart.scaleService.getScaleConstructor('time');
var scale = new Constructor({
ctx: mockContext,
options: Chart.scaleService.getScaleDefaults('time'), // use default config for scale
chart: {
data: mockData
},
id: scaleID
});
//scale.buildTicks();
scale.update(400, 50);
// Counts down because the lines are drawn top to bottom
expect(scale.ticks).toEqual([ 'Dec 28, 2014', 'Jan 4, 2015', 'Jan 11, 2015' ]);
});
it('should build ticks using date objects', function() {
// Helper to build date objects
function newDateFromRef(days) {
return moment('01/01/2015 12:00', 'DD/MM/YYYY HH:mm').add(days, 'd').toDate();
}
var scaleID = 'myScale';
var mockData = {
labels: [newDateFromRef(0), newDateFromRef(1), newDateFromRef(2), newDateFromRef(4), newDateFromRef(6), newDateFromRef(7), newDateFromRef(9)], // days
};
var mockContext = window.createMockContext();
var Constructor = Chart.scaleService.getScaleConstructor('time');
var scale = new Constructor({
ctx: mockContext,
options: Chart.scaleService.getScaleDefaults('time'), // use default config for scale
chart: {
data: mockData
},
id: scaleID
});
scale.update(400, 50);
// Counts down because the lines are drawn top to bottom
expect(scale.ticks).toEqual([ 'Dec 28, 2014', 'Jan 4, 2015', 'Jan 11, 2015' ]);
});
it('should build ticks when the data is xy points', function() {
// Helper to build date objects
function newDateFromRef(days) {
return moment('01/01/2015 12:00', 'DD/MM/YYYY HH:mm').add(days, 'd').toDate();
}
chartInstance = window.acquireChart({
type: 'line',
data: {
datasets: [{
xAxisID: 'xScale0',
yAxisID: 'yScale0',
data: [{
x: newDateFromRef(0),
y: 1
}, {
x: newDateFromRef(1),
y: 10
}, {
x: newDateFromRef(2),
y: 0
}, {
x: newDateFromRef(4),
y: 5
}, {
x: newDateFromRef(6),
y: 77
}, {
x: newDateFromRef(7),
y: 9
}, {
x: newDateFromRef(9),
y: 5
}]
}],
},
options: {
scales: {
xAxes: [{
id: 'xScale0',
type: 'time',
position: 'bottom'
}],
yAxes: [{
id: 'yScale0',
type: 'linear'
}]
}
}
});
// Counts down because the lines are drawn top to bottom
var xScale = chartInstance.scales.xScale0;
expect(xScale.ticks).toEqual([ 'Jan 1, 2015', 'Jan 3, 2015', 'Jan 5, 2015', 'Jan 7, 2015', 'Jan 9, 2015', 'Jan 11, 2015' ]);
});
it('should allow custom time parsers', function() {
chartInstance = window.acquireChart({
type: 'line',
data: {
datasets: [{
xAxisID: 'xScale0',
yAxisID: 'yScale0',
data: [{
x: 375068900,
y: 1
}]
}],
},
options: {
scales: {
xAxes: [{
id: 'xScale0',
type: 'time',
position: 'bottom',
time: {
unit: 'day',
round: true,
parser: function customTimeParser(label) {
return moment.unix(label);
}
}
}],
yAxes: [{
id: 'yScale0',
type: 'linear'
}]
}
}
});
// Counts down because the lines are drawn top to bottom
var xScale = chartInstance.scales.xScale0;
// Counts down because the lines are drawn top to bottom
expect(xScale.ticks[0]).toEqualOneOf(['Nov 19, 1981', 'Nov 20, 1981', 'Nov 21, 1981']); // handle time zone changes
expect(xScale.ticks[1]).toEqualOneOf(['Nov 19, 1981', 'Nov 20, 1981', 'Nov 21, 1981']); // handle time zone changes
});
it('should build ticks using the config unit', function() {
var scaleID = 'myScale';
var mockData = {
labels: ["2015-01-01T20:00:00", "2015-01-02T21:00:00"], // days
};
var mockContext = window.createMockContext();
var config = Chart.helpers.clone(Chart.scaleService.getScaleDefaults('time'));
config.time.unit = 'hour';
var Constructor = Chart.scaleService.getScaleConstructor('time');
var scale = new Constructor({
ctx: mockContext,
options: config, // use default config for scale
chart: {
data: mockData
},
id: scaleID
});
//scale.buildTicks();
scale.update(400, 50);
expect(scale.ticks).toEqual(['Jan 1, 8PM', 'Jan 1, 9PM', 'Jan 1, 10PM', 'Jan 1, 11PM', 'Jan 2, 12AM', 'Jan 2, 1AM', 'Jan 2, 2AM', 'Jan 2, 3AM', 'Jan 2, 4AM', 'Jan 2, 5AM', 'Jan 2, 6AM', 'Jan 2, 7AM', 'Jan 2, 8AM', 'Jan 2, 9AM', 'Jan 2, 10AM', 'Jan 2, 11AM', 'Jan 2, 12PM', 'Jan 2, 1PM', 'Jan 2, 2PM', 'Jan 2, 3PM', 'Jan 2, 4PM', 'Jan 2, 5PM', 'Jan 2, 6PM', 'Jan 2, 7PM', 'Jan 2, 8PM', 'Jan 2, 9PM']);
});
it('should build ticks using the config diff', function() {
var scaleID = 'myScale';
var mockData = {
labels: ["2015-01-01T20:00:00", "2015-02-02T21:00:00", "2015-02-21T01:00:00"], // days
};
var mockContext = window.createMockContext();
var config = Chart.helpers.clone(Chart.scaleService.getScaleDefaults('time'));
config.time.unit = 'week';
config.time.round = 'week';
var Constructor = Chart.scaleService.getScaleConstructor('time');
var scale = new Constructor({
ctx: mockContext,
options: config, // use default config for scale
chart: {
data: mockData
},
id: scaleID
});
//scale.buildTicks();
scale.update(400, 50);
// last date is feb 15 because we round to start of week
expect(scale.ticks).toEqual(['Dec 28, 2014', 'Jan 4, 2015', 'Jan 11, 2015', 'Jan 18, 2015', 'Jan 25, 2015', 'Feb 1, 2015', 'Feb 8, 2015', 'Feb 15, 2015']);
});
it('Should use the min and max options', function() {
var scaleID = 'myScale';
var mockData = {
labels: ["2015-01-01T20:00:00", "2015-01-02T20:00:00", "2015-01-03T20:00:00"], // days
};
var mockContext = window.createMockContext();
var config = Chart.helpers.clone(Chart.scaleService.getScaleDefaults('time'));
config.time.min = "2015-01-01T04:00:00";
config.time.max = "2015-01-05T06:00:00"
var Constructor = Chart.scaleService.getScaleConstructor('time');
var scale = new Constructor({
ctx: mockContext,
options: config, // use default config for scale
chart: {
data: mockData
},
id: scaleID
});
scale.update(400, 50);
expect(scale.ticks).toEqual([ 'Jan 1, 2015', 'Jan 5, 2015' ]);
});
it('Should use the isoWeekday option', function() {
var scaleID = 'myScale';
var mockData = {
labels: [
"2015-01-01T20:00:00", // Thursday
"2015-01-02T20:00:00", // Friday
"2015-01-03T20:00:00" // Saturday
]
};
var mockContext = window.createMockContext();
var config = Chart.helpers.clone(Chart.scaleService.getScaleDefaults('time'));
config.time.unit = 'week';
// Wednesday
config.time.isoWeekday = 3;
var Constructor = Chart.scaleService.getScaleConstructor('time');
var scale = new Constructor({
ctx: mockContext,
options: config, // use default config for scale
chart: {
data: mockData
},
id: scaleID
});
scale.update(400, 50);
expect(scale.ticks).toEqual([ 'Dec 31, 2014', 'Jan 7, 2015' ]);
});
it('should get the correct pixel for a value', function() {
chartInstance = window.acquireChart({
type: 'line',
data: {
datasets: [{
xAxisID: 'xScale0',
yAxisID: 'yScale0',
data: []
}],
labels: ["2015-01-01T20:00:00", "2015-01-02T21:00:00", "2015-01-03T22:00:00", "2015-01-05T23:00:00", "2015-01-07T03:00", "2015-01-08T10:00", "2015-01-10T12:00"], // days
},
options: {
scales: {
xAxes: [{
id: 'xScale0',
type: 'time',
position: 'bottom'
}],
yAxes: [{
id: 'yScale0',
type: 'linear',
position: 'left'
}]
}
}
});
var xScale = chartInstance.scales.xScale0;
expect(xScale.getPixelForValue('', 0, 0)).toBeCloseToPixel(78);
expect(xScale.getPixelForValue('', 6, 0)).toBeCloseToPixel(452);
expect(xScale.getValueForPixel(78)).toBeCloseToTime({
value: moment(chartInstance.data.labels[0]),
unit: 'hour',
threshold: 0.75
});
expect(xScale.getValueForPixel(452)).toBeCloseToTime({
value: moment(chartInstance.data.labels[6]),
unit: 'hour'
});
});
it('should get the correct label for a data value', function() {
chartInstance = window.acquireChart({
type: 'line',
data: {
datasets: [{
xAxisID: 'xScale0',
yAxisID: 'yScale0',
data: []
}],
labels: ["2015-01-01T20:00:00", "2015-01-02T21:00:00", "2015-01-03T22:00:00", "2015-01-05T23:00:00", "2015-01-07T03:00", "2015-01-08T10:00", "2015-01-10T12:00"], // days
},
options: {
scales: {
xAxes: [{
id: 'xScale0',
type: 'time',
position: 'bottom'
}],
yAxes: [{
id: 'yScale0',
type: 'linear',
position: 'left'
}]
}
}
});
var xScale = chartInstance.scales.xScale0;
expect(xScale.getLabelForIndex(0, 0)).toBe('2015-01-01T20:00:00');
expect(xScale.getLabelForIndex(6, 0)).toBe('2015-01-10T12:00');
});
});