core.controller.tests.js 30 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264126512661267126812691270127112721273127412751276127712781279128012811282128312841285128612871288128912901291129212931294129512961297129812991300130113021303130413051306130713081309131013111312131313141315131613171318131913201321132213231324132513261327132813291330133113321333133413351336133713381339134013411342134313441345134613471348134913501351135213531354135513561357135813591360
  1. describe('Chart', function() {
  2. // https://github.com/chartjs/Chart.js/issues/2481
  3. // See global.deprecations.tests.js for backward compatibility
  4. it('should be defined and prototype of chart instances', function() {
  5. var chart = acquireChart({});
  6. expect(Chart).toBeDefined();
  7. expect(Chart instanceof Object).toBeTruthy();
  8. expect(chart.constructor).toBe(Chart);
  9. expect(chart instanceof Chart).toBeTruthy();
  10. expect(Chart.prototype.isPrototypeOf(chart)).toBeTruthy();
  11. });
  12. describe('config initialization', function() {
  13. it('should create missing config.data properties', function() {
  14. var chart = acquireChart({});
  15. var data = chart.data;
  16. expect(data instanceof Object).toBeTruthy();
  17. expect(data.labels instanceof Array).toBeTruthy();
  18. expect(data.labels.length).toBe(0);
  19. expect(data.datasets instanceof Array).toBeTruthy();
  20. expect(data.datasets.length).toBe(0);
  21. });
  22. it('should not alter config.data references', function() {
  23. var ds0 = {data: [10, 11, 12, 13]};
  24. var ds1 = {data: [20, 21, 22, 23]};
  25. var datasets = [ds0, ds1];
  26. var labels = [0, 1, 2, 3];
  27. var data = {labels: labels, datasets: datasets};
  28. var chart = acquireChart({
  29. type: 'line',
  30. data: data
  31. });
  32. expect(chart.data).toBe(data);
  33. expect(chart.data.labels).toBe(labels);
  34. expect(chart.data.datasets).toBe(datasets);
  35. expect(chart.data.datasets[0]).toBe(ds0);
  36. expect(chart.data.datasets[1]).toBe(ds1);
  37. expect(chart.data.datasets[0].data).toBe(ds0.data);
  38. expect(chart.data.datasets[1].data).toBe(ds1.data);
  39. });
  40. it('should define chart.data as an alias for config.data', function() {
  41. var config = {data: {labels: [], datasets: []}};
  42. var chart = acquireChart(config);
  43. expect(chart.data).toBe(config.data);
  44. chart.data = {labels: [1, 2, 3], datasets: [{data: [4, 5, 6]}]};
  45. expect(config.data).toBe(chart.data);
  46. expect(config.data.labels).toEqual([1, 2, 3]);
  47. expect(config.data.datasets[0].data).toEqual([4, 5, 6]);
  48. config.data = {labels: [7, 8, 9], datasets: [{data: [10, 11, 12]}]};
  49. expect(chart.data).toBe(config.data);
  50. expect(chart.data.labels).toEqual([7, 8, 9]);
  51. expect(chart.data.datasets[0].data).toEqual([10, 11, 12]);
  52. });
  53. it('should initialize config with default options', function() {
  54. var callback = function() {};
  55. var defaults = Chart.defaults;
  56. defaults.global.responsiveAnimationDuration = 42;
  57. defaults.global.hover.onHover = callback;
  58. defaults.line.spanGaps = true;
  59. defaults.line.hover.mode = 'x-axis';
  60. var chart = acquireChart({
  61. type: 'line'
  62. });
  63. var options = chart.options;
  64. expect(options.defaultFontSize).toBe(defaults.global.defaultFontSize);
  65. expect(options.showLines).toBe(defaults.line.showLines);
  66. expect(options.spanGaps).toBe(true);
  67. expect(options.responsiveAnimationDuration).toBe(42);
  68. expect(options.hover.onHover).toBe(callback);
  69. expect(options.hover.mode).toBe('x-axis');
  70. defaults.global.responsiveAnimationDuration = 0;
  71. defaults.global.hover.onHover = null;
  72. defaults.line.spanGaps = false;
  73. defaults.line.hover.mode = 'label';
  74. });
  75. it('should override default options', function() {
  76. var callback = function() {};
  77. var defaults = Chart.defaults;
  78. defaults.global.responsiveAnimationDuration = 42;
  79. defaults.global.hover.onHover = callback;
  80. defaults.line.hover.mode = 'x-axis';
  81. defaults.line.spanGaps = true;
  82. var chart = acquireChart({
  83. type: 'line',
  84. options: {
  85. responsiveAnimationDuration: 4242,
  86. spanGaps: false,
  87. hover: {
  88. mode: 'dataset',
  89. },
  90. title: {
  91. position: 'bottom'
  92. }
  93. }
  94. });
  95. var options = chart.options;
  96. expect(options.responsiveAnimationDuration).toBe(4242);
  97. expect(options.showLines).toBe(defaults.global.showLines);
  98. expect(options.spanGaps).toBe(false);
  99. expect(options.hover.mode).toBe('dataset');
  100. expect(options.title.position).toBe('bottom');
  101. defaults.global.responsiveAnimationDuration = 0;
  102. defaults.global.hover.onHover = null;
  103. defaults.line.hover.mode = 'label';
  104. defaults.line.spanGaps = false;
  105. });
  106. it('should override axis positions that are incorrect', function() {
  107. var chart = acquireChart({
  108. type: 'line',
  109. options: {
  110. scales: {
  111. xAxes: [{
  112. position: 'left',
  113. }],
  114. yAxes: [{
  115. position: 'bottom'
  116. }]
  117. }
  118. }
  119. });
  120. var scaleOptions = chart.options.scales;
  121. expect(scaleOptions.xAxes[0].position).toBe('bottom');
  122. expect(scaleOptions.yAxes[0].position).toBe('left');
  123. });
  124. it('should throw an error if the chart type is incorrect', function() {
  125. function createChart() {
  126. acquireChart({
  127. type: 'area',
  128. data: {
  129. datasets: [{
  130. label: 'first',
  131. data: [10, 20]
  132. }],
  133. labels: ['0', '1'],
  134. },
  135. options: {
  136. scales: {
  137. xAxes: [{
  138. position: 'left',
  139. }],
  140. yAxes: [{
  141. position: 'bottom'
  142. }]
  143. }
  144. }
  145. });
  146. }
  147. expect(createChart).toThrow(new Error('"area" is not a chart type.'));
  148. });
  149. });
  150. describe('when merging scale options', function() {
  151. beforeEach(function() {
  152. Chart.helpers.merge(Chart.defaults.scale, {
  153. _jasmineCheckA: 'a0',
  154. _jasmineCheckB: 'b0',
  155. _jasmineCheckC: 'c0'
  156. });
  157. Chart.helpers.merge(Chart.scaleService.defaults.logarithmic, {
  158. _jasmineCheckB: 'b1',
  159. _jasmineCheckC: 'c1',
  160. });
  161. });
  162. afterEach(function() {
  163. delete Chart.defaults.scale._jasmineCheckA;
  164. delete Chart.defaults.scale._jasmineCheckB;
  165. delete Chart.defaults.scale._jasmineCheckC;
  166. delete Chart.scaleService.defaults.logarithmic._jasmineCheckB;
  167. delete Chart.scaleService.defaults.logarithmic._jasmineCheckC;
  168. });
  169. it('should default to "category" for x scales and "linear" for y scales', function() {
  170. var chart = acquireChart({
  171. type: 'line',
  172. options: {
  173. scales: {
  174. xAxes: [
  175. {id: 'foo0'},
  176. {id: 'foo1'}
  177. ],
  178. yAxes: [
  179. {id: 'bar0'},
  180. {id: 'bar1'}
  181. ]
  182. }
  183. }
  184. });
  185. expect(chart.scales.foo0.type).toBe('category');
  186. expect(chart.scales.foo1.type).toBe('category');
  187. expect(chart.scales.bar0.type).toBe('linear');
  188. expect(chart.scales.bar1.type).toBe('linear');
  189. });
  190. it('should correctly apply defaults on central scale', function() {
  191. var chart = acquireChart({
  192. type: 'line',
  193. options: {
  194. scale: {
  195. id: 'foo',
  196. type: 'logarithmic',
  197. _jasmineCheckC: 'c2',
  198. _jasmineCheckD: 'd2'
  199. }
  200. }
  201. });
  202. // let's check a few values from the user options and defaults
  203. expect(chart.scales.foo.type).toBe('logarithmic');
  204. expect(chart.scales.foo.options).toBe(chart.options.scale);
  205. expect(chart.scales.foo.options).toEqual(
  206. jasmine.objectContaining({
  207. _jasmineCheckA: 'a0',
  208. _jasmineCheckB: 'b1',
  209. _jasmineCheckC: 'c2',
  210. _jasmineCheckD: 'd2'
  211. }));
  212. });
  213. it('should correctly apply defaults on xy scales', function() {
  214. var chart = acquireChart({
  215. type: 'line',
  216. options: {
  217. scales: {
  218. xAxes: [{
  219. id: 'foo',
  220. type: 'logarithmic',
  221. _jasmineCheckC: 'c2',
  222. _jasmineCheckD: 'd2'
  223. }],
  224. yAxes: [{
  225. id: 'bar',
  226. type: 'time',
  227. _jasmineCheckC: 'c2',
  228. _jasmineCheckE: 'e2'
  229. }]
  230. }
  231. }
  232. });
  233. expect(chart.scales.foo.type).toBe('logarithmic');
  234. expect(chart.scales.foo.options).toBe(chart.options.scales.xAxes[0]);
  235. expect(chart.scales.foo.options).toEqual(
  236. jasmine.objectContaining({
  237. _jasmineCheckA: 'a0',
  238. _jasmineCheckB: 'b1',
  239. _jasmineCheckC: 'c2',
  240. _jasmineCheckD: 'd2'
  241. }));
  242. expect(chart.scales.bar.type).toBe('time');
  243. expect(chart.scales.bar.options).toBe(chart.options.scales.yAxes[0]);
  244. expect(chart.scales.bar.options).toEqual(
  245. jasmine.objectContaining({
  246. _jasmineCheckA: 'a0',
  247. _jasmineCheckB: 'b0',
  248. _jasmineCheckC: 'c2',
  249. _jasmineCheckE: 'e2'
  250. }));
  251. });
  252. it('should not alter defaults when merging config', function() {
  253. var chart = acquireChart({
  254. type: 'line',
  255. options: {
  256. _jasmineCheck: 42,
  257. scales: {
  258. xAxes: [{
  259. id: 'foo',
  260. type: 'linear',
  261. _jasmineCheck: 42,
  262. }],
  263. yAxes: [{
  264. id: 'bar',
  265. type: 'category',
  266. _jasmineCheck: 42,
  267. }]
  268. }
  269. }
  270. });
  271. expect(chart.options._jasmineCheck).toBeDefined();
  272. expect(chart.scales.foo.options._jasmineCheck).toBeDefined();
  273. expect(chart.scales.bar.options._jasmineCheck).toBeDefined();
  274. expect(Chart.defaults.line._jasmineCheck).not.toBeDefined();
  275. expect(Chart.defaults.global._jasmineCheck).not.toBeDefined();
  276. expect(Chart.scaleService.defaults.linear._jasmineCheck).not.toBeDefined();
  277. expect(Chart.scaleService.defaults.category._jasmineCheck).not.toBeDefined();
  278. });
  279. });
  280. describe('config.options.responsive: false', function() {
  281. it('should not inject the resizer element', function() {
  282. var chart = acquireChart({
  283. options: {
  284. responsive: false
  285. }
  286. });
  287. var wrapper = chart.canvas.parentNode;
  288. expect(wrapper.childNodes.length).toBe(1);
  289. expect(wrapper.firstChild.tagName).toBe('CANVAS');
  290. });
  291. });
  292. describe('config.options.responsive: true (maintainAspectRatio: false)', function() {
  293. it('should fill parent width and height', function() {
  294. var chart = acquireChart({
  295. options: {
  296. responsive: true,
  297. maintainAspectRatio: false
  298. }
  299. }, {
  300. canvas: {
  301. style: 'width: 150px; height: 245px'
  302. },
  303. wrapper: {
  304. style: 'width: 300px; height: 350px'
  305. }
  306. });
  307. expect(chart).toBeChartOfSize({
  308. dw: 300, dh: 350,
  309. rw: 300, rh: 350,
  310. });
  311. });
  312. it('should resize the canvas when parent width changes', function(done) {
  313. var chart = acquireChart({
  314. options: {
  315. responsive: true,
  316. maintainAspectRatio: false
  317. }
  318. }, {
  319. canvas: {
  320. style: ''
  321. },
  322. wrapper: {
  323. style: 'width: 300px; height: 350px; position: relative'
  324. }
  325. });
  326. expect(chart).toBeChartOfSize({
  327. dw: 300, dh: 350,
  328. rw: 300, rh: 350,
  329. });
  330. var wrapper = chart.canvas.parentNode;
  331. wrapper.style.width = '455px';
  332. waitForResize(chart, function() {
  333. expect(chart).toBeChartOfSize({
  334. dw: 455, dh: 350,
  335. rw: 455, rh: 350,
  336. });
  337. wrapper.style.width = '150px';
  338. waitForResize(chart, function() {
  339. expect(chart).toBeChartOfSize({
  340. dw: 150, dh: 350,
  341. rw: 150, rh: 350,
  342. });
  343. done();
  344. });
  345. });
  346. });
  347. it('should resize the canvas when parent is RTL and width changes', function(done) {
  348. var chart = acquireChart({
  349. options: {
  350. responsive: true,
  351. maintainAspectRatio: false
  352. }
  353. }, {
  354. canvas: {
  355. style: ''
  356. },
  357. wrapper: {
  358. style: 'width: 300px; height: 350px; position: relative; direction: rtl'
  359. }
  360. });
  361. expect(chart).toBeChartOfSize({
  362. dw: 300, dh: 350,
  363. rw: 300, rh: 350,
  364. });
  365. var wrapper = chart.canvas.parentNode;
  366. wrapper.style.width = '455px';
  367. waitForResize(chart, function() {
  368. expect(chart).toBeChartOfSize({
  369. dw: 455, dh: 350,
  370. rw: 455, rh: 350,
  371. });
  372. wrapper.style.width = '150px';
  373. waitForResize(chart, function() {
  374. expect(chart).toBeChartOfSize({
  375. dw: 150, dh: 350,
  376. rw: 150, rh: 350,
  377. });
  378. done();
  379. });
  380. });
  381. });
  382. it('should resize the canvas when parent height changes', function(done) {
  383. var chart = acquireChart({
  384. options: {
  385. responsive: true,
  386. maintainAspectRatio: false
  387. }
  388. }, {
  389. canvas: {
  390. style: ''
  391. },
  392. wrapper: {
  393. style: 'width: 300px; height: 350px; position: relative'
  394. }
  395. });
  396. expect(chart).toBeChartOfSize({
  397. dw: 300, dh: 350,
  398. rw: 300, rh: 350,
  399. });
  400. var wrapper = chart.canvas.parentNode;
  401. wrapper.style.height = '455px';
  402. waitForResize(chart, function() {
  403. expect(chart).toBeChartOfSize({
  404. dw: 300, dh: 455,
  405. rw: 300, rh: 455,
  406. });
  407. wrapper.style.height = '150px';
  408. waitForResize(chart, function() {
  409. expect(chart).toBeChartOfSize({
  410. dw: 300, dh: 150,
  411. rw: 300, rh: 150,
  412. });
  413. done();
  414. });
  415. });
  416. });
  417. it('should not include parent padding when resizing the canvas', function(done) {
  418. var chart = acquireChart({
  419. type: 'line',
  420. options: {
  421. responsive: true,
  422. maintainAspectRatio: false
  423. }
  424. }, {
  425. canvas: {
  426. style: ''
  427. },
  428. wrapper: {
  429. style: 'padding: 50px; width: 320px; height: 350px; position: relative'
  430. }
  431. });
  432. expect(chart).toBeChartOfSize({
  433. dw: 320, dh: 350,
  434. rw: 320, rh: 350,
  435. });
  436. var wrapper = chart.canvas.parentNode;
  437. wrapper.style.height = '355px';
  438. wrapper.style.width = '455px';
  439. waitForResize(chart, function() {
  440. expect(chart).toBeChartOfSize({
  441. dw: 455, dh: 355,
  442. rw: 455, rh: 355,
  443. });
  444. done();
  445. });
  446. });
  447. it('should resize the canvas when the canvas display style changes from "none" to "block"', function(done) {
  448. var chart = acquireChart({
  449. options: {
  450. responsive: true,
  451. maintainAspectRatio: false
  452. }
  453. }, {
  454. canvas: {
  455. style: 'display: none;'
  456. },
  457. wrapper: {
  458. style: 'width: 320px; height: 350px'
  459. }
  460. });
  461. var canvas = chart.canvas;
  462. canvas.style.display = 'block';
  463. waitForResize(chart, function() {
  464. expect(chart).toBeChartOfSize({
  465. dw: 320, dh: 350,
  466. rw: 320, rh: 350,
  467. });
  468. done();
  469. });
  470. });
  471. it('should resize the canvas when the wrapper display style changes from "none" to "block"', function(done) {
  472. var chart = acquireChart({
  473. options: {
  474. responsive: true,
  475. maintainAspectRatio: false
  476. }
  477. }, {
  478. canvas: {
  479. style: ''
  480. },
  481. wrapper: {
  482. style: 'display: none; width: 460px; height: 380px'
  483. }
  484. });
  485. var wrapper = chart.canvas.parentNode;
  486. wrapper.style.display = 'block';
  487. waitForResize(chart, function() {
  488. expect(chart).toBeChartOfSize({
  489. dw: 460, dh: 380,
  490. rw: 460, rh: 380,
  491. });
  492. done();
  493. });
  494. });
  495. // https://github.com/chartjs/Chart.js/issues/3790
  496. it('should resize the canvas if attached to the DOM after construction', function(done) {
  497. var canvas = document.createElement('canvas');
  498. var wrapper = document.createElement('div');
  499. var body = window.document.body;
  500. var chart = new Chart(canvas, {
  501. type: 'line',
  502. options: {
  503. responsive: true,
  504. maintainAspectRatio: false
  505. }
  506. });
  507. expect(chart).toBeChartOfSize({
  508. dw: 0, dh: 0,
  509. rw: 0, rh: 0,
  510. });
  511. wrapper.style.cssText = 'width: 455px; height: 355px';
  512. wrapper.appendChild(canvas);
  513. body.appendChild(wrapper);
  514. waitForResize(chart, function() {
  515. expect(chart).toBeChartOfSize({
  516. dw: 455, dh: 355,
  517. rw: 455, rh: 355,
  518. });
  519. body.removeChild(wrapper);
  520. chart.destroy();
  521. done();
  522. });
  523. });
  524. it('should resize the canvas when attached to a different parent', function(done) {
  525. var canvas = document.createElement('canvas');
  526. var wrapper = document.createElement('div');
  527. var body = window.document.body;
  528. var chart = new Chart(canvas, {
  529. type: 'line',
  530. options: {
  531. responsive: true,
  532. maintainAspectRatio: false
  533. }
  534. });
  535. expect(chart).toBeChartOfSize({
  536. dw: 0, dh: 0,
  537. rw: 0, rh: 0,
  538. });
  539. wrapper.style.cssText = 'width: 455px; height: 355px';
  540. wrapper.appendChild(canvas);
  541. body.appendChild(wrapper);
  542. waitForResize(chart, function() {
  543. var resizer = wrapper.firstChild;
  544. expect(resizer.className).toBe('chartjs-size-monitor');
  545. expect(resizer.tagName).toBe('DIV');
  546. expect(chart).toBeChartOfSize({
  547. dw: 455, dh: 355,
  548. rw: 455, rh: 355,
  549. });
  550. var target = document.createElement('div');
  551. target.style.cssText = 'width: 640px; height: 480px';
  552. target.appendChild(canvas);
  553. body.appendChild(target);
  554. waitForResize(chart, function() {
  555. expect(target.firstChild).toBe(resizer);
  556. expect(wrapper.firstChild).toBe(null);
  557. expect(chart).toBeChartOfSize({
  558. dw: 640, dh: 480,
  559. rw: 640, rh: 480,
  560. });
  561. body.removeChild(wrapper);
  562. body.removeChild(target);
  563. chart.destroy();
  564. done();
  565. });
  566. });
  567. });
  568. // https://github.com/chartjs/Chart.js/issues/3521
  569. it('should resize the canvas after the wrapper has been re-attached to the DOM', function(done) {
  570. var chart = acquireChart({
  571. options: {
  572. responsive: true,
  573. maintainAspectRatio: false
  574. }
  575. }, {
  576. canvas: {
  577. style: ''
  578. },
  579. wrapper: {
  580. style: 'width: 320px; height: 350px'
  581. }
  582. });
  583. expect(chart).toBeChartOfSize({
  584. dw: 320, dh: 350,
  585. rw: 320, rh: 350,
  586. });
  587. var wrapper = chart.canvas.parentNode;
  588. var parent = wrapper.parentNode;
  589. parent.removeChild(wrapper);
  590. parent.appendChild(wrapper);
  591. wrapper.style.height = '355px';
  592. waitForResize(chart, function() {
  593. expect(chart).toBeChartOfSize({
  594. dw: 320, dh: 355,
  595. rw: 320, rh: 355,
  596. });
  597. parent.removeChild(wrapper);
  598. wrapper.style.width = '455px';
  599. parent.appendChild(wrapper);
  600. waitForResize(chart, function() {
  601. expect(chart).toBeChartOfSize({
  602. dw: 455, dh: 355,
  603. rw: 455, rh: 355,
  604. });
  605. done();
  606. });
  607. });
  608. });
  609. // https://github.com/chartjs/Chart.js/issues/4737
  610. it('should resize the canvas when re-creating the chart', function(done) {
  611. var chart = acquireChart({
  612. options: {
  613. responsive: true
  614. }
  615. }, {
  616. wrapper: {
  617. style: 'width: 320px'
  618. }
  619. });
  620. waitForResize(chart, function() {
  621. var canvas = chart.canvas;
  622. expect(chart).toBeChartOfSize({
  623. dw: 320, dh: 320,
  624. rw: 320, rh: 320,
  625. });
  626. chart.destroy();
  627. chart = new Chart(canvas, {
  628. type: 'line',
  629. options: {
  630. responsive: true
  631. }
  632. });
  633. canvas.parentNode.style.width = '455px';
  634. waitForResize(chart, function() {
  635. expect(chart).toBeChartOfSize({
  636. dw: 455, dh: 455,
  637. rw: 455, rh: 455,
  638. });
  639. done();
  640. });
  641. });
  642. });
  643. });
  644. describe('config.options.responsive: true (maintainAspectRatio: true)', function() {
  645. it('should resize the canvas with correct aspect ratio when parent width changes', function(done) {
  646. var chart = acquireChart({
  647. type: 'line', // AR == 2
  648. options: {
  649. responsive: true,
  650. maintainAspectRatio: true
  651. }
  652. }, {
  653. canvas: {
  654. style: ''
  655. },
  656. wrapper: {
  657. style: 'width: 300px; height: 350px; position: relative'
  658. }
  659. });
  660. expect(chart).toBeChartOfSize({
  661. dw: 300, dh: 150,
  662. rw: 300, rh: 150,
  663. });
  664. var wrapper = chart.canvas.parentNode;
  665. wrapper.style.width = '450px';
  666. waitForResize(chart, function() {
  667. expect(chart).toBeChartOfSize({
  668. dw: 450, dh: 225,
  669. rw: 450, rh: 225,
  670. });
  671. wrapper.style.width = '150px';
  672. waitForResize(chart, function() {
  673. expect(chart).toBeChartOfSize({
  674. dw: 150, dh: 75,
  675. rw: 150, rh: 75,
  676. });
  677. done();
  678. });
  679. });
  680. });
  681. it('should not resize the canvas when parent height changes', function(done) {
  682. var chart = acquireChart({
  683. options: {
  684. responsive: true,
  685. maintainAspectRatio: true
  686. }
  687. }, {
  688. canvas: {
  689. style: ''
  690. },
  691. wrapper: {
  692. style: 'width: 320px; height: 350px; position: relative'
  693. }
  694. });
  695. expect(chart).toBeChartOfSize({
  696. dw: 320, dh: 160,
  697. rw: 320, rh: 160,
  698. });
  699. var wrapper = chart.canvas.parentNode;
  700. wrapper.style.height = '455px';
  701. waitForResize(chart, function() {
  702. expect(chart).toBeChartOfSize({
  703. dw: 320, dh: 160,
  704. rw: 320, rh: 160,
  705. });
  706. wrapper.style.height = '150px';
  707. waitForResize(chart, function() {
  708. expect(chart).toBeChartOfSize({
  709. dw: 320, dh: 160,
  710. rw: 320, rh: 160,
  711. });
  712. done();
  713. });
  714. });
  715. });
  716. });
  717. describe('Retina scale (a.k.a. device pixel ratio)', function() {
  718. beforeEach(function() {
  719. this.devicePixelRatio = window.devicePixelRatio;
  720. window.devicePixelRatio = 3;
  721. });
  722. afterEach(function() {
  723. window.devicePixelRatio = this.devicePixelRatio;
  724. });
  725. // see https://github.com/chartjs/Chart.js/issues/3575
  726. it ('should scale the render size but not the "implicit" display size', function() {
  727. var chart = acquireChart({
  728. options: {
  729. responsive: false
  730. }
  731. }, {
  732. canvas: {
  733. width: 320,
  734. height: 240,
  735. }
  736. });
  737. expect(chart).toBeChartOfSize({
  738. dw: 320, dh: 240,
  739. rw: 960, rh: 720,
  740. });
  741. });
  742. it ('should scale the render size but not the "explicit" display size', function() {
  743. var chart = acquireChart({
  744. options: {
  745. responsive: false
  746. }
  747. }, {
  748. canvas: {
  749. style: 'width: 320px; height: 240px'
  750. }
  751. });
  752. expect(chart).toBeChartOfSize({
  753. dw: 320, dh: 240,
  754. rw: 960, rh: 720,
  755. });
  756. });
  757. });
  758. describe('config.options.devicePixelRatio', function() {
  759. beforeEach(function() {
  760. this.devicePixelRatio = window.devicePixelRatio;
  761. window.devicePixelRatio = 1;
  762. });
  763. afterEach(function() {
  764. window.devicePixelRatio = this.devicePixelRatio;
  765. });
  766. // see https://github.com/chartjs/Chart.js/issues/3575
  767. it ('should scale the render size but not the "implicit" display size', function() {
  768. var chart = acquireChart({
  769. options: {
  770. responsive: false,
  771. devicePixelRatio: 3
  772. }
  773. }, {
  774. canvas: {
  775. width: 320,
  776. height: 240,
  777. }
  778. });
  779. expect(chart).toBeChartOfSize({
  780. dw: 320, dh: 240,
  781. rw: 960, rh: 720,
  782. });
  783. });
  784. it ('should scale the render size but not the "explicit" display size', function() {
  785. var chart = acquireChart({
  786. options: {
  787. responsive: false,
  788. devicePixelRatio: 3
  789. }
  790. }, {
  791. canvas: {
  792. style: 'width: 320px; height: 240px'
  793. }
  794. });
  795. expect(chart).toBeChartOfSize({
  796. dw: 320, dh: 240,
  797. rw: 960, rh: 720,
  798. });
  799. });
  800. });
  801. describe('controller.destroy', function() {
  802. it('should remove the resizer element when responsive: true', function(done) {
  803. var chart = acquireChart({
  804. options: {
  805. responsive: true
  806. }
  807. });
  808. waitForResize(chart, function() {
  809. var wrapper = chart.canvas.parentNode;
  810. var resizer = wrapper.firstChild;
  811. expect(wrapper.childNodes.length).toBe(2);
  812. expect(resizer.className).toBe('chartjs-size-monitor');
  813. expect(resizer.tagName).toBe('DIV');
  814. chart.destroy();
  815. expect(wrapper.childNodes.length).toBe(1);
  816. expect(wrapper.firstChild.tagName).toBe('CANVAS');
  817. done();
  818. });
  819. });
  820. });
  821. describe('controller.reset', function() {
  822. it('should reset the chart elements', function() {
  823. var chart = acquireChart({
  824. type: 'line',
  825. data: {
  826. labels: ['A', 'B', 'C', 'D'],
  827. datasets: [{
  828. data: [10, 20, 30, 0]
  829. }]
  830. },
  831. options: {
  832. responsive: true
  833. }
  834. });
  835. var meta = chart.getDatasetMeta(0);
  836. // Verify that points are at their initial correct location,
  837. // then we will reset and see that they moved
  838. expect(meta.data[0]._model.y).toBeCloseToPixel(333);
  839. expect(meta.data[1]._model.y).toBeCloseToPixel(183);
  840. expect(meta.data[2]._model.y).toBeCloseToPixel(32);
  841. expect(meta.data[3]._model.y).toBeCloseToPixel(482);
  842. chart.reset();
  843. // For a line chart, the animation state is the bottom
  844. expect(meta.data[0]._model.y).toBeCloseToPixel(482);
  845. expect(meta.data[1]._model.y).toBeCloseToPixel(482);
  846. expect(meta.data[2]._model.y).toBeCloseToPixel(482);
  847. expect(meta.data[3]._model.y).toBeCloseToPixel(482);
  848. });
  849. });
  850. describe('config update', function() {
  851. it ('should update options', function() {
  852. var chart = acquireChart({
  853. type: 'line',
  854. data: {
  855. labels: ['A', 'B', 'C', 'D'],
  856. datasets: [{
  857. data: [10, 20, 30, 100]
  858. }]
  859. },
  860. options: {
  861. responsive: true
  862. }
  863. });
  864. chart.options = {
  865. responsive: false,
  866. scales: {
  867. yAxes: [{
  868. ticks: {
  869. min: 0,
  870. max: 10
  871. }
  872. }]
  873. }
  874. };
  875. chart.update();
  876. var yScale = chart.scales['y-axis-0'];
  877. expect(yScale.options.ticks.min).toBe(0);
  878. expect(yScale.options.ticks.max).toBe(10);
  879. });
  880. it ('should update scales options', function() {
  881. var chart = acquireChart({
  882. type: 'line',
  883. data: {
  884. labels: ['A', 'B', 'C', 'D'],
  885. datasets: [{
  886. data: [10, 20, 30, 100]
  887. }]
  888. },
  889. options: {
  890. responsive: true
  891. }
  892. });
  893. chart.options.scales.yAxes[0].ticks.min = 0;
  894. chart.options.scales.yAxes[0].ticks.max = 10;
  895. chart.update();
  896. var yScale = chart.scales['y-axis-0'];
  897. expect(yScale.options.ticks.min).toBe(0);
  898. expect(yScale.options.ticks.max).toBe(10);
  899. });
  900. it ('should update scales options from new object', function() {
  901. var chart = acquireChart({
  902. type: 'line',
  903. data: {
  904. labels: ['A', 'B', 'C', 'D'],
  905. datasets: [{
  906. data: [10, 20, 30, 100]
  907. }]
  908. },
  909. options: {
  910. responsive: true
  911. }
  912. });
  913. var newScalesConfig = {
  914. yAxes: [{
  915. ticks: {
  916. min: 0,
  917. max: 10
  918. }
  919. }]
  920. };
  921. chart.options.scales = newScalesConfig;
  922. chart.update();
  923. var yScale = chart.scales['y-axis-0'];
  924. expect(yScale.options.ticks.min).toBe(0);
  925. expect(yScale.options.ticks.max).toBe(10);
  926. });
  927. it ('should assign unique scale IDs', function() {
  928. var chart = acquireChart({
  929. type: 'line',
  930. options: {
  931. scales: {
  932. xAxes: [{id: 'x-axis-0'}, {}, {}],
  933. yAxes: [{id: 'y-axis-1'}, {}, {}]
  934. }
  935. }
  936. });
  937. expect(Object.keys(chart.scales).sort()).toEqual([
  938. 'x-axis-0', 'x-axis-1', 'x-axis-2',
  939. 'y-axis-1', 'y-axis-2', 'y-axis-3'
  940. ]);
  941. });
  942. it ('should remove discarded scale', function() {
  943. var chart = acquireChart({
  944. type: 'line',
  945. data: {
  946. labels: ['A', 'B', 'C', 'D'],
  947. datasets: [{
  948. data: [10, 20, 30, 100]
  949. }]
  950. },
  951. options: {
  952. responsive: true,
  953. scales: {
  954. yAxes: [{
  955. id: 'yAxis0',
  956. ticks: {
  957. min: 0,
  958. max: 10
  959. }
  960. }]
  961. }
  962. }
  963. });
  964. var newScalesConfig = {
  965. yAxes: [{
  966. ticks: {
  967. min: 0,
  968. max: 10
  969. }
  970. }]
  971. };
  972. chart.options.scales = newScalesConfig;
  973. chart.update();
  974. var yScale = chart.scales.yAxis0;
  975. expect(yScale).toBeUndefined();
  976. var newyScale = chart.scales['y-axis-0'];
  977. expect(newyScale.options.ticks.min).toBe(0);
  978. expect(newyScale.options.ticks.max).toBe(10);
  979. });
  980. it ('should update tooltip options', function() {
  981. var chart = acquireChart({
  982. type: 'line',
  983. data: {
  984. labels: ['A', 'B', 'C', 'D'],
  985. datasets: [{
  986. data: [10, 20, 30, 100]
  987. }]
  988. },
  989. options: {
  990. responsive: true
  991. }
  992. });
  993. var newTooltipConfig = {
  994. mode: 'dataset',
  995. intersect: false
  996. };
  997. chart.options.tooltips = newTooltipConfig;
  998. chart.update();
  999. expect(chart.tooltip._options).toEqual(jasmine.objectContaining(newTooltipConfig));
  1000. });
  1001. it ('should reset the tooltip on update', function() {
  1002. var chart = acquireChart({
  1003. type: 'line',
  1004. data: {
  1005. labels: ['A', 'B', 'C', 'D'],
  1006. datasets: [{
  1007. data: [10, 20, 30, 100]
  1008. }]
  1009. },
  1010. options: {
  1011. responsive: true,
  1012. tooltip: {
  1013. mode: 'nearest'
  1014. }
  1015. }
  1016. });
  1017. // Trigger an event over top of a point to
  1018. // put an item into the tooltip
  1019. var meta = chart.getDatasetMeta(0);
  1020. var point = meta.data[1];
  1021. jasmine.triggerMouseEvent(chart, 'mousemove', point);
  1022. // Check and see if tooltip was displayed
  1023. var tooltip = chart.tooltip;
  1024. expect(chart.lastActive).toEqual([point]);
  1025. expect(tooltip._lastActive).toEqual([point]);
  1026. // Update and confirm tooltip is reset
  1027. chart.update();
  1028. expect(chart.lastActive).toEqual([]);
  1029. expect(tooltip._lastActive).toEqual([]);
  1030. });
  1031. it ('should update the metadata', function() {
  1032. var cfg = {
  1033. data: {
  1034. labels: ['A', 'B', 'C', 'D'],
  1035. datasets: [{
  1036. type: 'line',
  1037. data: [10, 20, 30, 0]
  1038. }]
  1039. },
  1040. options: {
  1041. responsive: true,
  1042. scales: {
  1043. xAxes: [{
  1044. type: 'category'
  1045. }],
  1046. yAxes: [{
  1047. scaleLabel: {
  1048. display: true,
  1049. labelString: 'Value'
  1050. }
  1051. }]
  1052. }
  1053. }
  1054. };
  1055. var chart = acquireChart(cfg);
  1056. var meta = chart.getDatasetMeta(0);
  1057. expect(meta.type).toBe('line');
  1058. // change the dataset to bar and check that meta was updated
  1059. chart.config.data.datasets[0].type = 'bar';
  1060. chart.update();
  1061. meta = chart.getDatasetMeta(0);
  1062. expect(meta.type).toBe('bar');
  1063. });
  1064. });
  1065. describe('plugin.extensions', function() {
  1066. it ('should notify plugin in correct order', function(done) {
  1067. var plugin = this.plugin = {};
  1068. var sequence = [];
  1069. var hooks = {
  1070. init: [
  1071. 'beforeInit',
  1072. 'afterInit'
  1073. ],
  1074. update: [
  1075. 'beforeUpdate',
  1076. 'beforeLayout',
  1077. 'afterLayout',
  1078. 'beforeDatasetsUpdate',
  1079. 'beforeDatasetUpdate',
  1080. 'afterDatasetUpdate',
  1081. 'afterDatasetsUpdate',
  1082. 'afterUpdate',
  1083. ],
  1084. render: [
  1085. 'beforeRender',
  1086. 'beforeDraw',
  1087. 'beforeDatasetsDraw',
  1088. 'beforeDatasetDraw',
  1089. 'afterDatasetDraw',
  1090. 'afterDatasetsDraw',
  1091. 'beforeTooltipDraw',
  1092. 'afterTooltipDraw',
  1093. 'afterDraw',
  1094. 'afterRender',
  1095. ],
  1096. resize: [
  1097. 'resize'
  1098. ],
  1099. destroy: [
  1100. 'destroy'
  1101. ]
  1102. };
  1103. Object.keys(hooks).forEach(function(group) {
  1104. hooks[group].forEach(function(name) {
  1105. plugin[name] = function() {
  1106. sequence.push(name);
  1107. };
  1108. });
  1109. });
  1110. var chart = window.acquireChart({
  1111. type: 'line',
  1112. data: {datasets: [{}]},
  1113. plugins: [plugin],
  1114. options: {
  1115. responsive: true
  1116. }
  1117. }, {
  1118. wrapper: {
  1119. style: 'width: 300px'
  1120. }
  1121. });
  1122. chart.canvas.parentNode.style.width = '400px';
  1123. waitForResize(chart, function() {
  1124. chart.destroy();
  1125. expect(sequence).toEqual([].concat(
  1126. hooks.init,
  1127. hooks.update,
  1128. hooks.render,
  1129. hooks.resize,
  1130. hooks.update,
  1131. hooks.render,
  1132. hooks.destroy
  1133. ));
  1134. done();
  1135. });
  1136. });
  1137. it('should not notify before/afterDatasetDraw if dataset is hidden', function() {
  1138. var sequence = [];
  1139. var plugin = this.plugin = {
  1140. beforeDatasetDraw: function(chart, args) {
  1141. sequence.push('before-' + args.index);
  1142. },
  1143. afterDatasetDraw: function(chart, args) {
  1144. sequence.push('after-' + args.index);
  1145. }
  1146. };
  1147. window.acquireChart({
  1148. type: 'line',
  1149. data: {datasets: [{}, {hidden: true}, {}]},
  1150. plugins: [plugin]
  1151. });
  1152. expect(sequence).toEqual([
  1153. 'before-2', 'after-2',
  1154. 'before-0', 'after-0'
  1155. ]);
  1156. });
  1157. });
  1158. describe('controller.update', function() {
  1159. beforeEach(function() {
  1160. this.chart = acquireChart({
  1161. type: 'doughnut',
  1162. options: {
  1163. animation: {
  1164. easing: 'linear',
  1165. duration: 500
  1166. }
  1167. }
  1168. });
  1169. this.addAnimationSpy = spyOn(Chart.animationService, 'addAnimation');
  1170. });
  1171. it('should add an animation with the default options', function() {
  1172. this.chart.update();
  1173. expect(this.addAnimationSpy).toHaveBeenCalledWith(
  1174. this.chart,
  1175. jasmine.objectContaining({easing: 'linear'}),
  1176. 500,
  1177. undefined
  1178. );
  1179. });
  1180. it('should add an animation with the provided options', function() {
  1181. this.chart.update({
  1182. duration: 800,
  1183. easing: 'easeOutBounce',
  1184. lazy: false,
  1185. });
  1186. expect(this.addAnimationSpy).toHaveBeenCalledWith(
  1187. this.chart,
  1188. jasmine.objectContaining({easing: 'easeOutBounce'}),
  1189. 800,
  1190. false
  1191. );
  1192. });
  1193. });
  1194. });