// Time scale tests
describe('Time scale tests', function() {
+ describe('auto', jasmine.fixture.specs('scale.time'));
+
function createScale(data, options, dimensions) {
var width = (dimensions && dimensions.width) || 400;
var height = (dimensions && dimensions.height) || 50;
expect(defaultConfig.ticks.callback).toEqual(jasmine.any(Function));
});
- describe('when given inputs of different types', 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();
- }
-
- it('should accept labels as strings', function() {
- var mockData = {
- labels: ['2015-01-01T12: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 scaleOptions = Chart.scaleService.getScaleDefaults('time');
- var scale = createScale(mockData, scaleOptions, {width: 1000, height: 200});
- var ticks = getLabels(scale);
-
- // `bounds === 'data'`: first and last ticks removed since outside the data range
- expect(ticks.length).toEqual(44);
- });
-
- it('should accept labels as date objects', function() {
- var mockData = {
- labels: [newDateFromRef(0), newDateFromRef(1), newDateFromRef(2), newDateFromRef(4), newDateFromRef(6), newDateFromRef(7), newDateFromRef(9)], // days
- };
- var scale = createScale(mockData, Chart.scaleService.getScaleDefaults('time'), {width: 1000, height: 200});
- var ticks = getLabels(scale);
-
- // `bounds === 'data'`: first and last ticks removed since outside the data range
- expect(ticks.length).toEqual(44);
- });
-
- it('should accept data as xy points', function() {
- var chart = window.acquireChart({
- type: 'line',
- data: {
- datasets: [{
- xAxisID: 'x',
- 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: {
- x: {
- type: 'time',
- position: 'bottom'
- },
- }
- }
- }, {canvas: {width: 800, height: 200}});
-
- var xScale = chart.scales.x;
- var ticks = getLabels(xScale);
-
- // `bounds === 'data'`: first and last ticks removed since outside the data range
- expect(ticks.length).toEqual(37);
- });
-
- it('should accept data as ty points', function() {
- var chart = window.acquireChart({
- type: 'line',
- data: {
- datasets: [{
- xAxisID: 'x',
- data: [{
- t: newDateFromRef(0),
- y: 1
- }, {
- t: newDateFromRef(1),
- y: 10
- }, {
- t: newDateFromRef(2),
- y: 0
- }, {
- t: newDateFromRef(4),
- y: 5
- }, {
- t: newDateFromRef(6),
- y: 77
- }, {
- t: newDateFromRef(7),
- y: 9
- }, {
- t: newDateFromRef(9),
- y: 5
- }]
- }],
- },
- options: {
- scales: {
- x: {
- type: 'time',
- position: 'bottom'
- },
- }
- }
- }, {canvas: {width: 800, height: 200}});
-
- var tScale = chart.scales.x;
- var ticks = getLabels(tScale);
-
- // `bounds === 'data'`: first and last ticks removed since outside the data range
- expect(ticks.length).toEqual(37);
- });
- });
-
- it('should allow custom time parsers', function() {
- var chart = window.acquireChart({
- type: 'line',
- data: {
- labels: ['foo', 'bar'],
- datasets: [{
- xAxisID: 'x',
- data: [0, 1]
- }],
- },
- options: {
- scales: {
- x: {
- type: 'time',
- position: 'bottom',
- time: {
- unit: 'day',
- round: true,
- parser: function(label) {
- return label === 'foo' ?
- moment('2000/01/02', 'YYYY/MM/DD') :
- moment('2016/05/08', 'YYYY/MM/DD');
- }
- },
- ticks: {
- source: 'labels'
- }
- },
- }
- }
- });
-
- // Counts down because the lines are drawn top to bottom
- var labels = getLabels(chart.scales.x);
-
- // Counts down because the lines are drawn top to bottom
- expect(labels[0]).toBe('Jan 2');
- expect(labels[1]).toBe('May 8');
- });
-
- it('should build ticks using the config unit', function() {
- var mockData = {
- labels: ['2015-01-01T20:00:00', '2015-01-02T21:00:00'], // days
- };
-
- var config = Chart.helpers.clone(Chart.scaleService.getScaleDefaults('time'));
- config.time.unit = 'hour';
-
- var scale = createScale(mockData, config, {width: 2500, height: 200});
- var ticks = getLabels(scale);
-
- expect(ticks).toEqual(['8PM', '9PM', '10PM', '11PM', '12AM', '1AM', '2AM', '3AM', '4AM', '5AM', '6AM', '7AM', '8AM', '9AM', '10AM', '11AM', '12PM', '1PM', '2PM', '3PM', '4PM', '5PM', '6PM', '7PM', '8PM', '9PM']);
- });
-
- it('should build ticks honoring the minUnit', function() {
- var mockData = {
- labels: ['2015-01-01T20:00:00', '2015-01-02T21:00:00'], // days
- };
-
- var config = Chart.helpers.mergeIf({
- bounds: 'ticks',
- time: {
- minUnit: 'day'
- }
- }, Chart.scaleService.getScaleDefaults('time'));
-
- var scale = createScale(mockData, config);
- var ticks = getLabels(scale);
-
- expect(ticks).toEqual(['Jan 1', 'Jan 2', 'Jan 3']);
- });
-
it('should correctly determine the unit', function() {
var date = moment('Jan 01 1990', 'MMM DD YYYY');
var data = [];
expect(scale._unit).toEqual('month');
});
- it('should build ticks based on the appropriate label capacity', function() {
- var mockData = {
- labels: [
- '2012-01-01', '2013-01-01', '2014-01-01', '2015-01-01',
- '2016-01-01', '2017-01-01', '2018-01-01', '2019-01-01'
- ]
- };
-
- var config = Chart.helpers.mergeIf({
- time: {
- unit: 'year'
- }
- }, Chart.scaleService.getScaleDefaults('time'));
-
- var scale = createScale(mockData, config);
- var ticks = getLabels(scale);
-
- expect(ticks).toEqual(['2012', '2013', '2014', '2015', '2016', '2017', '2018', '2019']);
- });
-
- it('should build ticks using the config diff', function() {
- var mockData = {
- labels: ['2015-01-01T20:00:00', '2015-02-02T21:00:00', '2015-02-21T01:00:00'], // days
- };
-
- var config = Chart.helpers.mergeIf({
- bounds: 'ticks',
- time: {
- unit: 'week',
- round: 'week'
- }
- }, Chart.scaleService.getScaleDefaults('time'));
-
- var scale = createScale(mockData, config, {width: 800, height: 200});
- var ticks = getLabels(scale);
-
- // last date is feb 15 because we round to start of week
- expect(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']);
- });
-
- describe('config step size', function() {
- it('should use the stepSize property', function() {
- var mockData = {
- labels: ['2015-01-01T20:00:00', '2015-01-01T21:00:00'],
- };
-
- var config = Chart.helpers.mergeIf({
- bounds: 'ticks',
- time: {
- unit: 'hour',
- stepSize: 2
- }
- }, Chart.scaleService.getScaleDefaults('time'));
-
- var scale = createScale(mockData, config, {width: 2500, height: 200});
- var ticks = getLabels(scale);
-
- expect(ticks).toEqual(['8PM', '10PM']);
- });
- });
-
describe('when specifying limits', function() {
var mockData = {
labels: ['2015-01-01T20:00:00', '2015-01-02T20:00:00', '2015-01-03T20:00:00'],
});
});
- describe('when reverse', function() {
- describe('is "true"', function() {
- beforeEach(function() {
- this.chart = window.acquireChart({
- type: 'line',
- data: {
- labels: ['2017', '2018', '2019', '2020', '2021'],
- datasets: [{data: [0, 1, 2, 3, 4]}]
- },
- options: {
- scales: {
- x: {
- type: 'time',
- reverse: true,
- time: {
- parser: 'YYYY',
- unit: 'year'
- },
- ticks: {
- source: 'labels',
- }
- },
- y: {
- display: false
- }
- }
- }
- });
- });
-
- it ('should reverse the labels', function() {
- var scale = this.chart.scales.x;
- expect(scale.getPixelForValue(moment('2017').valueOf())).toBeCloseToPixel(scale.left + scale.width);
- expect(scale.getPixelForValue(moment('2021').valueOf())).toBeCloseToPixel(scale.left);
- });
-
- it ('should reverse the values for pixels', function() {
- var scale = this.chart.scales.x;
- expect(scale.getValueForPixel(scale.left)).toBeCloseToTime({
- value: moment('2021-01-01T00:00:00'),
- unit: 'hour',
- });
- expect(scale.getValueForPixel(scale.left + scale.width)).toBeCloseToTime({
- value: moment('2017-01-01T00:00:00'),
- unit: 'hour',
- });
- });
-
- it ('should reverse the bars and add offsets if offset is true', function() {
- var chart = this.chart;
- var scale = chart.scales.x;
- var options = chart.options.scales.x;
-
- options.offset = true;
- chart.update();
-
- var numTicks = scale.ticks.length;
- var firstTickInterval = scale.getPixelForTick(numTicks - 2) - scale.getPixelForTick(numTicks - 1);
- var lastTickInterval = scale.getPixelForTick(0) - scale.getPixelForTick(1);
-
- expect(scale.getPixelForValue(moment('2017').valueOf())).toBeCloseToPixel(scale.left + scale.width - lastTickInterval / 2);
- expect(scale.getPixelForValue(moment('2021').valueOf())).toBeCloseToPixel(scale.left + firstTickInterval / 2);
- });
-
- it ('should reverse the values for pixels if offset is true', function() {
- var chart = this.chart;
- var scale = chart.scales.x;
- var options = chart.options.scales.x;
-
- options.offset = true;
- chart.update();
-
- var numTicks = scale.ticks.length;
- var firstTickInterval = scale.getPixelForTick(numTicks - 2) - scale.getPixelForTick(numTicks - 1);
- var lastTickInterval = scale.getPixelForTick(0) - scale.getPixelForTick(1);
-
- expect(scale.getValueForPixel(scale.left + lastTickInterval / 2)).toBeCloseToTime({
- value: moment('2021-01-01T00:00:00'),
- unit: 'year',
- });
- expect(scale.getValueForPixel(scale.left + scale.width - firstTickInterval / 2)).toBeCloseToTime({
- value: moment('2017-01-01T00:00:00'),
- unit: 'hour',
- });
- });
- });
- });
-
- describe('when reverse is "true" and distribution', function() {
- describe('is "series"', function() {
- beforeEach(function() {
- this.chart = window.acquireChart({
- type: 'line',
- data: {
- labels: ['2017', '2019', '2020', '2025', '2042'],
- datasets: [{data: [0, 1, 2, 3, 4, 5]}]
- },
- options: {
- scales: {
- x: {
- type: 'time',
- time: {
- parser: 'YYYY'
- },
- distribution: 'series',
- reverse: true,
- ticks: {
- source: 'labels'
- }
- },
- y: {
- display: false
- }
- }
- }
- });
- });
-
- it ('should reverse the labels and space data out with the same gap, whatever their time values', function() {
- var scale = this.chart.scales.x;
- var start = scale.left;
- var slice = scale.width / 4;
-
- expect(scale.getPixelForValue(moment('2017').valueOf())).toBeCloseToPixel(start + slice * 4);
- expect(scale.getPixelForValue(moment('2019').valueOf())).toBeCloseToPixel(start + slice * 3);
- expect(scale.getPixelForValue(moment('2020').valueOf())).toBeCloseToPixel(start + slice * 2);
- expect(scale.getPixelForValue(moment('2025').valueOf())).toBeCloseToPixel(start + slice);
- expect(scale.getPixelForValue(moment('2042').valueOf())).toBeCloseToPixel(start);
- });
-
- it ('should reverse the labels and should add a step before if scale.min is before the first data', function() {
- var chart = this.chart;
- var scale = chart.scales.x;
- var options = chart.options.scales.x;
-
- options.min = '2012';
- chart.update();
-
- var start = scale.left;
- var slice = scale.width / 5;
-
- expect(scale.getPixelForValue(moment('2017').valueOf())).toBeCloseToPixel(start + slice * 4);
- expect(scale.getPixelForValue(moment('2042').valueOf())).toBeCloseToPixel(start);
- });
-
- it ('should reverse the labels and should add a step after if scale.max is after the last data', function() {
- var chart = this.chart;
- var scale = chart.scales.x;
- var options = chart.options.scales.x;
-
- options.max = '2050';
- chart.update();
-
- var start = scale.left;
- var slice = scale.width / 5;
-
- expect(scale.getPixelForValue(moment('2017').valueOf())).toBeCloseToPixel(start + slice * 5);
- expect(scale.getPixelForValue(moment('2042').valueOf())).toBeCloseToPixel(start + slice);
- });
-
- it ('should reverse the labels and should add steps before and after if scale.min/max are outside the data range', function() {
- var chart = this.chart;
- var scale = chart.scales.x;
- var options = chart.options.scales.x;
-
- options.min = '2012';
- options.max = '2050';
- chart.update();
-
- var start = scale.left;
- var slice = scale.width / 6;
-
- expect(scale.getPixelForValue(moment('2017').valueOf())).toBeCloseToPixel(start + slice * 5);
- expect(scale.getPixelForValue(moment('2042').valueOf())).toBeCloseToPixel(start + slice);
- });
- });
- describe('is "linear"', function() {
- beforeEach(function() {
- this.chart = window.acquireChart({
- type: 'line',
- data: {
- labels: ['2017', '2019', '2020', '2025', '2042'],
- datasets: [{data: [0, 1, 2, 3, 4, 5]}]
- },
- options: {
- scales: {
- x: {
- type: 'time',
- time: {
- parser: 'YYYY'
- },
- distribution: 'linear',
- reverse: true,
- ticks: {
- source: 'labels'
- }
- },
- y: {
- display: false
- }
- }
- }
- });
- });
-
- it ('should reverse the labels and should space data out with a gap relative to their time values', function() {
- var scale = this.chart.scales.x;
- var start = scale.left;
- var slice = scale.width / (2042 - 2017);
-
- expect(scale.getPixelForValue(moment('2017').valueOf())).toBeCloseToPixel(start + slice * (2042 - 2017));
- expect(scale.getPixelForValue(moment('2019').valueOf())).toBeCloseToPixel(start + slice * (2042 - 2019));
- expect(scale.getPixelForValue(moment('2020').valueOf())).toBeCloseToPixel(start + slice * (2042 - 2020));
- expect(scale.getPixelForValue(moment('2025').valueOf())).toBeCloseToPixel(start + slice * (2042 - 2025));
- expect(scale.getPixelForValue(moment('2042').valueOf())).toBeCloseToPixel(start);
- });
-
- it ('should reverse the labels and should take in account scale min and max if outside the ticks range', function() {
- var chart = this.chart;
- var scale = chart.scales.x;
- var options = chart.options.scales.x;
-
- options.min = '2012';
- options.max = '2050';
- chart.update();
-
- var start = scale.left;
- var slice = scale.width / (2050 - 2012);
-
- expect(scale.getPixelForValue(moment('2017').valueOf())).toBeCloseToPixel(start + slice * (2050 - 2017));
- expect(scale.getPixelForValue(moment('2019').valueOf())).toBeCloseToPixel(start + slice * (2050 - 2019));
- expect(scale.getPixelForValue(moment('2020').valueOf())).toBeCloseToPixel(start + slice * (2050 - 2020));
- expect(scale.getPixelForValue(moment('2025').valueOf())).toBeCloseToPixel(start + slice * (2050 - 2025));
- expect(scale.getPixelForValue(moment('2042').valueOf())).toBeCloseToPixel(start + slice * (2050 - 2042));
- });
- });
- });
-
- describe('labels', function() {
- it('should read labels from scale / xLabels / yLabels', function() {
- var timeOpts = {
- parser: 'YYYY',
- unit: 'year',
- displayFormats: {
- year: 'YYYY'
- }
- };
- var chart = window.acquireChart({
- type: 'line',
- data: {
- labels: ['1975', '1976', '1977'],
- xLabels: ['1985', '1986', '1987'],
- yLabels: ['1995', '1996', '1997']
- },
- options: {
- scales: {
- x: {
- type: 'time',
- labels: ['2015', '2016', '2017'],
- time: timeOpts
- },
- x2: {
- type: 'time',
- position: 'bottom',
- time: timeOpts
- },
- y: {
- type: 'time',
- time: timeOpts
- },
- y2: {
- position: 'left',
- type: 'time',
- labels: ['2005', '2006', '2007'],
- time: timeOpts
- }
- }
- }
- });
-
- expect(getLabels(chart.scales.x)).toEqual(['2015', '2016', '2017']);
- expect(getLabels(chart.scales.x2)).toEqual(['1985', '1986', '1987']);
- expect(getLabels(chart.scales.y)).toEqual(['1995', '1996', '1997']);
- expect(getLabels(chart.scales.y2)).toEqual(['2005', '2006', '2007']);
- });
- });
-
- it('should handle autoskip with major and minor ticks', function() {
- var date = moment('Jan 01 1990', 'MMM DD YYYY');
- var data = [];
- for (var i = 0; i < 60; i++) {
- data.push({x: date.valueOf(), y: Math.random()});
- date = date.clone().add(1, 'month');
- }
-
- var chart = window.acquireChart({
- type: 'line',
- data: {
- datasets: [{
- xAxisID: 'x',
- data: data
- }],
- },
- options: {
- scales: {
- x: {
- type: 'time',
- ticks: {
- major: {
- enabled: true
- },
- source: 'data',
- autoSkip: true
- }
- },
- }
- }
- });
-
- var scale = chart.scales.x;
-
- var labels = scale.ticks.map(function(t) {
- return t.label;
- });
- expect(labels).toEqual([
- '1990', 'Apr 1990', 'Jul 1990', 'Oct 1990',
- '1991', 'Apr 1991', 'Jul 1991', 'Oct 1991',
- '1992', 'Apr 1992', 'Jul 1992', 'Oct 1992',
- '1993', 'Apr 1993', 'Jul 1993', 'Oct 1993',
- '1994', 'Apr 1994', 'Jul 1994', 'Oct 1994']);
- });
-
describe('Deprecations', function() {
describe('options.time.displayFormats', function() {
it('should generate defaults from adapter presets', function() {