scale.time.tests.js 36 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290129112921293129412951296129712981299130013011302130313041305130613071308130913101311131213131314131513161317131813191320
  1. // Time scale tests
  2. describe('Time scale tests', function() {
  3. function createScale(data, options) {
  4. var scaleID = 'myScale';
  5. var mockContext = window.createMockContext();
  6. var Constructor = Chart.scaleService.getScaleConstructor('time');
  7. var scale = new Constructor({
  8. ctx: mockContext,
  9. options: options,
  10. chart: {
  11. data: data
  12. },
  13. id: scaleID
  14. });
  15. scale.update(400, 50);
  16. return scale;
  17. }
  18. function getTicksLabels(scale) {
  19. return scale.ticks;
  20. }
  21. beforeEach(function() {
  22. // Need a time matcher for getValueFromPixel
  23. jasmine.addMatchers({
  24. toBeCloseToTime: function() {
  25. return {
  26. compare: function(actual, expected) {
  27. var result = false;
  28. var diff = actual.diff(expected.value, expected.unit, true);
  29. result = Math.abs(diff) < (expected.threshold !== undefined ? expected.threshold : 0.01);
  30. return {
  31. pass: result
  32. };
  33. }
  34. };
  35. }
  36. });
  37. });
  38. it('should load moment.js as a dependency', function() {
  39. expect(window.moment).not.toBe(undefined);
  40. });
  41. it('should register the constructor with the scale service', function() {
  42. var Constructor = Chart.scaleService.getScaleConstructor('time');
  43. expect(Constructor).not.toBe(undefined);
  44. expect(typeof Constructor).toBe('function');
  45. });
  46. it('should have the correct default config', function() {
  47. var defaultConfig = Chart.scaleService.getScaleDefaults('time');
  48. expect(defaultConfig).toEqual({
  49. display: true,
  50. gridLines: {
  51. color: 'rgba(0, 0, 0, 0.1)',
  52. drawBorder: true,
  53. drawOnChartArea: true,
  54. drawTicks: true,
  55. tickMarkLength: 10,
  56. lineWidth: 1,
  57. offsetGridLines: false,
  58. display: true,
  59. zeroLineColor: 'rgba(0,0,0,0.25)',
  60. zeroLineWidth: 1,
  61. zeroLineBorderDash: [],
  62. zeroLineBorderDashOffset: 0.0,
  63. borderDash: [],
  64. borderDashOffset: 0.0
  65. },
  66. position: 'bottom',
  67. offset: false,
  68. scaleLabel: Chart.defaults.scale.scaleLabel,
  69. bounds: 'data',
  70. distribution: 'linear',
  71. ticks: {
  72. beginAtZero: false,
  73. minRotation: 0,
  74. maxRotation: 50,
  75. mirror: false,
  76. source: 'auto',
  77. padding: 0,
  78. reverse: false,
  79. display: true,
  80. callback: defaultConfig.ticks.callback, // make this nicer, then check explicitly below,
  81. autoSkip: false,
  82. autoSkipPadding: 0,
  83. labelOffset: 0,
  84. minor: {},
  85. major: {
  86. enabled: false
  87. },
  88. },
  89. time: {
  90. parser: false,
  91. format: false,
  92. unit: false,
  93. round: false,
  94. isoWeekday: false,
  95. displayFormat: false,
  96. minUnit: 'millisecond',
  97. displayFormats: {
  98. millisecond: 'h:mm:ss.SSS a', // 11:20:01.123 AM
  99. second: 'h:mm:ss a', // 11:20:01 AM
  100. minute: 'h:mm a', // 11:20 AM
  101. hour: 'hA', // 5PM
  102. day: 'MMM D', // Sep 4
  103. week: 'll', // Week 46, or maybe "[W]WW - YYYY" ?
  104. month: 'MMM YYYY', // Sept 2015
  105. quarter: '[Q]Q - YYYY', // Q3
  106. year: 'YYYY' // 2015
  107. },
  108. }
  109. });
  110. // Is this actually a function
  111. expect(defaultConfig.ticks.callback).toEqual(jasmine.any(Function));
  112. });
  113. describe('when given inputs of different types', function() {
  114. // Helper to build date objects
  115. function newDateFromRef(days) {
  116. return moment('01/01/2015 12:00', 'DD/MM/YYYY HH:mm').add(days, 'd').toDate();
  117. }
  118. it('should accept labels as strings', function() {
  119. var mockData = {
  120. 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
  121. };
  122. var scaleOptions = Chart.scaleService.getScaleDefaults('time');
  123. var scale = createScale(mockData, scaleOptions);
  124. scale.update(1000, 200);
  125. var ticks = getTicksLabels(scale);
  126. // `bounds === 'data'`: first and last ticks removed since outside the data range
  127. expect(ticks).toEqual(['Jan 2', 'Jan 3', 'Jan 4', 'Jan 5', 'Jan 6', 'Jan 7', 'Jan 8', 'Jan 9', 'Jan 10']);
  128. });
  129. it('should accept labels as date objects', function() {
  130. var mockData = {
  131. labels: [newDateFromRef(0), newDateFromRef(1), newDateFromRef(2), newDateFromRef(4), newDateFromRef(6), newDateFromRef(7), newDateFromRef(9)], // days
  132. };
  133. var scale = createScale(mockData, Chart.scaleService.getScaleDefaults('time'));
  134. scale.update(1000, 200);
  135. var ticks = getTicksLabels(scale);
  136. // `bounds === 'data'`: first and last ticks removed since outside the data range
  137. expect(ticks).toEqual(['Jan 2', 'Jan 3', 'Jan 4', 'Jan 5', 'Jan 6', 'Jan 7', 'Jan 8', 'Jan 9', 'Jan 10']);
  138. });
  139. it('should accept data as xy points', function() {
  140. var chart = window.acquireChart({
  141. type: 'line',
  142. data: {
  143. datasets: [{
  144. xAxisID: 'xScale0',
  145. data: [{
  146. x: newDateFromRef(0),
  147. y: 1
  148. }, {
  149. x: newDateFromRef(1),
  150. y: 10
  151. }, {
  152. x: newDateFromRef(2),
  153. y: 0
  154. }, {
  155. x: newDateFromRef(4),
  156. y: 5
  157. }, {
  158. x: newDateFromRef(6),
  159. y: 77
  160. }, {
  161. x: newDateFromRef(7),
  162. y: 9
  163. }, {
  164. x: newDateFromRef(9),
  165. y: 5
  166. }]
  167. }],
  168. },
  169. options: {
  170. scales: {
  171. xAxes: [{
  172. id: 'xScale0',
  173. type: 'time',
  174. position: 'bottom'
  175. }],
  176. }
  177. }
  178. });
  179. var xScale = chart.scales.xScale0;
  180. xScale.update(800, 200);
  181. var ticks = getTicksLabels(xScale);
  182. // `bounds === 'data'`: first and last ticks removed since outside the data range
  183. expect(ticks).toEqual(['Jan 2', 'Jan 3', 'Jan 4', 'Jan 5', 'Jan 6', 'Jan 7', 'Jan 8', 'Jan 9', 'Jan 10']);
  184. });
  185. it('should accept data as ty points', function() {
  186. var chart = window.acquireChart({
  187. type: 'line',
  188. data: {
  189. datasets: [{
  190. xAxisID: 'tScale0',
  191. data: [{
  192. t: newDateFromRef(0),
  193. y: 1
  194. }, {
  195. t: newDateFromRef(1),
  196. y: 10
  197. }, {
  198. t: newDateFromRef(2),
  199. y: 0
  200. }, {
  201. t: newDateFromRef(4),
  202. y: 5
  203. }, {
  204. t: newDateFromRef(6),
  205. y: 77
  206. }, {
  207. t: newDateFromRef(7),
  208. y: 9
  209. }, {
  210. t: newDateFromRef(9),
  211. y: 5
  212. }]
  213. }],
  214. },
  215. options: {
  216. scales: {
  217. xAxes: [{
  218. id: 'tScale0',
  219. type: 'time',
  220. position: 'bottom'
  221. }],
  222. }
  223. }
  224. });
  225. var tScale = chart.scales.tScale0;
  226. tScale.update(800, 200);
  227. var ticks = getTicksLabels(tScale);
  228. // `bounds === 'data'`: first and last ticks removed since outside the data range
  229. expect(ticks).toEqual(['Jan 2', 'Jan 3', 'Jan 4', 'Jan 5', 'Jan 6', 'Jan 7', 'Jan 8', 'Jan 9', 'Jan 10']);
  230. });
  231. });
  232. it('should allow custom time parsers', function() {
  233. var chart = window.acquireChart({
  234. type: 'line',
  235. data: {
  236. labels: ['foo', 'bar'],
  237. datasets: [{
  238. xAxisID: 'xScale0',
  239. data: [0, 1]
  240. }],
  241. },
  242. options: {
  243. scales: {
  244. xAxes: [{
  245. id: 'xScale0',
  246. type: 'time',
  247. position: 'bottom',
  248. time: {
  249. unit: 'day',
  250. round: true,
  251. parser: function(label) {
  252. return label === 'foo' ?
  253. moment('2000/01/02', 'YYYY/MM/DD') :
  254. moment('2016/05/08', 'YYYY/MM/DD');
  255. }
  256. },
  257. ticks: {
  258. source: 'labels'
  259. }
  260. }],
  261. }
  262. }
  263. });
  264. // Counts down because the lines are drawn top to bottom
  265. var xScale = chart.scales.xScale0;
  266. // Counts down because the lines are drawn top to bottom
  267. expect(xScale.ticks[0]).toBe('Jan 2');
  268. expect(xScale.ticks[1]).toBe('May 8');
  269. });
  270. it('should build ticks using the config unit', function() {
  271. var mockData = {
  272. labels: ['2015-01-01T20:00:00', '2015-01-02T21:00:00'], // days
  273. };
  274. var config = Chart.helpers.clone(Chart.scaleService.getScaleDefaults('time'));
  275. config.time.unit = 'hour';
  276. var scale = createScale(mockData, config);
  277. scale.update(2500, 200);
  278. var ticks = getTicksLabels(scale);
  279. 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']);
  280. });
  281. it('build ticks honoring the minUnit', function() {
  282. var mockData = {
  283. labels: ['2015-01-01T20:00:00', '2015-01-02T21:00:00'], // days
  284. };
  285. var config = Chart.helpers.mergeIf({
  286. bounds: 'ticks',
  287. time: {
  288. minUnit: 'day'
  289. }
  290. }, Chart.scaleService.getScaleDefaults('time'));
  291. var scale = createScale(mockData, config);
  292. var ticks = getTicksLabels(scale);
  293. expect(ticks).toEqual(['Jan 1', 'Jan 2', 'Jan 3']);
  294. });
  295. it('should build ticks using the config diff', function() {
  296. var mockData = {
  297. labels: ['2015-01-01T20:00:00', '2015-02-02T21:00:00', '2015-02-21T01:00:00'], // days
  298. };
  299. var config = Chart.helpers.mergeIf({
  300. bounds: 'ticks',
  301. time: {
  302. unit: 'week',
  303. round: 'week'
  304. }
  305. }, Chart.scaleService.getScaleDefaults('time'));
  306. var scale = createScale(mockData, config);
  307. scale.update(800, 200);
  308. var ticks = getTicksLabels(scale);
  309. // last date is feb 15 because we round to start of week
  310. 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']);
  311. });
  312. describe('config step size', function() {
  313. it('should use the stepSize property', function() {
  314. var mockData = {
  315. labels: ['2015-01-01T20:00:00', '2015-01-01T21:00:00'],
  316. };
  317. var config = Chart.helpers.mergeIf({
  318. bounds: 'ticks',
  319. time: {
  320. unit: 'hour',
  321. stepSize: 2
  322. }
  323. }, Chart.scaleService.getScaleDefaults('time'));
  324. var scale = createScale(mockData, config);
  325. scale.update(2500, 200);
  326. var ticks = getTicksLabels(scale);
  327. expect(ticks).toEqual(['8PM', '10PM']);
  328. });
  329. });
  330. describe('when specifying limits', function() {
  331. var mockData = {
  332. labels: ['2015-01-01T20:00:00', '2015-01-02T20:00:00', '2015-01-03T20:00:00'],
  333. };
  334. var config;
  335. beforeEach(function() {
  336. config = Chart.helpers.clone(Chart.scaleService.getScaleDefaults('time'));
  337. config.ticks.source = 'labels';
  338. config.time.unit = 'day';
  339. });
  340. it('should use the min option when less than first label for building ticks', function() {
  341. config.time.min = '2014-12-29T04:00:00';
  342. var scale = createScale(mockData, config);
  343. expect(scale.ticks[0]).toEqual('Jan 1');
  344. });
  345. it('should use the min option when greater than first label for building ticks', function() {
  346. config.time.min = '2015-01-02T04:00:00';
  347. var scale = createScale(mockData, config);
  348. expect(scale.ticks[0]).toEqual('Jan 2');
  349. });
  350. it('should use the max option when greater than last label for building ticks', function() {
  351. config.time.max = '2015-01-05T06:00:00';
  352. var scale = createScale(mockData, config);
  353. expect(scale.ticks[scale.ticks.length - 1]).toEqual('Jan 3');
  354. });
  355. it('should use the max option when less than last label for building ticks', function() {
  356. config.time.max = '2015-01-02T23:00:00';
  357. var scale = createScale(mockData, config);
  358. expect(scale.ticks[scale.ticks.length - 1]).toEqual('Jan 2');
  359. });
  360. });
  361. it('should use the isoWeekday option', function() {
  362. var mockData = {
  363. labels: [
  364. '2015-01-01T20:00:00', // Thursday
  365. '2015-01-02T20:00:00', // Friday
  366. '2015-01-03T20:00:00' // Saturday
  367. ]
  368. };
  369. var config = Chart.helpers.mergeIf({
  370. bounds: 'ticks',
  371. time: {
  372. unit: 'week',
  373. isoWeekday: 3 // Wednesday
  374. }
  375. }, Chart.scaleService.getScaleDefaults('time'));
  376. var scale = createScale(mockData, config);
  377. var ticks = getTicksLabels(scale);
  378. expect(ticks).toEqual(['Dec 31, 2014', 'Jan 7, 2015']);
  379. });
  380. describe('when rendering several days', function() {
  381. beforeEach(function() {
  382. this.chart = window.acquireChart({
  383. type: 'line',
  384. data: {
  385. datasets: [{
  386. xAxisID: 'xScale0',
  387. data: []
  388. }],
  389. labels: [
  390. '2015-01-01T20:00:00',
  391. '2015-01-02T21:00:00',
  392. '2015-01-03T22:00:00',
  393. '2015-01-05T23:00:00',
  394. '2015-01-07T03:00',
  395. '2015-01-08T10:00',
  396. '2015-01-10T12:00'
  397. ]
  398. },
  399. options: {
  400. scales: {
  401. xAxes: [{
  402. id: 'xScale0',
  403. type: 'time',
  404. position: 'bottom'
  405. }],
  406. }
  407. }
  408. });
  409. this.scale = this.chart.scales.xScale0;
  410. });
  411. it('should be bounded by the nearest week beginnings', function() {
  412. var chart = this.chart;
  413. var scale = this.scale;
  414. expect(scale.getValueForPixel(scale.left)).toBeGreaterThan(moment(chart.data.labels[0]).startOf('week'));
  415. expect(scale.getValueForPixel(scale.right)).toBeLessThan(moment(chart.data.labels[chart.data.labels.length - 1]).add(1, 'week').endOf('week'));
  416. });
  417. it('should convert between screen coordinates and times', function() {
  418. var chart = this.chart;
  419. var scale = this.scale;
  420. var timeRange = moment(scale.max).valueOf() - moment(scale.min).valueOf();
  421. var msPerPix = timeRange / scale.width;
  422. var firstPointOffsetMs = moment(chart.config.data.labels[0]).valueOf() - scale.min;
  423. var firstPointPixel = scale.left + firstPointOffsetMs / msPerPix;
  424. var lastPointOffsetMs = moment(chart.config.data.labels[chart.config.data.labels.length - 1]).valueOf() - scale.min;
  425. var lastPointPixel = scale.left + lastPointOffsetMs / msPerPix;
  426. expect(scale.getPixelForValue('', 0, 0)).toBeCloseToPixel(firstPointPixel);
  427. expect(scale.getPixelForValue(chart.data.labels[0])).toBeCloseToPixel(firstPointPixel);
  428. expect(scale.getValueForPixel(firstPointPixel)).toBeCloseToTime({
  429. value: moment(chart.data.labels[0]),
  430. unit: 'hour',
  431. });
  432. expect(scale.getPixelForValue('', 6, 0)).toBeCloseToPixel(lastPointPixel);
  433. expect(scale.getValueForPixel(lastPointPixel)).toBeCloseToTime({
  434. value: moment(chart.data.labels[6]),
  435. unit: 'hour'
  436. });
  437. });
  438. });
  439. describe('when rendering several years', function() {
  440. beforeEach(function() {
  441. this.chart = window.acquireChart({
  442. type: 'line',
  443. data: {
  444. labels: ['2005-07-04', '2017-01-20'],
  445. },
  446. options: {
  447. scales: {
  448. xAxes: [{
  449. id: 'xScale0',
  450. type: 'time',
  451. bounds: 'ticks',
  452. position: 'bottom'
  453. }],
  454. }
  455. }
  456. });
  457. this.scale = this.chart.scales.xScale0;
  458. this.scale.update(800, 200);
  459. });
  460. it('should be bounded by nearest step\'s year start and end', function() {
  461. var scale = this.scale;
  462. var ticks = scale.getTicks();
  463. var step = ticks[1].value - ticks[0].value;
  464. var stepsAmount = Math.floor((scale.max - scale.min) / step);
  465. expect(scale.getValueForPixel(scale.left)).toBeCloseToTime({
  466. value: moment(scale.min).startOf('year'),
  467. unit: 'hour',
  468. });
  469. expect(scale.getValueForPixel(scale.right)).toBeCloseToTime({
  470. value: moment(scale.min + step * stepsAmount).endOf('year'),
  471. unit: 'hour',
  472. });
  473. });
  474. it('should build the correct ticks', function() {
  475. // Where 'correct' is a two year spacing.
  476. expect(getTicksLabels(this.scale)).toEqual(['2005', '2007', '2009', '2011', '2013', '2015', '2017', '2019']);
  477. });
  478. it('should have ticks with accurate labels', function() {
  479. var scale = this.scale;
  480. var ticks = scale.getTicks();
  481. var pixelsPerYear = scale.width / 14;
  482. for (var i = 0; i < ticks.length - 1; i++) {
  483. var offset = 2 * pixelsPerYear * i;
  484. expect(scale.getValueForPixel(scale.left + offset)).toBeCloseToTime({
  485. value: moment(ticks[i].label + '-01-01'),
  486. unit: 'day',
  487. threshold: 0.5,
  488. });
  489. }
  490. });
  491. });
  492. it('should get the correct label for a data value', function() {
  493. var chart = window.acquireChart({
  494. type: 'line',
  495. data: {
  496. datasets: [{
  497. xAxisID: 'xScale0',
  498. data: [null, 10, 3]
  499. }],
  500. 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
  501. },
  502. options: {
  503. scales: {
  504. xAxes: [{
  505. id: 'xScale0',
  506. type: 'time',
  507. position: 'bottom'
  508. }],
  509. }
  510. }
  511. });
  512. var xScale = chart.scales.xScale0;
  513. expect(xScale.getLabelForIndex(0, 0)).toBeTruthy();
  514. expect(xScale.getLabelForIndex(0, 0)).toBe('2015-01-01T20:00:00');
  515. expect(xScale.getLabelForIndex(6, 0)).toBe('2015-01-10T12:00');
  516. });
  517. it('should get the correct label when time is specified as a string', function() {
  518. var chart = window.acquireChart({
  519. type: 'line',
  520. data: {
  521. datasets: [{
  522. xAxisID: 'xScale0',
  523. data: [{t: '2015-01-01T20:00:00', y: 10}, {t: '2015-01-02T21:00:00', y: 3}]
  524. }],
  525. },
  526. options: {
  527. scales: {
  528. xAxes: [{
  529. id: 'xScale0',
  530. type: 'time',
  531. position: 'bottom'
  532. }],
  533. }
  534. }
  535. });
  536. var xScale = chart.scales.xScale0;
  537. expect(xScale.getLabelForIndex(0, 0)).toBeTruthy();
  538. expect(xScale.getLabelForIndex(0, 0)).toBe('2015-01-01T20:00:00');
  539. });
  540. it('should get the correct label for a timestamp with milliseconds', function() {
  541. var chart = window.acquireChart({
  542. type: 'line',
  543. data: {
  544. datasets: [{
  545. xAxisID: 'xScale0',
  546. data: [
  547. {t: +new Date('2018-01-08 05:14:23.234'), y: 10},
  548. {t: +new Date('2018-01-09 06:17:43.426'), y: 3}
  549. ]
  550. }],
  551. },
  552. options: {
  553. scales: {
  554. xAxes: [{
  555. id: 'xScale0',
  556. type: 'time',
  557. position: 'bottom'
  558. }],
  559. }
  560. }
  561. });
  562. var xScale = chart.scales.xScale0;
  563. var label = xScale.getLabelForIndex(0, 0);
  564. expect(label).toEqual('Jan 8, 2018 5:14:23.234 am');
  565. });
  566. it('should get the correct label for a timestamp with time', function() {
  567. var chart = window.acquireChart({
  568. type: 'line',
  569. data: {
  570. datasets: [{
  571. xAxisID: 'xScale0',
  572. data: [
  573. {t: +new Date('2018-01-08 05:14:23'), y: 10},
  574. {t: +new Date('2018-01-09 06:17:43'), y: 3}
  575. ]
  576. }],
  577. },
  578. options: {
  579. scales: {
  580. xAxes: [{
  581. id: 'xScale0',
  582. type: 'time',
  583. position: 'bottom'
  584. }],
  585. }
  586. }
  587. });
  588. var xScale = chart.scales.xScale0;
  589. var label = xScale.getLabelForIndex(0, 0);
  590. expect(label).toEqual('Jan 8, 2018 5:14:23 am');
  591. });
  592. it('should get the correct label for a timestamp representing a date', function() {
  593. var chart = window.acquireChart({
  594. type: 'line',
  595. data: {
  596. datasets: [{
  597. xAxisID: 'xScale0',
  598. data: [
  599. {t: +new Date('2018-01-08 00:00:00'), y: 10},
  600. {t: +new Date('2018-01-09 00:00:00'), y: 3}
  601. ]
  602. }],
  603. },
  604. options: {
  605. scales: {
  606. xAxes: [{
  607. id: 'xScale0',
  608. type: 'time',
  609. position: 'bottom'
  610. }],
  611. }
  612. }
  613. });
  614. var xScale = chart.scales.xScale0;
  615. var label = xScale.getLabelForIndex(0, 0);
  616. expect(label).toEqual('Jan 8, 2018');
  617. });
  618. it('should get the correct pixel for only one data in the dataset', function() {
  619. var chart = window.acquireChart({
  620. type: 'line',
  621. data: {
  622. labels: ['2016-05-27'],
  623. datasets: [{
  624. xAxisID: 'xScale0',
  625. data: [5]
  626. }]
  627. },
  628. options: {
  629. scales: {
  630. xAxes: [{
  631. id: 'xScale0',
  632. display: true,
  633. type: 'time'
  634. }]
  635. }
  636. }
  637. });
  638. var xScale = chart.scales.xScale0;
  639. var pixel = xScale.getPixelForValue('', 0, 0);
  640. expect(xScale.getValueForPixel(pixel).valueOf()).toEqual(moment(chart.data.labels[0]).valueOf());
  641. });
  642. it('does not create a negative width chart when hidden', function() {
  643. var chart = window.acquireChart({
  644. type: 'line',
  645. data: {
  646. datasets: [{
  647. data: []
  648. }]
  649. },
  650. options: {
  651. scales: {
  652. xAxes: [{
  653. type: 'time',
  654. time: {
  655. min: moment().subtract(1, 'months'),
  656. max: moment(),
  657. }
  658. }],
  659. },
  660. responsive: true,
  661. },
  662. }, {
  663. wrapper: {
  664. style: 'display: none',
  665. },
  666. });
  667. expect(chart.scales['y-axis-0'].width).toEqual(0);
  668. expect(chart.scales['y-axis-0'].maxWidth).toEqual(0);
  669. expect(chart.width).toEqual(0);
  670. });
  671. describe('when ticks.source', function() {
  672. describe('is "labels"', function() {
  673. beforeEach(function() {
  674. this.chart = window.acquireChart({
  675. type: 'line',
  676. data: {
  677. labels: ['2017', '2019', '2020', '2025', '2042'],
  678. datasets: [{data: [0, 1, 2, 3, 4, 5]}]
  679. },
  680. options: {
  681. scales: {
  682. xAxes: [{
  683. id: 'x',
  684. type: 'time',
  685. time: {
  686. parser: 'YYYY'
  687. },
  688. ticks: {
  689. source: 'labels'
  690. }
  691. }]
  692. }
  693. }
  694. });
  695. });
  696. it ('should generate ticks from "data.labels"', function() {
  697. var scale = this.chart.scales.x;
  698. expect(scale.min).toEqual(+moment('2017', 'YYYY'));
  699. expect(scale.max).toEqual(+moment('2042', 'YYYY'));
  700. expect(getTicksLabels(scale)).toEqual([
  701. '2017', '2019', '2020', '2025', '2042']);
  702. });
  703. it ('should not add ticks for min and max if they extend the labels range', function() {
  704. var chart = this.chart;
  705. var scale = chart.scales.x;
  706. var options = chart.options.scales.xAxes[0];
  707. options.time.min = '2012';
  708. options.time.max = '2051';
  709. chart.update();
  710. expect(scale.min).toEqual(+moment('2012', 'YYYY'));
  711. expect(scale.max).toEqual(+moment('2051', 'YYYY'));
  712. expect(getTicksLabels(scale)).toEqual([
  713. '2017', '2019', '2020', '2025', '2042']);
  714. });
  715. it ('should not duplicate ticks if min and max are the labels limits', function() {
  716. var chart = this.chart;
  717. var scale = chart.scales.x;
  718. var options = chart.options.scales.xAxes[0];
  719. options.time.min = '2017';
  720. options.time.max = '2042';
  721. chart.update();
  722. expect(scale.min).toEqual(+moment('2017', 'YYYY'));
  723. expect(scale.max).toEqual(+moment('2042', 'YYYY'));
  724. expect(getTicksLabels(scale)).toEqual([
  725. '2017', '2019', '2020', '2025', '2042']);
  726. });
  727. it ('should correctly handle empty `data.labels` using "day" if `time.unit` is undefined`', function() {
  728. var chart = this.chart;
  729. var scale = chart.scales.x;
  730. chart.data.labels = [];
  731. chart.update();
  732. expect(scale.min).toEqual(+moment().startOf('day'));
  733. expect(scale.max).toEqual(+moment().endOf('day') + 1);
  734. expect(getTicksLabels(scale)).toEqual([]);
  735. });
  736. it ('should correctly handle empty `data.labels` using `time.unit`', function() {
  737. var chart = this.chart;
  738. var scale = chart.scales.x;
  739. var options = chart.options.scales.xAxes[0];
  740. options.time.unit = 'year';
  741. chart.data.labels = [];
  742. chart.update();
  743. expect(scale.min).toEqual(+moment().startOf('year'));
  744. expect(scale.max).toEqual(+moment().endOf('year') + 1);
  745. expect(getTicksLabels(scale)).toEqual([]);
  746. });
  747. });
  748. describe('is "data"', function() {
  749. beforeEach(function() {
  750. this.chart = window.acquireChart({
  751. type: 'line',
  752. data: {
  753. labels: ['2017', '2019', '2020', '2025', '2042'],
  754. datasets: [
  755. {data: [0, 1, 2, 3, 4, 5]},
  756. {data: [
  757. {t: '2018', y: 6},
  758. {t: '2020', y: 7},
  759. {t: '2043', y: 8}
  760. ]}
  761. ]
  762. },
  763. options: {
  764. scales: {
  765. xAxes: [{
  766. id: 'x',
  767. type: 'time',
  768. time: {
  769. parser: 'YYYY'
  770. },
  771. ticks: {
  772. source: 'data'
  773. }
  774. }]
  775. }
  776. }
  777. });
  778. });
  779. it ('should generate ticks from "datasets.data"', function() {
  780. var scale = this.chart.scales.x;
  781. expect(scale.min).toEqual(+moment('2017', 'YYYY'));
  782. expect(scale.max).toEqual(+moment('2043', 'YYYY'));
  783. expect(getTicksLabels(scale)).toEqual([
  784. '2017', '2018', '2019', '2020', '2025', '2042', '2043']);
  785. });
  786. it ('should not add ticks for min and max if they extend the labels range', function() {
  787. var chart = this.chart;
  788. var scale = chart.scales.x;
  789. var options = chart.options.scales.xAxes[0];
  790. options.time.min = '2012';
  791. options.time.max = '2051';
  792. chart.update();
  793. expect(scale.min).toEqual(+moment('2012', 'YYYY'));
  794. expect(scale.max).toEqual(+moment('2051', 'YYYY'));
  795. expect(getTicksLabels(scale)).toEqual([
  796. '2017', '2018', '2019', '2020', '2025', '2042', '2043']);
  797. });
  798. it ('should not duplicate ticks if min and max are the labels limits', function() {
  799. var chart = this.chart;
  800. var scale = chart.scales.x;
  801. var options = chart.options.scales.xAxes[0];
  802. options.time.min = '2017';
  803. options.time.max = '2043';
  804. chart.update();
  805. expect(scale.min).toEqual(+moment('2017', 'YYYY'));
  806. expect(scale.max).toEqual(+moment('2043', 'YYYY'));
  807. expect(getTicksLabels(scale)).toEqual([
  808. '2017', '2018', '2019', '2020', '2025', '2042', '2043']);
  809. });
  810. it ('should correctly handle empty `data.labels` using "day" if `time.unit` is undefined`', function() {
  811. var chart = this.chart;
  812. var scale = chart.scales.x;
  813. chart.data.labels = [];
  814. chart.update();
  815. expect(scale.min).toEqual(+moment('2018', 'YYYY'));
  816. expect(scale.max).toEqual(+moment('2043', 'YYYY'));
  817. expect(getTicksLabels(scale)).toEqual([
  818. '2018', '2020', '2043']);
  819. });
  820. it ('should correctly handle empty `data.labels` and hidden datasets using `time.unit`', function() {
  821. var chart = this.chart;
  822. var scale = chart.scales.x;
  823. var options = chart.options.scales.xAxes[0];
  824. options.time.unit = 'year';
  825. chart.data.labels = [];
  826. var meta = chart.getDatasetMeta(1);
  827. meta.hidden = true;
  828. chart.update();
  829. expect(scale.min).toEqual(+moment().startOf('year'));
  830. expect(scale.max).toEqual(+moment().endOf('year') + 1);
  831. expect(getTicksLabels(scale)).toEqual([]);
  832. });
  833. });
  834. });
  835. describe('when distribution', function() {
  836. describe('is "series"', function() {
  837. beforeEach(function() {
  838. this.chart = window.acquireChart({
  839. type: 'line',
  840. data: {
  841. labels: ['2017', '2019', '2020', '2025', '2042'],
  842. datasets: [{data: [0, 1, 2, 3, 4, 5]}]
  843. },
  844. options: {
  845. scales: {
  846. xAxes: [{
  847. id: 'x',
  848. type: 'time',
  849. time: {
  850. parser: 'YYYY'
  851. },
  852. distribution: 'series',
  853. ticks: {
  854. source: 'labels'
  855. }
  856. }],
  857. yAxes: [{
  858. display: false
  859. }]
  860. }
  861. }
  862. });
  863. });
  864. it ('should space data out with the same gap, whatever their time values', function() {
  865. var scale = this.chart.scales.x;
  866. var start = scale.left;
  867. var slice = scale.width / 4;
  868. expect(scale.getPixelForValue('2017')).toBeCloseToPixel(start);
  869. expect(scale.getPixelForValue('2019')).toBeCloseToPixel(start + slice);
  870. expect(scale.getPixelForValue('2020')).toBeCloseToPixel(start + slice * 2);
  871. expect(scale.getPixelForValue('2025')).toBeCloseToPixel(start + slice * 3);
  872. expect(scale.getPixelForValue('2042')).toBeCloseToPixel(start + slice * 4);
  873. });
  874. it ('should add a step before if scale.min is before the first data', function() {
  875. var chart = this.chart;
  876. var scale = chart.scales.x;
  877. var options = chart.options.scales.xAxes[0];
  878. options.time.min = '2012';
  879. chart.update();
  880. var start = scale.left;
  881. var slice = scale.width / 5;
  882. expect(scale.getPixelForValue('2017')).toBeCloseToPixel(start + slice);
  883. expect(scale.getPixelForValue('2042')).toBeCloseToPixel(start + slice * 5);
  884. });
  885. it ('should add a step after if scale.max is after the last data', function() {
  886. var chart = this.chart;
  887. var scale = chart.scales.x;
  888. var options = chart.options.scales.xAxes[0];
  889. options.time.max = '2050';
  890. chart.update();
  891. var start = scale.left;
  892. var slice = scale.width / 5;
  893. expect(scale.getPixelForValue('2017')).toBeCloseToPixel(start);
  894. expect(scale.getPixelForValue('2042')).toBeCloseToPixel(start + slice * 4);
  895. });
  896. it ('should add steps before and after if scale.min/max are outside the data range', function() {
  897. var chart = this.chart;
  898. var scale = chart.scales.x;
  899. var options = chart.options.scales.xAxes[0];
  900. options.time.min = '2012';
  901. options.time.max = '2050';
  902. chart.update();
  903. var start = scale.left;
  904. var slice = scale.width / 6;
  905. expect(scale.getPixelForValue('2017')).toBeCloseToPixel(start + slice);
  906. expect(scale.getPixelForValue('2042')).toBeCloseToPixel(start + slice * 5);
  907. });
  908. });
  909. describe('is "linear"', function() {
  910. beforeEach(function() {
  911. this.chart = window.acquireChart({
  912. type: 'line',
  913. data: {
  914. labels: ['2017', '2019', '2020', '2025', '2042'],
  915. datasets: [{data: [0, 1, 2, 3, 4, 5]}]
  916. },
  917. options: {
  918. scales: {
  919. xAxes: [{
  920. id: 'x',
  921. type: 'time',
  922. time: {
  923. parser: 'YYYY'
  924. },
  925. distribution: 'linear',
  926. ticks: {
  927. source: 'labels'
  928. }
  929. }],
  930. yAxes: [{
  931. display: false
  932. }]
  933. }
  934. }
  935. });
  936. });
  937. it ('should space data out with a gap relative to their time values', function() {
  938. var scale = this.chart.scales.x;
  939. var start = scale.left;
  940. var slice = scale.width / (2042 - 2017);
  941. expect(scale.getPixelForValue('2017')).toBeCloseToPixel(start);
  942. expect(scale.getPixelForValue('2019')).toBeCloseToPixel(start + slice * (2019 - 2017));
  943. expect(scale.getPixelForValue('2020')).toBeCloseToPixel(start + slice * (2020 - 2017));
  944. expect(scale.getPixelForValue('2025')).toBeCloseToPixel(start + slice * (2025 - 2017));
  945. expect(scale.getPixelForValue('2042')).toBeCloseToPixel(start + slice * (2042 - 2017));
  946. });
  947. it ('should take in account scale min and max if outside the ticks range', function() {
  948. var chart = this.chart;
  949. var scale = chart.scales.x;
  950. var options = chart.options.scales.xAxes[0];
  951. options.time.min = '2012';
  952. options.time.max = '2050';
  953. chart.update();
  954. var start = scale.left;
  955. var slice = scale.width / (2050 - 2012);
  956. expect(scale.getPixelForValue('2017')).toBeCloseToPixel(start + slice * (2017 - 2012));
  957. expect(scale.getPixelForValue('2019')).toBeCloseToPixel(start + slice * (2019 - 2012));
  958. expect(scale.getPixelForValue('2020')).toBeCloseToPixel(start + slice * (2020 - 2012));
  959. expect(scale.getPixelForValue('2025')).toBeCloseToPixel(start + slice * (2025 - 2012));
  960. expect(scale.getPixelForValue('2042')).toBeCloseToPixel(start + slice * (2042 - 2012));
  961. });
  962. });
  963. });
  964. describe('when bounds', function() {
  965. describe('is "data"', function() {
  966. it ('should preserve the data range', function() {
  967. var chart = window.acquireChart({
  968. type: 'line',
  969. data: {
  970. labels: ['02/20 08:00', '02/21 09:00', '02/22 10:00', '02/23 11:00'],
  971. datasets: [{data: [0, 1, 2, 3, 4, 5]}]
  972. },
  973. options: {
  974. scales: {
  975. xAxes: [{
  976. id: 'x',
  977. type: 'time',
  978. bounds: 'data',
  979. time: {
  980. parser: 'MM/DD HH:mm',
  981. unit: 'day'
  982. }
  983. }],
  984. yAxes: [{
  985. display: false
  986. }]
  987. }
  988. }
  989. });
  990. var scale = chart.scales.x;
  991. expect(scale.min).toEqual(+moment('02/20 08:00', 'MM/DD HH:mm'));
  992. expect(scale.max).toEqual(+moment('02/23 11:00', 'MM/DD HH:mm'));
  993. expect(scale.getPixelForValue('02/20 08:00')).toBeCloseToPixel(scale.left);
  994. expect(scale.getPixelForValue('02/23 11:00')).toBeCloseToPixel(scale.left + scale.width);
  995. expect(getTicksLabels(scale)).toEqual([
  996. 'Feb 21', 'Feb 22', 'Feb 23']);
  997. });
  998. });
  999. describe('is "labels"', function() {
  1000. it('should preserve the label range', function() {
  1001. var chart = window.acquireChart({
  1002. type: 'line',
  1003. data: {
  1004. labels: ['02/20 08:00', '02/21 09:00', '02/22 10:00', '02/23 11:00'],
  1005. datasets: [{data: [0, 1, 2, 3, 4, 5]}]
  1006. },
  1007. options: {
  1008. scales: {
  1009. xAxes: [{
  1010. id: 'x',
  1011. type: 'time',
  1012. bounds: 'ticks',
  1013. time: {
  1014. parser: 'MM/DD HH:mm',
  1015. unit: 'day'
  1016. }
  1017. }],
  1018. yAxes: [{
  1019. display: false
  1020. }]
  1021. }
  1022. }
  1023. });
  1024. var scale = chart.scales.x;
  1025. var ticks = scale.getTicks();
  1026. expect(scale.min).toEqual(ticks[0].value);
  1027. expect(scale.max).toEqual(ticks[ticks.length - 1].value);
  1028. expect(scale.getPixelForValue('02/20 08:00')).toBeCloseToPixel(60);
  1029. expect(scale.getPixelForValue('02/23 11:00')).toBeCloseToPixel(426);
  1030. expect(getTicksLabels(scale)).toEqual([
  1031. 'Feb 20', 'Feb 21', 'Feb 22', 'Feb 23', 'Feb 24']);
  1032. });
  1033. });
  1034. });
  1035. describe('when time.min and/or time.max are defined', function() {
  1036. ['auto', 'data', 'labels'].forEach(function(source) {
  1037. ['data', 'ticks'].forEach(function(bounds) {
  1038. describe('and ticks.source is "' + source + '" and bounds "' + bounds + '"', function() {
  1039. beforeEach(function() {
  1040. this.chart = window.acquireChart({
  1041. type: 'line',
  1042. data: {
  1043. labels: ['02/20 08:00', '02/21 09:00', '02/22 10:00', '02/23 11:00'],
  1044. datasets: [{data: [0, 1, 2, 3, 4, 5]}]
  1045. },
  1046. options: {
  1047. scales: {
  1048. xAxes: [{
  1049. id: 'x',
  1050. type: 'time',
  1051. bounds: bounds,
  1052. time: {
  1053. parser: 'MM/DD HH:mm',
  1054. unit: 'day'
  1055. },
  1056. ticks: {
  1057. source: source
  1058. }
  1059. }],
  1060. yAxes: [{
  1061. display: false
  1062. }]
  1063. }
  1064. }
  1065. });
  1066. });
  1067. it ('should expand scale to the min/max range', function() {
  1068. var chart = this.chart;
  1069. var scale = chart.scales.x;
  1070. var options = chart.options.scales.xAxes[0];
  1071. var min = '02/19 07:00';
  1072. var max = '02/24 08:00';
  1073. options.time.min = min;
  1074. options.time.max = max;
  1075. chart.update();
  1076. expect(scale.min).toEqual(+moment(min, 'MM/DD HH:mm'));
  1077. expect(scale.max).toEqual(+moment(max, 'MM/DD HH:mm'));
  1078. expect(scale.getPixelForValue(min)).toBeCloseToPixel(scale.left);
  1079. expect(scale.getPixelForValue(max)).toBeCloseToPixel(scale.left + scale.width);
  1080. scale.getTicks().forEach(function(tick) {
  1081. expect(tick.value >= +moment(min, 'MM/DD HH:mm')).toBeTruthy();
  1082. expect(tick.value <= +moment(max, 'MM/DD HH:mm')).toBeTruthy();
  1083. });
  1084. });
  1085. it ('should shrink scale to the min/max range', function() {
  1086. var chart = this.chart;
  1087. var scale = chart.scales.x;
  1088. var options = chart.options.scales.xAxes[0];
  1089. var min = '02/21 07:00';
  1090. var max = '02/22 20:00';
  1091. options.time.min = min;
  1092. options.time.max = max;
  1093. chart.update();
  1094. expect(scale.min).toEqual(+moment(min, 'MM/DD HH:mm'));
  1095. expect(scale.max).toEqual(+moment(max, 'MM/DD HH:mm'));
  1096. expect(scale.getPixelForValue(min)).toBeCloseToPixel(scale.left);
  1097. expect(scale.getPixelForValue(max)).toBeCloseToPixel(scale.left + scale.width);
  1098. scale.getTicks().forEach(function(tick) {
  1099. expect(tick.value >= +moment(min, 'MM/DD HH:mm')).toBeTruthy();
  1100. expect(tick.value <= +moment(max, 'MM/DD HH:mm')).toBeTruthy();
  1101. });
  1102. });
  1103. });
  1104. });
  1105. });
  1106. });
  1107. ['auto', 'data', 'labels'].forEach(function(source) {
  1108. ['series', 'linear'].forEach(function(distribution) {
  1109. describe('when ticks.source is "' + source + '" and distribution is "' + distribution + '"', function() {
  1110. beforeEach(function() {
  1111. this.chart = window.acquireChart({
  1112. type: 'line',
  1113. data: {
  1114. labels: ['2017', '2019', '2020', '2025', '2042'],
  1115. datasets: [{data: [0, 1, 2, 3, 4, 5]}]
  1116. },
  1117. options: {
  1118. scales: {
  1119. xAxes: [{
  1120. id: 'x',
  1121. type: 'time',
  1122. time: {
  1123. parser: 'YYYY'
  1124. },
  1125. ticks: {
  1126. source: source
  1127. },
  1128. distribution: distribution
  1129. }]
  1130. }
  1131. }
  1132. });
  1133. });
  1134. it ('should not add offset from the edges', function() {
  1135. var scale = this.chart.scales.x;
  1136. expect(scale.getPixelForValue('2017')).toBeCloseToPixel(scale.left);
  1137. expect(scale.getPixelForValue('2042')).toBeCloseToPixel(scale.left + scale.width);
  1138. });
  1139. it ('should add offset from the edges if offset is true', function() {
  1140. var chart = this.chart;
  1141. var scale = chart.scales.x;
  1142. var options = chart.options.scales.xAxes[0];
  1143. options.offset = true;
  1144. chart.update();
  1145. var numTicks = scale.ticks.length;
  1146. var firstTickInterval = scale.getPixelForTick(1) - scale.getPixelForTick(0);
  1147. var lastTickInterval = scale.getPixelForTick(numTicks - 1) - scale.getPixelForTick(numTicks - 2);
  1148. expect(scale.getPixelForValue('2017')).toBeCloseToPixel(scale.left + firstTickInterval / 2);
  1149. expect(scale.getPixelForValue('2042')).toBeCloseToPixel(scale.left + scale.width - lastTickInterval / 2);
  1150. });
  1151. it ('should not add offset if min and max extend the labels range', function() {
  1152. var chart = this.chart;
  1153. var scale = chart.scales.x;
  1154. var options = chart.options.scales.xAxes[0];
  1155. options.time.min = '2012';
  1156. options.time.max = '2051';
  1157. chart.update();
  1158. expect(scale.getPixelForValue('2012')).toBeCloseToPixel(scale.left);
  1159. expect(scale.getPixelForValue('2051')).toBeCloseToPixel(scale.left + scale.width);
  1160. });
  1161. it ('should not add offset if min and max extend the labels range and offset is true', function() {
  1162. var chart = this.chart;
  1163. var scale = chart.scales.x;
  1164. var options = chart.options.scales.xAxes[0];
  1165. options.time.min = '2012';
  1166. options.time.max = '2051';
  1167. options.offset = true;
  1168. chart.update();
  1169. expect(scale.getPixelForValue('2012')).toBeCloseToPixel(scale.left);
  1170. expect(scale.getPixelForValue('2051')).toBeCloseToPixel(scale.left + scale.width);
  1171. });
  1172. });
  1173. });
  1174. });
  1175. });