732 lines
17KB

  1. describe('Linear Scale', function() {
  2. var chartInstance;
  3. beforeEach(function() {
  4. window.addDefaultMatchers(jasmine);
  5. });
  6. afterEach(function() {
  7. if (chartInstance)
  8. {
  9. releaseChart(chartInstance);
  10. }
  11. });
  12. it('Should register the constructor with the scale service', function() {
  13. var Constructor = Chart.scaleService.getScaleConstructor('linear');
  14. expect(Constructor).not.toBe(undefined);
  15. expect(typeof Constructor).toBe('function');
  16. });
  17. it('Should have the correct default config', function() {
  18. var defaultConfig = Chart.scaleService.getScaleDefaults('linear');
  19. expect(defaultConfig).toEqual({
  20. display: true,
  21. gridLines: {
  22. color: "rgba(0, 0, 0, 0.1)",
  23. drawBorder: true,
  24. drawOnChartArea: true,
  25. drawTicks: true, // draw ticks extending towards the label
  26. tickMarkLength: 10,
  27. lineWidth: 1,
  28. offsetGridLines: false,
  29. display: true,
  30. zeroLineColor: "rgba(0,0,0,0.25)",
  31. zeroLineWidth: 1,
  32. },
  33. position: "left",
  34. scaleLabel: {
  35. labelString: '',
  36. display: false,
  37. },
  38. ticks: {
  39. beginAtZero: false,
  40. minRotation: 0,
  41. maxRotation: 50,
  42. mirror: false,
  43. padding: 10,
  44. reverse: false,
  45. display: true,
  46. callback: defaultConfig.ticks.callback, // make this work nicer, then check below
  47. autoSkip: true,
  48. autoSkipPadding: 0,
  49. labelOffset: 0
  50. }
  51. });
  52. expect(defaultConfig.ticks.callback).toEqual(jasmine.any(Function));
  53. });
  54. it('Should correctly determine the max & min data values', function() {
  55. chartInstance = window.acquireChart({
  56. type: 'bar',
  57. data: {
  58. datasets: [{
  59. yAxisID: 'yScale0',
  60. data: [10, 5, 0, -5, 78, -100]
  61. }, {
  62. yAxisID: 'yScale1',
  63. data: [-1000, 1000],
  64. }, {
  65. yAxisID: 'yScale0',
  66. data: [150]
  67. }],
  68. labels: ['a', 'b', 'c', 'd', 'e', 'f']
  69. },
  70. options: {
  71. scales: {
  72. yAxes: [{
  73. id: 'yScale0',
  74. type: 'linear'
  75. }, {
  76. id: 'yScale1',
  77. type: 'linear'
  78. }]
  79. }
  80. }
  81. });
  82. expect(chartInstance.scales.yScale0).not.toEqual(undefined); // must construct
  83. expect(chartInstance.scales.yScale0.min).toBe(-100);
  84. expect(chartInstance.scales.yScale0.max).toBe(150);
  85. });
  86. it('Should correctly determine the max & min of string data values', function() {
  87. chartInstance = window.acquireChart({
  88. type: 'bar',
  89. data: {
  90. datasets: [{
  91. yAxisID: 'yScale0',
  92. data: ['10', '5', '0', '-5', '78', '-100']
  93. }, {
  94. yAxisID: 'yScale1',
  95. data: ['-1000', '1000'],
  96. }, {
  97. yAxisID: 'yScale0',
  98. data: ['150']
  99. }],
  100. labels: ['a', 'b', 'c', 'd', 'e', 'f']
  101. },
  102. options: {
  103. scales: {
  104. yAxes: [{
  105. id: 'yScale0',
  106. type: 'linear'
  107. }, {
  108. id: 'yScale1',
  109. type: 'linear'
  110. }]
  111. }
  112. }
  113. });
  114. expect(chartInstance.scales.yScale0).not.toEqual(undefined); // must construct
  115. expect(chartInstance.scales.yScale0.min).toBe(-100);
  116. expect(chartInstance.scales.yScale0.max).toBe(150);
  117. });
  118. it('Should correctly determine the max & min data values ignoring hidden datasets', function() {
  119. chartInstance = window.acquireChart({
  120. type: 'bar',
  121. data: {
  122. datasets: [{
  123. yAxisID: 'yScale0',
  124. data: ['10', '5', '0', '-5', '78', '-100']
  125. }, {
  126. yAxisID: 'yScale1',
  127. data: ['-1000', '1000'],
  128. }, {
  129. yAxisID: 'yScale0',
  130. data: ['150'],
  131. hidden: true
  132. }],
  133. labels: ['a', 'b', 'c', 'd', 'e', 'f']
  134. },
  135. options: {
  136. scales: {
  137. yAxes: [{
  138. id: 'yScale0',
  139. type: 'linear'
  140. }, {
  141. id: 'yScale1',
  142. type: 'linear'
  143. }]
  144. }
  145. }
  146. });
  147. expect(chartInstance.scales.yScale0).not.toEqual(undefined); // must construct
  148. expect(chartInstance.scales.yScale0.min).toBe(-100);
  149. expect(chartInstance.scales.yScale0.max).toBe(80);
  150. });
  151. it('Should correctly determine the max & min data values ignoring data that is NaN', function() {
  152. chartInstance = window.acquireChart({
  153. type: 'bar',
  154. data: {
  155. datasets: [{
  156. yAxisID: 'yScale0',
  157. data: [null, 90, NaN, undefined, 45, 30]
  158. }],
  159. labels: ['a', 'b', 'c', 'd', 'e', 'f']
  160. },
  161. options: {
  162. scales: {
  163. yAxes: [{
  164. id: 'yScale0',
  165. type: 'linear'
  166. }]
  167. }
  168. }
  169. });
  170. expect(chartInstance.scales.yScale0.min).toBe(30);
  171. expect(chartInstance.scales.yScale0.max).toBe(90);
  172. // Scale is now stacked
  173. chartInstance.scales.yScale0.options.stacked = true;
  174. chartInstance.update();
  175. expect(chartInstance.scales.yScale0.min).toBe(0);
  176. expect(chartInstance.scales.yScale0.max).toBe(90);
  177. });
  178. it('Should correctly determine the max & min for scatter data', function() {
  179. chartInstance = window.acquireChart({
  180. type: 'line',
  181. data: {
  182. datasets: [{
  183. xAxisID: 'xScale0',
  184. yAxisID: 'yScale0',
  185. data: [{
  186. x: 10,
  187. y: 100
  188. }, {
  189. x: -10,
  190. y: 0
  191. }, {
  192. x: 0,
  193. y: 0
  194. }, {
  195. x: 99,
  196. y: 7
  197. }]
  198. }],
  199. },
  200. options: {
  201. scales: {
  202. xAxes: [{
  203. id: 'xScale0',
  204. type: 'linear',
  205. position: 'bottom'
  206. }],
  207. yAxes: [{
  208. id: 'yScale0',
  209. type: 'linear'
  210. }]
  211. }
  212. }
  213. });
  214. chartInstance.update();
  215. expect(chartInstance.scales.xScale0.min).toBe(-20);
  216. expect(chartInstance.scales.xScale0.max).toBe(100);
  217. expect(chartInstance.scales.yScale0.min).toBe(0);
  218. expect(chartInstance.scales.yScale0.max).toBe(100);
  219. });
  220. it('Should correctly get the label for the given index', function() {
  221. chartInstance = window.acquireChart({
  222. type: 'line',
  223. data: {
  224. datasets: [{
  225. xAxisID: 'xScale0',
  226. yAxisID: 'yScale0',
  227. data: [{
  228. x: 10,
  229. y: 100
  230. }, {
  231. x: -10,
  232. y: 0
  233. }, {
  234. x: 0,
  235. y: 0
  236. }, {
  237. x: 99,
  238. y: 7
  239. }]
  240. }],
  241. },
  242. options: {
  243. scales: {
  244. xAxes: [{
  245. id: 'xScale0',
  246. type: 'linear',
  247. position: 'bottom'
  248. }],
  249. yAxes: [{
  250. id: 'yScale0',
  251. type: 'linear'
  252. }]
  253. }
  254. }
  255. });
  256. chartInstance.update();
  257. expect(chartInstance.scales.yScale0.getLabelForIndex(3, 0)).toBe(7);
  258. });
  259. it('Should correctly determine the min and max data values when stacked mode is turned on', function() {
  260. chartInstance = window.acquireChart({
  261. type: 'line',
  262. data: {
  263. datasets: [{
  264. yAxisID: 'yScale0',
  265. data: [10, 5, 0, -5, 78, -100],
  266. type: 'bar'
  267. }, {
  268. yAxisID: 'yScale1',
  269. data: [-1000, 1000],
  270. }, {
  271. yAxisID: 'yScale0',
  272. data: [150, 0, 0, -100, -10, 9],
  273. type: 'bar'
  274. }, {
  275. yAxisID: 'yScale0',
  276. data: [10, 10, 10, 10, 10, 10],
  277. type: 'line'
  278. }],
  279. labels: ['a', 'b', 'c', 'd', 'e', 'f']
  280. },
  281. options: {
  282. scales: {
  283. yAxes: [{
  284. id: 'yScale0',
  285. type: 'linear',
  286. stacked: true
  287. }, {
  288. id: 'yScale1',
  289. type: 'linear'
  290. }]
  291. }
  292. }
  293. });
  294. chartInstance.update();
  295. expect(chartInstance.scales.yScale0.min).toBe(-150);
  296. expect(chartInstance.scales.yScale0.max).toBe(200);
  297. });
  298. it('Should correctly determine the min and max data values when stacked mode is turned on and there are hidden datasets', function() {
  299. chartInstance = window.acquireChart({
  300. type: 'bar',
  301. data: {
  302. datasets: [{
  303. yAxisID: 'yScale0',
  304. data: [10, 5, 0, -5, 78, -100],
  305. }, {
  306. yAxisID: 'yScale1',
  307. data: [-1000, 1000],
  308. }, {
  309. yAxisID: 'yScale0',
  310. data: [150, 0, 0, -100, -10, 9],
  311. }, {
  312. yAxisID: 'yScale0',
  313. data: [10, 20, 30, 40, 50, 60],
  314. hidden: true
  315. }],
  316. labels: ['a', 'b', 'c', 'd', 'e', 'f']
  317. },
  318. options: {
  319. scales: {
  320. yAxes: [{
  321. id: 'yScale0',
  322. type: 'linear',
  323. stacked: true
  324. }, {
  325. id: 'yScale1',
  326. type: 'linear'
  327. }]
  328. }
  329. }
  330. });
  331. chartInstance.update();
  332. expect(chartInstance.scales.yScale0.min).toBe(-150);
  333. expect(chartInstance.scales.yScale0.max).toBe(200);
  334. });
  335. it('Should correctly determine the min and max data values when stacked mode is turned on there are multiple types of datasets', function() {
  336. chartInstance = window.acquireChart({
  337. type: 'bar',
  338. data: {
  339. datasets: [{
  340. yAxisID: 'yScale0',
  341. type: 'bar',
  342. data: [10, 5, 0, -5, 78, -100]
  343. }, {
  344. type: 'line',
  345. data: [10, 10, 10, 10, 10, 10],
  346. }, {
  347. type: 'bar',
  348. data: [150, 0, 0, -100, -10, 9]
  349. }],
  350. labels: ['a', 'b', 'c', 'd', 'e', 'f']
  351. },
  352. options: {
  353. scales: {
  354. yAxes: [{
  355. id: 'yScale0',
  356. type: 'linear',
  357. stacked: true
  358. }]
  359. }
  360. }
  361. });
  362. chartInstance.scales.yScale0.determineDataLimits();
  363. expect(chartInstance.scales.yScale0.min).toBe(-105);
  364. expect(chartInstance.scales.yScale0.max).toBe(160);
  365. });
  366. it('Should ensure that the scale has a max and min that are not equal', function() {
  367. chartInstance = window.acquireChart({
  368. type: 'bar',
  369. data: {
  370. datasets: [],
  371. labels: ['a', 'b', 'c', 'd', 'e', 'f']
  372. },
  373. options: {
  374. scales: {
  375. yAxes: [{
  376. id: 'yScale0',
  377. type: 'linear'
  378. }]
  379. }
  380. }
  381. });
  382. expect(chartInstance.scales.yScale0).not.toEqual(undefined); // must construct
  383. expect(chartInstance.scales.yScale0.min).toBe(-1);
  384. expect(chartInstance.scales.yScale0.max).toBe(1);
  385. });
  386. it('Should ensure that the scale has a max and min that are not equal when beginAtZero is set', function() {
  387. chartInstance = window.acquireChart({
  388. type: 'bar',
  389. data: {
  390. datasets: [],
  391. labels: ['a', 'b', 'c', 'd', 'e', 'f']
  392. },
  393. options: {
  394. scales: {
  395. yAxes: [{
  396. id: 'yScale0',
  397. type: 'linear',
  398. ticks: {
  399. beginAtZero: true
  400. }
  401. }]
  402. }
  403. }
  404. });
  405. expect(chartInstance.scales.yScale0).not.toEqual(undefined); // must construct
  406. expect(chartInstance.scales.yScale0.min).toBe(0);
  407. expect(chartInstance.scales.yScale0.max).toBe(1);
  408. });
  409. it('Should use the suggestedMin and suggestedMax options', function() {
  410. chartInstance = window.acquireChart({
  411. type: 'bar',
  412. data: {
  413. datasets: [{
  414. yAxisID: 'yScale0',
  415. data: [1, 1, 1, 2, 1, 0]
  416. }],
  417. labels: ['a', 'b', 'c', 'd', 'e', 'f']
  418. },
  419. options: {
  420. scales: {
  421. yAxes: [{
  422. id: 'yScale0',
  423. type: 'linear',
  424. ticks: {
  425. suggestedMax: 10,
  426. suggestedMin: -10
  427. }
  428. }]
  429. }
  430. }
  431. });
  432. expect(chartInstance.scales.yScale0).not.toEqual(undefined); // must construct
  433. expect(chartInstance.scales.yScale0.min).toBe(-10);
  434. expect(chartInstance.scales.yScale0.max).toBe(10);
  435. });
  436. it('Should use the min and max options', function() {
  437. chartInstance = window.acquireChart({
  438. type: 'bar',
  439. data: {
  440. datasets: [{
  441. yAxisID: 'yScale0',
  442. data: [1, 1, 1, 2, 1, 0]
  443. }],
  444. labels: ['a', 'b', 'c', 'd', 'e', 'f']
  445. },
  446. options: {
  447. scales: {
  448. yAxes: [{
  449. id: 'yScale0',
  450. type: 'linear',
  451. ticks: {
  452. max: 1010,
  453. min: -1010
  454. }
  455. }]
  456. }
  457. }
  458. });
  459. expect(chartInstance.scales.yScale0).not.toEqual(undefined); // must construct
  460. expect(chartInstance.scales.yScale0.min).toBe(-1010);
  461. expect(chartInstance.scales.yScale0.max).toBe(1010);
  462. expect(chartInstance.scales.yScale0.ticks[0]).toBe('1010');
  463. expect(chartInstance.scales.yScale0.ticks[chartInstance.scales.yScale0.ticks.length - 1]).toBe('-1010');
  464. });
  465. it('should forcibly include 0 in the range if the beginAtZero option is used', function() {
  466. chartInstance = window.acquireChart({
  467. type: 'bar',
  468. data: {
  469. datasets: [{
  470. yAxisID: 'yScale0',
  471. data: [20, 30, 40, 50]
  472. }],
  473. labels: ['a', 'b', 'c', 'd']
  474. },
  475. options: {
  476. scales: {
  477. yAxes: [{
  478. id: 'yScale0',
  479. type: 'linear',
  480. }]
  481. }
  482. }
  483. });
  484. expect(chartInstance.scales.yScale0).not.toEqual(undefined); // must construct
  485. expect(chartInstance.scales.yScale0.ticks).toEqual(['50', '45', '40', '35', '30', '25', '20']);
  486. chartInstance.scales.yScale0.options.ticks.beginAtZero = true;
  487. chartInstance.update();
  488. expect(chartInstance.scales.yScale0.ticks).toEqual(['50', '45', '40', '35', '30', '25', '20', '15', '10', '5', '0']);
  489. chartInstance.data.datasets[0].data = [-20, -30, -40, -50];
  490. chartInstance.update();
  491. expect(chartInstance.scales.yScale0.ticks).toEqual(['0', '-5', '-10', '-15', '-20', '-25', '-30', '-35', '-40', '-45', '-50']);
  492. chartInstance.scales.yScale0.options.ticks.beginAtZero = false;
  493. chartInstance.update();
  494. expect(chartInstance.scales.yScale0.ticks).toEqual(['-20', '-25', '-30', '-35', '-40', '-45', '-50']);
  495. });
  496. it('Should generate tick marks in the correct order in reversed mode', function() {
  497. chartInstance = window.acquireChart({
  498. type: 'bar',
  499. data: {
  500. datasets: [{
  501. yAxisID: 'yScale0',
  502. data: [10, 5, 0, 25, 78]
  503. }],
  504. labels: ['a', 'b', 'c', 'd']
  505. },
  506. options: {
  507. scales: {
  508. yAxes: [{
  509. id: 'yScale0',
  510. type: 'linear',
  511. ticks: {
  512. reverse: true
  513. }
  514. }]
  515. }
  516. }
  517. });
  518. expect(chartInstance.scales.yScale0.ticks).toEqual(['0', '10', '20', '30', '40', '50', '60', '70', '80']);
  519. expect(chartInstance.scales.yScale0.start).toBe(80);
  520. expect(chartInstance.scales.yScale0.end).toBe(0);
  521. });
  522. it('should use the correct number of decimal places in the default format function', function() {
  523. chartInstance = window.acquireChart({
  524. type: 'bar',
  525. data: {
  526. datasets: [{
  527. yAxisID: 'yScale0',
  528. data: [0.06, 0.005, 0, 0.025, 0.0078]
  529. }],
  530. labels: ['a', 'b', 'c', 'd']
  531. },
  532. options: {
  533. scales: {
  534. yAxes: [{
  535. id: 'yScale0',
  536. type: 'linear',
  537. }]
  538. }
  539. }
  540. });
  541. expect(chartInstance.scales.yScale0.ticks).toEqual(['0.06', '0.05', '0.04', '0.03', '0.02', '0.01', '0']);
  542. });
  543. it('Should build labels using the user supplied callback', function() {
  544. chartInstance = window.acquireChart({
  545. type: 'bar',
  546. data: {
  547. datasets: [{
  548. yAxisID: 'yScale0',
  549. data: [10, 5, 0, 25, 78]
  550. }],
  551. labels: ['a', 'b', 'c', 'd']
  552. },
  553. options: {
  554. scales: {
  555. yAxes: [{
  556. id: 'yScale0',
  557. type: 'linear',
  558. ticks: {
  559. callback: function(value, index) {
  560. return index.toString();
  561. }
  562. }
  563. }]
  564. }
  565. }
  566. });
  567. // Just the index
  568. expect(chartInstance.scales.yScale0.ticks).toEqual(['0', '1', '2', '3', '4', '5', '6', '7', '8']);
  569. });
  570. it('Should get the correct pixel value for a point', function() {
  571. chartInstance = window.acquireChart({
  572. type: 'line',
  573. data: {
  574. datasets: [{
  575. xAxisID: 'xScale0',
  576. yAxisID: 'yScale0',
  577. data: []
  578. }],
  579. },
  580. options: {
  581. scales: {
  582. xAxes: [{
  583. id: 'xScale0',
  584. type: 'linear',
  585. position: 'bottom'
  586. }],
  587. yAxes: [{
  588. id: 'yScale0',
  589. type: 'linear'
  590. }]
  591. }
  592. }
  593. });
  594. var xScale = chartInstance.scales.xScale0;
  595. expect(xScale.getPixelForValue(1, 0, 0)).toBeCloseToPixel(501); // right - paddingRight
  596. expect(xScale.getPixelForValue(-1, 0, 0)).toBeCloseToPixel(41); // left + paddingLeft
  597. expect(xScale.getPixelForValue(0, 0, 0)).toBeCloseToPixel(271); // halfway*/
  598. expect(xScale.getValueForPixel(501)).toBeCloseTo(1, 1e-2);
  599. expect(xScale.getValueForPixel(41)).toBeCloseTo(-1, 1e-2);
  600. expect(xScale.getValueForPixel(271)).toBeCloseTo(0, 1e-2);
  601. var yScale = chartInstance.scales.yScale0;
  602. expect(yScale.getPixelForValue(1, 0, 0)).toBeCloseToPixel(32); // right - paddingRight
  603. expect(yScale.getPixelForValue(-1, 0, 0)).toBeCloseToPixel(484); // left + paddingLeft
  604. expect(yScale.getPixelForValue(0, 0, 0)).toBeCloseToPixel(258); // halfway*/
  605. expect(yScale.getValueForPixel(32)).toBe(1);
  606. expect(yScale.getValueForPixel(484)).toBe(-1);
  607. expect(yScale.getValueForPixel(258)).toBe(0);
  608. });
  609. it('should fit correctly', function() {
  610. chartInstance = window.acquireChart({
  611. type: 'line',
  612. data: {
  613. datasets: [{
  614. xAxisID: 'xScale0',
  615. yAxisID: 'yScale0',
  616. data: [{
  617. x: 10,
  618. y: 100
  619. }, {
  620. x: -10,
  621. y: 0
  622. }, {
  623. x: 0,
  624. y: 0
  625. }, {
  626. x: 99,
  627. y: 7
  628. }]
  629. }],
  630. },
  631. options: {
  632. scales: {
  633. xAxes: [{
  634. id: 'xScale0',
  635. type: 'linear',
  636. position: 'bottom'
  637. }],
  638. yAxes: [{
  639. id: 'yScale0',
  640. type: 'linear'
  641. }]
  642. }
  643. }
  644. });
  645. var xScale = chartInstance.scales.xScale0;
  646. expect(xScale.paddingTop).toBeCloseToPixel(0);
  647. expect(xScale.paddingBottom).toBeCloseToPixel(0);
  648. expect(xScale.paddingLeft).toBeCloseToPixel(0);
  649. expect(xScale.paddingRight).toBeCloseToPixel(13.5);
  650. expect(xScale.width).toBeCloseToPixel(471);
  651. expect(xScale.height).toBeCloseToPixel(28);
  652. var yScale = chartInstance.scales.yScale0;
  653. expect(yScale.paddingTop).toBeCloseToPixel(0);
  654. expect(yScale.paddingBottom).toBeCloseToPixel(0);
  655. expect(yScale.paddingLeft).toBeCloseToPixel(0);
  656. expect(yScale.paddingRight).toBeCloseToPixel(0);
  657. expect(yScale.width).toBeCloseToPixel(41);
  658. expect(yScale.height).toBeCloseToPixel(452);
  659. // Extra size when scale label showing
  660. xScale.options.scaleLabel.display = true;
  661. yScale.options.scaleLabel.display = true;
  662. chartInstance.update();
  663. expect(xScale.paddingTop).toBeCloseToPixel(0);
  664. expect(xScale.paddingBottom).toBeCloseToPixel(0);
  665. expect(xScale.paddingLeft).toBeCloseToPixel(0);
  666. expect(xScale.paddingRight).toBeCloseToPixel(13.5);
  667. expect(xScale.width).toBeCloseToPixel(453);
  668. expect(xScale.height).toBeCloseToPixel(46);
  669. expect(yScale.paddingTop).toBeCloseToPixel(0);
  670. expect(yScale.paddingBottom).toBeCloseToPixel(0);
  671. expect(yScale.paddingLeft).toBeCloseToPixel(0);
  672. expect(yScale.paddingRight).toBeCloseToPixel(0);
  673. expect(yScale.width).toBeCloseToPixel(59);
  674. expect(yScale.height).toBeCloseToPixel(434);
  675. });
  676. });