graph_specs.ts 14 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460
  1. ///<reference path="../../../../headers/common.d.ts" />
  2. import {
  3. describe,
  4. beforeEach,
  5. it,
  6. sinon,
  7. expect,
  8. angularMocks
  9. } from "../../../../../test/lib/common";
  10. import "../module";
  11. import angular from "angular";
  12. import $ from "jquery";
  13. import helpers from "test/specs/helpers";
  14. import TimeSeries from "app/core/time_series2";
  15. import moment from "moment";
  16. import { Emitter } from "app/core/core";
  17. describe("grafanaGraph", function() {
  18. beforeEach(angularMocks.module("grafana.core"));
  19. function graphScenario(desc, func, elementWidth = 500) {
  20. describe(desc, () => {
  21. var ctx: any = {};
  22. ctx.setup = setupFunc => {
  23. beforeEach(
  24. angularMocks.module($provide => {
  25. $provide.value("timeSrv", new helpers.TimeSrvStub());
  26. })
  27. );
  28. beforeEach(
  29. angularMocks.inject(($rootScope, $compile) => {
  30. var ctrl: any = {
  31. height: 200,
  32. panel: {
  33. events: new Emitter(),
  34. legend: {},
  35. grid: {},
  36. yaxes: [
  37. {
  38. min: null,
  39. max: null,
  40. format: "short",
  41. logBase: 1
  42. },
  43. {
  44. min: null,
  45. max: null,
  46. format: "short",
  47. logBase: 1
  48. }
  49. ],
  50. thresholds: [],
  51. xaxis: {},
  52. seriesOverrides: [],
  53. tooltip: {
  54. shared: true
  55. }
  56. },
  57. renderingCompleted: sinon.spy(),
  58. hiddenSeries: {},
  59. dashboard: {
  60. getTimezone: sinon.stub().returns("browser")
  61. },
  62. range: {
  63. from: moment([2015, 1, 1, 10]),
  64. to: moment([2015, 1, 1, 22])
  65. }
  66. };
  67. var scope = $rootScope.$new();
  68. scope.ctrl = ctrl;
  69. scope.ctrl.events = ctrl.panel.events;
  70. $rootScope.onAppEvent = sinon.spy();
  71. ctx.data = [];
  72. ctx.data.push(
  73. new TimeSeries({
  74. datapoints: [[1, 1], [2, 2]],
  75. alias: "series1"
  76. })
  77. );
  78. ctx.data.push(
  79. new TimeSeries({
  80. datapoints: [[10, 1], [20, 2]],
  81. alias: "series2"
  82. })
  83. );
  84. setupFunc(ctrl, ctx.data);
  85. var element = angular.element(
  86. "<div style='width:" + elementWidth + "px' grafana-graph><div>"
  87. );
  88. $compile(element)(scope);
  89. scope.$digest();
  90. $.plot = ctx.plotSpy = sinon.spy();
  91. ctrl.events.emit("render", ctx.data);
  92. ctrl.events.emit("render-legend");
  93. ctrl.events.emit("legend-rendering-complete");
  94. ctx.plotData = ctx.plotSpy.getCall(0).args[1];
  95. ctx.plotOptions = ctx.plotSpy.getCall(0).args[2];
  96. })
  97. );
  98. };
  99. func(ctx);
  100. });
  101. }
  102. graphScenario("simple lines options", ctx => {
  103. ctx.setup(ctrl => {
  104. ctrl.panel.lines = true;
  105. ctrl.panel.fill = 5;
  106. ctrl.panel.linewidth = 3;
  107. ctrl.panel.steppedLine = true;
  108. });
  109. it("should configure plot with correct options", () => {
  110. expect(ctx.plotOptions.series.lines.show).to.be(true);
  111. expect(ctx.plotOptions.series.lines.fill).to.be(0.5);
  112. expect(ctx.plotOptions.series.lines.lineWidth).to.be(3);
  113. expect(ctx.plotOptions.series.lines.steps).to.be(true);
  114. });
  115. });
  116. graphScenario("sorting stacked series as legend. disabled", ctx => {
  117. ctx.setup(ctrl => {
  118. ctrl.panel.legend.sort = undefined;
  119. ctrl.panel.stack = false;
  120. });
  121. it("should not modify order of time series", () => {
  122. expect(ctx.plotData[0].alias).to.be("series1");
  123. expect(ctx.plotData[1].alias).to.be("series2");
  124. });
  125. });
  126. graphScenario(
  127. "sorting stacked series as legend. min descending order",
  128. ctx => {
  129. ctx.setup(ctrl => {
  130. ctrl.panel.legend.sort = "min";
  131. ctrl.panel.legend.sortDesc = true;
  132. ctrl.panel.stack = true;
  133. });
  134. it("highest value should be first", () => {
  135. expect(ctx.plotData[0].alias).to.be("series2");
  136. expect(ctx.plotData[1].alias).to.be("series1");
  137. });
  138. }
  139. );
  140. graphScenario(
  141. "sorting stacked series as legend. min ascending order",
  142. ctx => {
  143. ctx.setup((ctrl, data) => {
  144. ctrl.panel.legend.sort = "min";
  145. ctrl.panel.legend.sortDesc = false;
  146. ctrl.panel.stack = true;
  147. });
  148. it("lowest value should be first", () => {
  149. expect(ctx.plotData[0].alias).to.be("series1");
  150. expect(ctx.plotData[1].alias).to.be("series2");
  151. });
  152. }
  153. );
  154. graphScenario("sorting stacked series as legend. stacking disabled", ctx => {
  155. ctx.setup(ctrl => {
  156. ctrl.panel.legend.sort = "min";
  157. ctrl.panel.legend.sortDesc = true;
  158. ctrl.panel.stack = false;
  159. });
  160. it("highest value should be first", () => {
  161. expect(ctx.plotData[0].alias).to.be("series1");
  162. expect(ctx.plotData[1].alias).to.be("series2");
  163. });
  164. });
  165. graphScenario(
  166. "sorting stacked series as legend. current descending order",
  167. ctx => {
  168. ctx.setup(ctrl => {
  169. ctrl.panel.legend.sort = "current";
  170. ctrl.panel.legend.sortDesc = true;
  171. ctrl.panel.stack = true;
  172. });
  173. it("highest last value should be first", () => {
  174. expect(ctx.plotData[0].alias).to.be("series2");
  175. expect(ctx.plotData[1].alias).to.be("series1");
  176. });
  177. }
  178. );
  179. graphScenario("when logBase is log 10", function(ctx) {
  180. ctx.setup(function(ctrl, data) {
  181. ctrl.panel.yaxes[0].logBase = 10;
  182. data[0] = new TimeSeries({
  183. datapoints: [[2000, 1], [0.002, 2], [0, 3], [-1, 4]],
  184. alias: "seriesAutoscale"
  185. });
  186. data[0].yaxis = 1;
  187. ctrl.panel.yaxes[1].logBase = 10;
  188. ctrl.panel.yaxes[1].min = "0.05";
  189. ctrl.panel.yaxes[1].max = "1500";
  190. data[1] = new TimeSeries({
  191. datapoints: [[2000, 1], [0.002, 2], [0, 3], [-1, 4]],
  192. alias: "seriesFixedscale"
  193. });
  194. data[1].yaxis = 2;
  195. });
  196. it("should apply axis transform, autoscaling (if necessary) and ticks", function() {
  197. var axisAutoscale = ctx.plotOptions.yaxes[0];
  198. expect(axisAutoscale.transform(100)).to.be(2);
  199. expect(axisAutoscale.inverseTransform(-3)).to.within(
  200. 0.00099999999,
  201. 0.00100000001
  202. );
  203. expect(axisAutoscale.min).to.within(0.00099999999, 0.00100000001);
  204. expect(axisAutoscale.max).to.be(10000);
  205. expect(axisAutoscale.ticks.length).to.within(7, 8);
  206. expect(axisAutoscale.ticks[0]).to.within(0.00099999999, 0.00100000001);
  207. if (axisAutoscale.ticks.length === 7) {
  208. expect(axisAutoscale.ticks[axisAutoscale.ticks.length - 1]).to.within(
  209. 999.9999,
  210. 1000.0001
  211. );
  212. } else {
  213. expect(axisAutoscale.ticks[axisAutoscale.ticks.length - 1]).to.be(
  214. 10000
  215. );
  216. }
  217. var axisFixedscale = ctx.plotOptions.yaxes[1];
  218. expect(axisFixedscale.min).to.be(0.05);
  219. expect(axisFixedscale.max).to.be(1500);
  220. expect(axisFixedscale.ticks.length).to.be(5);
  221. expect(axisFixedscale.ticks[0]).to.be(0.1);
  222. expect(axisFixedscale.ticks[4]).to.be(1000);
  223. });
  224. });
  225. graphScenario(
  226. "when logBase is log 10 and data points contain only zeroes",
  227. function(ctx) {
  228. ctx.setup(function(ctrl, data) {
  229. ctrl.panel.yaxes[0].logBase = 10;
  230. data[0] = new TimeSeries({
  231. datapoints: [[0, 1], [0, 2], [0, 3], [0, 4]],
  232. alias: "seriesAutoscale"
  233. });
  234. data[0].yaxis = 1;
  235. });
  236. it("should not set min and max and should create some fake ticks", function() {
  237. var axisAutoscale = ctx.plotOptions.yaxes[0];
  238. expect(axisAutoscale.transform(100)).to.be(2);
  239. expect(axisAutoscale.inverseTransform(-3)).to.within(
  240. 0.00099999999,
  241. 0.00100000001
  242. );
  243. expect(axisAutoscale.min).to.be(undefined);
  244. expect(axisAutoscale.max).to.be(undefined);
  245. expect(axisAutoscale.ticks.length).to.be(2);
  246. expect(axisAutoscale.ticks[0]).to.be(1);
  247. expect(axisAutoscale.ticks[1]).to.be(2);
  248. });
  249. }
  250. );
  251. // y-min set 0 is a special case for log scale,
  252. // this approximates it by setting min to 0.1
  253. graphScenario(
  254. "when logBase is log 10 and y-min is set to 0 and auto min is > 0.1",
  255. function(ctx) {
  256. ctx.setup(function(ctrl, data) {
  257. ctrl.panel.yaxes[0].logBase = 10;
  258. ctrl.panel.yaxes[0].min = "0";
  259. data[0] = new TimeSeries({
  260. datapoints: [[2000, 1], [4, 2], [500, 3], [3000, 4]],
  261. alias: "seriesAutoscale"
  262. });
  263. data[0].yaxis = 1;
  264. });
  265. it("should set min to 0.1 and add a tick for 0.1", function() {
  266. var axisAutoscale = ctx.plotOptions.yaxes[0];
  267. expect(axisAutoscale.transform(100)).to.be(2);
  268. expect(axisAutoscale.inverseTransform(-3)).to.within(
  269. 0.00099999999,
  270. 0.00100000001
  271. );
  272. expect(axisAutoscale.min).to.be(0.1);
  273. expect(axisAutoscale.max).to.be(10000);
  274. expect(axisAutoscale.ticks.length).to.be(6);
  275. expect(axisAutoscale.ticks[0]).to.be(0.1);
  276. expect(axisAutoscale.ticks[5]).to.be(10000);
  277. });
  278. }
  279. );
  280. graphScenario(
  281. "when logBase is log 2 and y-min is set to 0 and num of ticks exceeds max",
  282. function(ctx) {
  283. ctx.setup(function(ctrl, data) {
  284. const heightForApprox5Ticks = 125;
  285. ctrl.height = heightForApprox5Ticks;
  286. ctrl.panel.yaxes[0].logBase = 2;
  287. ctrl.panel.yaxes[0].min = "0";
  288. data[0] = new TimeSeries({
  289. datapoints: [
  290. [2000, 1],
  291. [4, 2],
  292. [500, 3],
  293. [3000, 4],
  294. [10000, 5],
  295. [100000, 6]
  296. ],
  297. alias: "seriesAutoscale"
  298. });
  299. data[0].yaxis = 1;
  300. });
  301. it("should regenerate ticks so that if fits on the y-axis", function() {
  302. var axisAutoscale = ctx.plotOptions.yaxes[0];
  303. expect(axisAutoscale.min).to.be(0.1);
  304. expect(axisAutoscale.ticks.length).to.be(8);
  305. expect(axisAutoscale.ticks[0]).to.be(0.1);
  306. expect(axisAutoscale.ticks[7]).to.be(262144);
  307. expect(axisAutoscale.max).to.be(262144);
  308. });
  309. it("should set axis max to be max tick value", function() {
  310. expect(ctx.plotOptions.yaxes[0].max).to.be(262144);
  311. });
  312. }
  313. );
  314. graphScenario("dashed lines options", function(ctx) {
  315. ctx.setup(function(ctrl) {
  316. ctrl.panel.lines = true;
  317. ctrl.panel.linewidth = 2;
  318. ctrl.panel.dashes = true;
  319. });
  320. it("should configure dashed plot with correct options", function() {
  321. expect(ctx.plotOptions.series.lines.show).to.be(true);
  322. expect(ctx.plotOptions.series.dashes.lineWidth).to.be(2);
  323. expect(ctx.plotOptions.series.dashes.show).to.be(true);
  324. });
  325. });
  326. graphScenario("should use timeStep for barWidth", function(ctx) {
  327. ctx.setup(function(ctrl, data) {
  328. ctrl.panel.bars = true;
  329. data[0] = new TimeSeries({
  330. datapoints: [[1, 10], [2, 20]],
  331. alias: "series1"
  332. });
  333. });
  334. it("should set barWidth", function() {
  335. expect(ctx.plotOptions.series.bars.barWidth).to.be(1 / 1.5);
  336. });
  337. });
  338. graphScenario("series option overrides, fill & points", function(ctx) {
  339. ctx.setup(function(ctrl, data) {
  340. ctrl.panel.lines = true;
  341. ctrl.panel.fill = 5;
  342. data[0].zindex = 10;
  343. data[1].alias = "test";
  344. data[1].lines = { fill: 0.001 };
  345. data[1].points = { show: true };
  346. });
  347. it("should match second series and fill zero, and enable points", function() {
  348. expect(ctx.plotOptions.series.lines.fill).to.be(0.5);
  349. expect(ctx.plotData[1].lines.fill).to.be(0.001);
  350. expect(ctx.plotData[1].points.show).to.be(true);
  351. });
  352. });
  353. graphScenario("should order series order according to zindex", function(ctx) {
  354. ctx.setup(function(ctrl, data) {
  355. data[1].zindex = 1;
  356. data[0].zindex = 10;
  357. });
  358. it("should move zindex 2 last", function() {
  359. expect(ctx.plotData[0].alias).to.be("series2");
  360. expect(ctx.plotData[1].alias).to.be("series1");
  361. });
  362. });
  363. graphScenario("when series is hidden", function(ctx) {
  364. ctx.setup(function(ctrl) {
  365. ctrl.hiddenSeries = { series2: true };
  366. });
  367. it("should remove datapoints and disable stack", function() {
  368. expect(ctx.plotData[0].alias).to.be("series1");
  369. expect(ctx.plotData[1].data.length).to.be(0);
  370. expect(ctx.plotData[1].stack).to.be(false);
  371. });
  372. });
  373. graphScenario("when stack and percent", function(ctx) {
  374. ctx.setup(function(ctrl) {
  375. ctrl.panel.percentage = true;
  376. ctrl.panel.stack = true;
  377. });
  378. it("should show percentage", function() {
  379. var axis = ctx.plotOptions.yaxes[0];
  380. expect(axis.tickFormatter(100, axis)).to.be("100%");
  381. });
  382. });
  383. graphScenario(
  384. "when panel too narrow to show x-axis dates in same granularity as wide panels",
  385. function(ctx) {
  386. describe("and the range is less than 24 hours", function() {
  387. ctx.setup(function(ctrl) {
  388. ctrl.range.from = moment([2015, 1, 1, 10]);
  389. ctrl.range.to = moment([2015, 1, 1, 22]);
  390. });
  391. it("should format dates as hours minutes", function() {
  392. var axis = ctx.plotOptions.xaxis;
  393. expect(axis.timeformat).to.be("%H:%M");
  394. });
  395. });
  396. describe("and the range is less than one year", function() {
  397. ctx.setup(function(scope) {
  398. scope.range.from = moment([2015, 1, 1]);
  399. scope.range.to = moment([2015, 11, 20]);
  400. });
  401. it("should format dates as month days", function() {
  402. var axis = ctx.plotOptions.xaxis;
  403. expect(axis.timeformat).to.be("%m/%d");
  404. });
  405. });
  406. },
  407. 10
  408. );
  409. });