1import TimeSeries, { updateLegendValues } from 'app/core/time_series2';
2
3describe('TimeSeries', () => {
4  let points, series: any;
5  const yAxisFormats = ['short', 'ms'];
6  let testData: { alias?: string; datapoints: any };
7
8  beforeEach(() => {
9    testData = {
10      alias: 'test',
11      datapoints: [
12        [1, 2],
13        [null, 3],
14        [10, 4],
15        [8, 5],
16      ],
17    };
18  });
19
20  describe('when getting flot pairs', () => {
21    it('with connected style, should ignore nulls', () => {
22      series = new TimeSeries(testData);
23      points = series.getFlotPairs('connected', yAxisFormats);
24      expect(points.length).toBe(3);
25    });
26
27    it('with null as zero style, should replace nulls with zero', () => {
28      series = new TimeSeries(testData);
29      points = series.getFlotPairs('null as zero', yAxisFormats);
30      expect(points.length).toBe(4);
31      expect(points[1][1]).toBe(0);
32    });
33
34    it('if last is null current should pick next to last', () => {
35      series = new TimeSeries({
36        datapoints: [
37          [10, 1],
38          [null, 2],
39        ],
40      });
41      series.getFlotPairs('null', yAxisFormats);
42      expect(series.stats.current).toBe(10);
43    });
44
45    it('max value should work for negative values', () => {
46      series = new TimeSeries({
47        datapoints: [
48          [-10, 1],
49          [-4, 2],
50        ],
51      });
52      series.getFlotPairs('null', yAxisFormats);
53      expect(series.stats.max).toBe(-4);
54    });
55
56    it('average value should ignore nulls', () => {
57      series = new TimeSeries(testData);
58      series.getFlotPairs('null', yAxisFormats);
59      expect(series.stats.avg).toBe(6.333333333333333);
60    });
61
62    it('the delta value should account for nulls', () => {
63      series = new TimeSeries({
64        datapoints: [
65          [1, 2],
66          [3, 3],
67          [null, 4],
68          [10, 5],
69          [15, 6],
70        ],
71      });
72      series.getFlotPairs('null', yAxisFormats);
73      expect(series.stats.delta).toBe(14);
74    });
75
76    it('the delta value should account for nulls on first', () => {
77      series = new TimeSeries({
78        datapoints: [
79          [null, 2],
80          [1, 3],
81          [10, 4],
82          [15, 5],
83        ],
84      });
85      series.getFlotPairs('null', yAxisFormats);
86      expect(series.stats.delta).toBe(14);
87    });
88
89    it('the delta value should account for nulls on last', () => {
90      series = new TimeSeries({
91        datapoints: [
92          [1, 2],
93          [5, 3],
94          [10, 4],
95          [null, 5],
96        ],
97      });
98      series.getFlotPairs('null', yAxisFormats);
99      expect(series.stats.delta).toBe(9);
100    });
101
102    it('the delta value should account for resets', () => {
103      series = new TimeSeries({
104        datapoints: [
105          [1, 2],
106          [5, 3],
107          [10, 4],
108          [0, 5],
109          [10, 6],
110        ],
111      });
112      series.getFlotPairs('null', yAxisFormats);
113      expect(series.stats.delta).toBe(19);
114    });
115
116    it('the delta value should account for resets on last', () => {
117      series = new TimeSeries({
118        datapoints: [
119          [1, 2],
120          [2, 3],
121          [10, 4],
122          [8, 5],
123        ],
124      });
125      series.getFlotPairs('null', yAxisFormats);
126      expect(series.stats.delta).toBe(17);
127    });
128
129    it('the range value should be max - min', () => {
130      series = new TimeSeries(testData);
131      series.getFlotPairs('null', yAxisFormats);
132      expect(series.stats.range).toBe(9);
133    });
134
135    it('first value should ingone nulls', () => {
136      series = new TimeSeries(testData);
137      series.getFlotPairs('null', yAxisFormats);
138      expect(series.stats.first).toBe(1);
139      series = new TimeSeries({
140        datapoints: [
141          [null, 2],
142          [1, 3],
143          [10, 4],
144          [8, 5],
145        ],
146      });
147      series.getFlotPairs('null', yAxisFormats);
148      expect(series.stats.first).toBe(1);
149    });
150
151    it('with null as zero style, average value should treat nulls as 0', () => {
152      series = new TimeSeries(testData);
153      series.getFlotPairs('null as zero', yAxisFormats);
154      expect(series.stats.avg).toBe(4.75);
155    });
156
157    it('average value should be null if all values is null', () => {
158      series = new TimeSeries({
159        datapoints: [
160          [null, 2],
161          [null, 3],
162          [null, 4],
163          [null, 5],
164        ],
165      });
166      series.getFlotPairs('null');
167      expect(series.stats.avg).toBe(null);
168    });
169
170    it('calculates timeStep', () => {
171      series = new TimeSeries({
172        datapoints: [
173          [null, 1],
174          [null, 2],
175          [null, 3],
176        ],
177      });
178      series.getFlotPairs('null');
179      expect(series.stats.timeStep).toBe(1);
180
181      series = new TimeSeries({
182        datapoints: [
183          [0, 1530529290],
184          [0, 1530529305],
185          [0, 1530529320],
186        ],
187      });
188      series.getFlotPairs('null');
189      expect(series.stats.timeStep).toBe(15);
190    });
191  });
192
193  describe('When checking if ms resolution is needed', () => {
194    describe('msResolution with second resolution timestamps', () => {
195      beforeEach(() => {
196        series = new TimeSeries({
197          datapoints: [
198            [45, 1234567890],
199            [60, 1234567899],
200          ],
201        });
202      });
203
204      it('should set hasMsResolution to false', () => {
205        expect(series.hasMsResolution).toBe(false);
206      });
207    });
208
209    describe('msResolution with millisecond resolution timestamps', () => {
210      beforeEach(() => {
211        series = new TimeSeries({
212          datapoints: [
213            [55, 1236547890001],
214            [90, 1234456709000],
215          ],
216        });
217      });
218
219      it('should show millisecond resolution tooltip', () => {
220        expect(series.hasMsResolution).toBe(true);
221      });
222    });
223
224    describe('msResolution with millisecond resolution timestamps but with trailing zeroes', () => {
225      beforeEach(() => {
226        series = new TimeSeries({
227          datapoints: [
228            [45, 1234567890000],
229            [60, 1234567899000],
230          ],
231        });
232      });
233
234      it('should not show millisecond resolution tooltip', () => {
235        expect(series.hasMsResolution).toBe(false);
236      });
237    });
238  });
239
240  describe('can detect if series contains ms precision', () => {
241    let fakedata: any;
242
243    beforeEach(() => {
244      fakedata = testData;
245    });
246
247    it('missing datapoint with ms precision', () => {
248      fakedata.datapoints[0] = [1337, 1234567890000];
249      series = new TimeSeries(fakedata);
250      expect(series.isMsResolutionNeeded()).toBe(false);
251    });
252
253    it('contains datapoint with ms precision', () => {
254      fakedata.datapoints[0] = [1337, 1236547890001];
255      series = new TimeSeries(fakedata);
256      expect(series.isMsResolutionNeeded()).toBe(true);
257    });
258  });
259
260  describe('series overrides', () => {
261    let series: any;
262    beforeEach(() => {
263      series = new TimeSeries(testData);
264    });
265
266    describe('fill & points', () => {
267      beforeEach(() => {
268        series.alias = 'test';
269        series.applySeriesOverrides([{ alias: 'test', fill: 0, points: true }]);
270      });
271
272      it('should set fill zero, and enable points', () => {
273        expect(series.lines.fill).toBe(0.001);
274        expect(series.points.show).toBe(true);
275      });
276    });
277
278    describe('fillGradient', () => {
279      beforeEach(() => {
280        series.alias = 'test';
281        series.applySeriesOverrides([{ alias: 'test', fill: 10, fillGradient: 10 }]);
282      });
283
284      it('should set fillcolor to gradient', () => {
285        expect(series.lines.fillColor).toMatchObject({
286          colors: [{ opacity: 0.0 }, { opacity: 1 }],
287        });
288      });
289    });
290
291    describe('series option overrides, bars, true & lines false', () => {
292      beforeEach(() => {
293        series.alias = 'test';
294        series.applySeriesOverrides([{ alias: 'test', bars: true, lines: false }]);
295      });
296
297      it('should disable lines, and enable bars', () => {
298        expect(series.lines.show).toBe(false);
299        expect(series.bars.show).toBe(true);
300      });
301    });
302
303    describe('series option overrides, linewidth, stack', () => {
304      beforeEach(() => {
305        series.alias = 'test';
306        series.applySeriesOverrides([{ alias: 'test', linewidth: 5, stack: false }]);
307      });
308
309      it('should disable stack, and set lineWidth', () => {
310        expect(series.stack).toBe(false);
311        expect(series.lines.lineWidth).toBe(5);
312      });
313    });
314
315    describe('series option overrides, dashes and lineWidth', () => {
316      beforeEach(() => {
317        series.alias = 'test';
318        series.applySeriesOverrides([{ alias: 'test', linewidth: 5, dashes: true }]);
319      });
320
321      it('should enable dashes, set dashes lineWidth to 5 and lines lineWidth to 0', () => {
322        expect(series.dashes.show).toBe(true);
323        expect(series.dashes.lineWidth).toBe(5);
324        expect(series.lines.lineWidth).toBe(0);
325      });
326    });
327
328    describe('series option overrides, fill below to', () => {
329      beforeEach(() => {
330        series.alias = 'test';
331        series.applySeriesOverrides([{ alias: 'test', fillBelowTo: 'min' }]);
332      });
333
334      it('should disable line fill and add fillBelowTo', () => {
335        expect(series.fillBelowTo).toBe('min');
336      });
337    });
338
339    describe('series option overrides, pointradius, steppedLine', () => {
340      beforeEach(() => {
341        series.alias = 'test';
342        series.applySeriesOverrides([{ alias: 'test', pointradius: 5, steppedLine: true }]);
343      });
344
345      it('should set pointradius, and set steppedLine', () => {
346        expect(series.points.radius).toBe(5);
347        expect(series.lines.steps).toBe(true);
348      });
349    });
350
351    describe('override match on regex', () => {
352      beforeEach(() => {
353        series.alias = 'test_01';
354        series.applySeriesOverrides([{ alias: '/.*01/', lines: false }]);
355      });
356
357      it('should match second series', () => {
358        expect(series.lines.show).toBe(false);
359      });
360    });
361
362    describe('override series y-axis, and z-index', () => {
363      beforeEach(() => {
364        series.alias = 'test';
365        series.applySeriesOverrides([{ alias: 'test', yaxis: 2, zindex: 2 }]);
366      });
367
368      it('should set yaxis', () => {
369        expect(series.yaxis).toBe(2);
370      });
371
372      it('should set zindex', () => {
373        expect(series.zindex).toBe(2);
374      });
375    });
376
377    describe('override color', () => {
378      beforeEach(() => {
379        series.applySeriesOverrides([{ alias: 'test', color: '#112233' }]);
380      });
381
382      it('should set color', () => {
383        expect(series.color).toBe('#112233');
384      });
385
386      it('should set bars.fillColor', () => {
387        expect(series.bars.fillColor).toBe('#112233');
388      });
389    });
390  });
391
392  describe('value formatter', () => {
393    let series: any;
394    beforeEach(() => {
395      series = new TimeSeries(testData);
396    });
397
398    it('should format non-numeric values as empty string', () => {
399      expect(series.formatValue(null)).toBe('');
400      expect(series.formatValue(undefined)).toBe('');
401      expect(series.formatValue(NaN)).toBe('');
402      expect(series.formatValue(Infinity)).toBe('');
403      expect(series.formatValue(-Infinity)).toBe('');
404    });
405  });
406
407  describe('legend decimals', () => {
408    let series: any, panel: any;
409    const height = 200;
410    beforeEach(() => {
411      testData = {
412        alias: 'test',
413        datapoints: [
414          [1, 2],
415          [0, 3],
416          [10, 4],
417          [8, 5],
418        ],
419      };
420      series = new TimeSeries(testData);
421      series.getFlotPairs();
422      panel = {
423        decimals: null,
424        yaxes: [
425          {
426            decimals: null,
427          },
428        ],
429      };
430    });
431
432    it('should set decimals to null if no decimals set', () => {
433      const data = [series];
434      // Expect ticks with this data will have decimals = 1
435      updateLegendValues(data, panel, height);
436      expect(data[0].decimals).toBe(null);
437    });
438
439    it('should set decimals to Y axis decimals + 1', () => {
440      panel.yaxes[0].decimals = 2;
441      const data = [series];
442      updateLegendValues(data, panel, height);
443      expect(data[0].decimals).toBe(3);
444    });
445
446    it('should set decimals to legend decimals value if it was set explicitly', () => {
447      panel.decimals = 3;
448      const data = [series];
449      updateLegendValues(data, panel, height);
450      expect(data[0].decimals).toBe(3);
451    });
452  });
453});
454