Fix tests after r213119 and r213120.
[WebKit-https.git] / Websites / perf.webkit.org / browser-tests / time-series-chart-tests.js
1
2 describe('TimeSeriesChart', () => {
3
4     it('should be constructible with an empty sourec list and an empty options', () => {
5         return ChartTest.importChartScripts(new BrowsingContext).then((TimeSeriesChart) => {
6             new TimeSeriesChart([], {});
7         });
8     });
9
10     describe('computeTimeGrid', () => {
11         it('should return an empty array when the start and the end times are identical', () => {
12             return ChartTest.importChartScripts(new BrowsingContext).then((TimeSeriesChart) => {
13                 const someTime = Date.now();
14                 const labels = TimeSeriesChart.computeTimeGrid(someTime, someTime, 0);
15                 expect(labels).to.be.a('array');
16                 expect(labels.length).to.be(0);
17             });
18         });
19
20         const millisecondsPerHour = 3600 * 1000;
21         const millisecondsPerDay = 24 * millisecondsPerHour;
22
23         it('should return an empty array when maxLabels is 0', () => {
24             return ChartTest.importChartScripts(new BrowsingContext).then((TimeSeriesChart) => {
25                 const endTime = Date.now();
26                 const labels = TimeSeriesChart.computeTimeGrid(endTime - millisecondsPerDay, endTime, 0);
27                 expect(labels).to.be.a('array');
28                 expect(labels.length).to.be(0);
29             });
30         });
31
32         it('should return an empty array when maxLabels is 0 even when the interval spans multiple months', () => {
33             return ChartTest.importChartScripts(new BrowsingContext).then((TimeSeriesChart) => {
34                 const endTime = Date.now();
35                 const labels = TimeSeriesChart.computeTimeGrid(endTime - 120 * millisecondsPerDay, endTime, 0);
36                 expect(labels).to.be.a('array');
37                 expect(labels.length).to.be(0);
38             });
39         });
40
41         function checkGridItem(item, label, expectedDate)
42         {
43             expect(item.label).to.be(label);
44             expect(item.time.__proto__.constructor.name).to.be('Date');
45             expect(+item.time).to.be(+new Date(expectedDate));
46         }
47
48         it('should generate one hour label with just day for two hour interval when maxLabels is 1', () => {
49             return ChartTest.importChartScripts(new BrowsingContext).then((TimeSeriesChart) => {
50                 const endTime = new Date('2017-01-15T07:53:00Z');
51                 const labels = TimeSeriesChart.computeTimeGrid(endTime - 2 * millisecondsPerHour, +endTime, 1);
52                 expect(labels).to.be.a('array');
53                 expect(labels.length).to.be(1);
54                 checkGridItem(labels[0], '6AM', '2017-01-15T06:00:00Z');
55             });
56         });
57
58         it('should generate two two-hour labels for four hour interval when maxLabels is 2', () => {
59             return ChartTest.importChartScripts(new BrowsingContext).then((TimeSeriesChart) => {
60                 const endTime = new Date('2017-01-15T07:53:00Z');
61                 const labels = TimeSeriesChart.computeTimeGrid(endTime - 4 * millisecondsPerHour, +endTime, 2);
62                 expect(labels).to.be.a('array');
63                 expect(labels.length).to.be(2);
64                 checkGridItem(labels[0], '4AM', '2017-01-15T04:00:00Z');
65                 checkGridItem(labels[1], '6AM', '2017-01-15T06:00:00Z');
66             });
67         });
68
69         it('should generate six two-hour labels for twelve hour interval when maxLabels is 6', () => {
70             return ChartTest.importChartScripts(new BrowsingContext).then((TimeSeriesChart) => {
71                 const endTime = new Date('2017-01-15T07:53:00Z');
72                 const labels = TimeSeriesChart.computeTimeGrid(+endTime, +endTime + 12 * millisecondsPerHour, 6);
73                 expect(labels).to.be.a('array');
74                 expect(labels.length).to.be(6);
75                 checkGridItem(labels[0], '8AM', '2017-01-15T08:00:00Z');
76                 checkGridItem(labels[1], '10AM', '2017-01-15T10:00:00Z');
77                 checkGridItem(labels[2], '12PM', '2017-01-15T12:00:00Z');
78                 checkGridItem(labels[3], '2PM', '2017-01-15T14:00:00Z');
79                 checkGridItem(labels[4], '4PM', '2017-01-15T16:00:00Z');
80                 checkGridItem(labels[5], '6PM', '2017-01-15T18:00:00Z');
81             });
82         });
83
84         it('should generate six two-hour labels with one date label for twelve hour interval that cross a day when maxLabels is 6', () => {
85             return ChartTest.importChartScripts(new BrowsingContext).then((TimeSeriesChart) => {
86                 const endTime = new Date('2017-01-15T16:12:00Z');
87                 const labels = TimeSeriesChart.computeTimeGrid(+endTime, +endTime + 12 * millisecondsPerHour, 6);
88                 expect(labels).to.be.a('array');
89                 expect(labels.length).to.be(6);
90                 checkGridItem(labels[0], '6PM', '2017-01-15T18:00:00Z');
91                 checkGridItem(labels[1], '8PM', '2017-01-15T20:00:00Z');
92                 checkGridItem(labels[2], '10PM', '2017-01-15T22:00:00Z');
93                 checkGridItem(labels[3], '1/16', '2017-01-16T00:00:00Z');
94                 checkGridItem(labels[4], '2AM', '2017-01-16T02:00:00Z');
95                 checkGridItem(labels[5], '4AM', '2017-01-16T04:00:00Z');
96             });
97         });
98
99         it('should generate three two-hour labels for six hour interval that cross a year when maxLabels is 5', () => {
100             return ChartTest.importChartScripts(new BrowsingContext).then((TimeSeriesChart) => {
101                 const endTime = new Date('2016-12-31T21:37:00Z');
102                 const labels = TimeSeriesChart.computeTimeGrid(+endTime, +endTime + 6 * millisecondsPerHour, 5);
103                 expect(labels).to.be.a('array');
104                 expect(labels.length).to.be(3);
105                 checkGridItem(labels[0], '10PM', '2016-12-31T22:00:00Z');
106                 checkGridItem(labels[1], '1/1', '2017-01-01T00:00:00Z');
107                 checkGridItem(labels[2], '2AM', '2017-01-01T02:00:00Z');
108             });
109         });
110
111         it('should generate one one-day label for one day interval when maxLabels is 1', () => {
112             return ChartTest.importChartScripts(new BrowsingContext).then((TimeSeriesChart) => {
113                 const endTime = new Date('2017-01-15T07:53:00Z');
114                 const labels = TimeSeriesChart.computeTimeGrid(endTime - millisecondsPerDay, +endTime, 1);
115                 expect(labels).to.be.a('array');
116                 expect(labels.length).to.be(1);
117                 checkGridItem(labels[0], '1/15', '2017-01-15T00:00:00Z');
118             });
119         });
120
121         it('should generate two one-day labels for one day interval when maxLabels is 2', () => {
122             return ChartTest.importChartScripts(new BrowsingContext).then((TimeSeriesChart) => {
123                 const endTime = new Date('2017-01-15T07:53:00Z');
124                 const labels = TimeSeriesChart.computeTimeGrid(endTime - millisecondsPerDay, +endTime, 2);
125                 expect(labels).to.be.a('array');
126                 expect(labels.length).to.be(2);
127                 checkGridItem(labels[0], '1/14 12PM', '2017-01-14T12:00:00Z');
128                 checkGridItem(labels[1], '1/15', '2017-01-15T00:00:00Z');
129             });
130         });
131
132         it('should generate four half-day labels for two day interval when maxLabels is 5', () => {
133             return ChartTest.importChartScripts(new BrowsingContext).then((TimeSeriesChart) => {
134                 const endTime = new Date('2017-01-15T16:12:00Z');
135                 const labels = TimeSeriesChart.computeTimeGrid(+endTime, +endTime + 2 * millisecondsPerDay, 5);
136                 expect(labels).to.be.a('array');
137                 expect(labels.length).to.be(4);
138                 checkGridItem(labels[0], '1/16', '2017-01-16T00:00:00Z');
139                 checkGridItem(labels[1], '12PM', '2017-01-16T12:00:00Z');
140                 checkGridItem(labels[2], '1/17', '2017-01-17T00:00:00Z');
141                 checkGridItem(labels[3], '12PM', '2017-01-17T12:00:00Z');
142             });
143         });
144
145         it('should generate four half-day labels for two day interval that cross a year when maxLabels is 5', () => {
146             return ChartTest.importChartScripts(new BrowsingContext).then((TimeSeriesChart) => {
147                 const endTime = new Date('2016-12-31T09:12:00Z');
148                 const labels = TimeSeriesChart.computeTimeGrid(+endTime, +endTime + 2 * millisecondsPerDay, 5);
149                 expect(labels).to.be.a('array');
150                 expect(labels.length).to.be(4);
151                 checkGridItem(labels[0], '12/31 12PM', '2016-12-31T12:00:00Z');
152                 checkGridItem(labels[1], '1/1', '2017-01-01T00:00:00Z');
153                 checkGridItem(labels[2], '12PM', '2017-01-01T12:00:00Z');
154                 checkGridItem(labels[3], '1/2', '2017-01-02T00:00:00Z');
155             });
156         });
157
158         it('should generate seven per-day labels for one week interval when maxLabels is 10', () => {
159             return ChartTest.importChartScripts(new BrowsingContext).then((TimeSeriesChart) => {
160                 const endTime = new Date('2017-01-15T07:53:00Z');
161                 const labels = TimeSeriesChart.computeTimeGrid(endTime - 7 * millisecondsPerDay, endTime, 10);
162                 expect(labels).to.be.a('array');
163                 expect(labels.length).to.be(7);
164                 checkGridItem(labels[0], '1/9', '2017-01-09T00:00:00Z');
165                 checkGridItem(labels[1], '1/10', '2017-01-10T00:00:00Z');
166                 checkGridItem(labels[2], '1/11', '2017-01-11T00:00:00Z');
167                 checkGridItem(labels[3], '1/12', '2017-01-12T00:00:00Z');
168                 checkGridItem(labels[4], '1/13', '2017-01-13T00:00:00Z');
169                 checkGridItem(labels[5], '1/14', '2017-01-14T00:00:00Z');
170                 checkGridItem(labels[6], '1/15', '2017-01-15T00:00:00Z');
171             });
172         });
173
174         it('should generate three two-day labels for one week interval when maxLabels is 4', () => {
175             return ChartTest.importChartScripts(new BrowsingContext).then((TimeSeriesChart) => {
176                 const endTime = new Date('2017-01-15T07:53:00Z');
177                 const labels = TimeSeriesChart.computeTimeGrid(endTime - 7 * millisecondsPerDay, endTime, 4);
178                 expect(labels).to.be.a('array');
179                 expect(labels.length).to.be(3);
180                 checkGridItem(labels[0], '1/10', '2017-01-10T00:00:00Z');
181                 checkGridItem(labels[1], '1/12', '2017-01-12T00:00:00Z');
182                 checkGridItem(labels[2], '1/14', '2017-01-14T00:00:00Z');
183             });
184         });
185
186         it('should generate seven one-day labels for two week interval when maxLabels is 8', () => {
187             return ChartTest.importChartScripts(new BrowsingContext).then((TimeSeriesChart) => {
188                 const endTime = new Date('2017-01-15T18:53:00Z');
189                 const labels = TimeSeriesChart.computeTimeGrid(+endTime, +endTime + 14 * millisecondsPerDay, 8);
190                 expect(labels).to.be.a('array');
191                 expect(labels.length).to.be(7);
192                 checkGridItem(labels[0], '1/17', '2017-01-17T00:00:00Z');
193                 checkGridItem(labels[1], '1/19', '2017-01-19T00:00:00Z');
194                 checkGridItem(labels[2], '1/21', '2017-01-21T00:00:00Z');
195                 checkGridItem(labels[3], '1/23', '2017-01-23T00:00:00Z');
196                 checkGridItem(labels[4], '1/25', '2017-01-25T00:00:00Z');
197                 checkGridItem(labels[5], '1/27', '2017-01-27T00:00:00Z');
198                 checkGridItem(labels[6], '1/29', '2017-01-29T00:00:00Z');
199             });
200         });
201
202         it('should generate two one-week labels for two week interval when maxLabels is 3', () => {
203             return ChartTest.importChartScripts(new BrowsingContext).then((TimeSeriesChart) => {
204                 const endTime = new Date('2017-01-15T18:53:00Z');
205                 const labels = TimeSeriesChart.computeTimeGrid(+endTime, +endTime + 14 * millisecondsPerDay, 3);
206                 expect(labels).to.be.a('array');
207                 expect(labels.length).to.be(2);
208                 checkGridItem(labels[0], '1/22', '2017-01-22T00:00:00Z');
209                 checkGridItem(labels[1], '1/29', '2017-01-29T00:00:00Z');
210             });
211         });
212
213         it('should generate seven one-month labels for six and half months interval starting before 15th when maxLabels is 7', () => {
214             return ChartTest.importChartScripts(new BrowsingContext).then((TimeSeriesChart) => {
215                 const endTime = new Date('2017-01-15T18:53:00Z');
216                 const labels = TimeSeriesChart.computeTimeGrid(new Date('2016-07-12T18:53:00Z'), new Date('2017-01-18T08:17:53Z'), 7);
217                 expect(labels).to.be.a('array');
218                 expect(labels.length).to.be(7);
219                 checkGridItem(labels[0], '7/15', '2016-07-15T00:00:00Z');
220                 checkGridItem(labels[1], '8/15', '2016-08-15T00:00:00Z');
221                 checkGridItem(labels[2], '9/15', '2016-09-15T00:00:00Z');
222                 checkGridItem(labels[3], '10/15', '2016-10-15T00:00:00Z');
223                 checkGridItem(labels[4], '11/15', '2016-11-15T00:00:00Z');
224                 checkGridItem(labels[5], '12/15', '2016-12-15T00:00:00Z');
225                 checkGridItem(labels[6], '1/15', '2017-01-15T00:00:00Z');
226             });
227         });
228
229         it('should generate seven one-month labels for six months interval staring after 15th when maxLabels is 7', () => {
230             return ChartTest.importChartScripts(new BrowsingContext).then((TimeSeriesChart) => {
231                 const endTime = new Date('2017-01-15T18:53:00Z');
232                 const labels = TimeSeriesChart.computeTimeGrid(new Date('2016-07-18T18:53:00Z'), new Date('2017-01-18T08:17:53Z'), 7);
233                 expect(labels).to.be.a('array');
234                 expect(labels.length).to.be(6);
235                 checkGridItem(labels[0], '8/1', '2016-08-01T00:00:00Z');
236                 checkGridItem(labels[1], '9/1', '2016-09-01T00:00:00Z');
237                 checkGridItem(labels[2], '10/1', '2016-10-01T00:00:00Z');
238                 checkGridItem(labels[3], '11/1', '2016-11-01T00:00:00Z');
239                 checkGridItem(labels[4], '12/1', '2016-12-01T00:00:00Z');
240             });
241         });
242
243         it('should generate six two-months labels for one year interval when maxLabels is 7', () => {
244             return ChartTest.importChartScripts(new BrowsingContext).then((TimeSeriesChart) => {
245                 const endTime = new Date('2017-01-15T18:53:00Z');
246                 const labels = TimeSeriesChart.computeTimeGrid(new Date('2016-07-11T18:53:00Z'), new Date('2017-07-27T08:17:53Z'), 7);
247                 expect(labels).to.be.a('array');
248                 expect(labels.length).to.be(6);
249                 checkGridItem(labels[0], '9/1', '2016-09-01T00:00:00Z');
250                 checkGridItem(labels[1], '11/1', '2016-11-01T00:00:00Z');
251                 checkGridItem(labels[2], '1/1', '2017-01-01T00:00:00Z');
252                 checkGridItem(labels[3], '3/1', '2017-03-01T00:00:00Z');
253                 checkGridItem(labels[4], '5/1', '2017-05-01T00:00:00Z');
254                 checkGridItem(labels[5], '7/1', '2017-07-01T00:00:00Z');
255             });
256         });
257
258         it('should generate four three-months labels for one year interval when maxLabels is 5', () => {
259             return ChartTest.importChartScripts(new BrowsingContext).then((TimeSeriesChart) => {
260                 const endTime = new Date('2017-01-15T18:53:00Z');
261                 const labels = TimeSeriesChart.computeTimeGrid(new Date('2016-07-11T18:53:00Z'), new Date('2017-07-27T08:17:53Z'), 5);
262                 expect(labels).to.be.a('array');
263                 expect(labels.length).to.be(4);
264                 checkGridItem(labels[0], '10/1', '2016-10-01T00:00:00Z');
265                 checkGridItem(labels[1], '1/1', '2017-01-01T00:00:00Z');
266                 checkGridItem(labels[2], '4/1', '2017-04-01T00:00:00Z');
267                 checkGridItem(labels[3], '7/1', '2017-07-01T00:00:00Z');
268             });
269         });
270     });
271
272     describe('computeValueGrid', () => {
273
274         function checkValueGrid(actual, expected) {
275             expect(actual).to.be.a('array');
276             expect(JSON.stringify(actual)).to.be(JSON.stringify(expected));
277         }
278
279         function approximate(number)
280         {
281             return Math.round(number * 100000000) / 100000000;
282         }
283
284         it('should generate [0.5, 1.0, 1.5, 2.0] for [0.3, 2.3] when maxLabels is 5', () => {
285             const context = new BrowsingContext;
286             return ChartTest.importChartScripts(context).then((TimeSeriesChart) => {
287                 const Metric = context.symbols.Metric;
288                 const grid = TimeSeriesChart.computeValueGrid(0.3, 2.3, 5, Metric.makeFormatter('pt', 2));
289                 expect(grid.map((item) => approximate(item.value))).to.eql([0.5, 1.0, 1.5, 2.0]);
290                 expect(grid.map((item) => item.label)).to.eql(['0.5 pt', '1.0 pt', '1.5 pt', '2.0 pt']);
291             });
292         });
293
294         it('should generate [0.4, 0.6, 0.8, 1, 1.2, 1.4, 1.6, 1.8, 2, 2.2] for [0.3, 2.3] when maxLabels is 10', () => {
295             const context = new BrowsingContext;
296             return ChartTest.importChartScripts(context).then((TimeSeriesChart) => {
297                 const Metric = context.symbols.Metric;
298                 const grid = TimeSeriesChart.computeValueGrid(0.3, 2.3, 10, Metric.makeFormatter('pt', 2));
299                 expect(grid.map((item) => approximate(item.value))).to.eql([0.4, 0.6, 0.8, 1, 1.2, 1.4, 1.6, 1.8, 2, 2.2]);
300                 expect(grid.map((item) => item.label)).to.eql(['0.4 pt', '0.6 pt', '0.8 pt', '1.0 pt', '1.2 pt', '1.4 pt', '1.6 pt', '1.8 pt', '2.0 pt', '2.2 pt']);
301             });
302         });
303
304         it('should generate [1, 2] for [0.3, 2.3] when maxLabels is 2', () => {
305             const context = new BrowsingContext;
306             return ChartTest.importChartScripts(context).then((TimeSeriesChart) => {
307                 const Metric = context.symbols.Metric;
308                 const grid = TimeSeriesChart.computeValueGrid(0.3, 2.3, 2, Metric.makeFormatter('pt', 2));
309                 expect(grid.map((item) => item.value)).to.eql([1, 2]);
310                 expect(grid.map((item) => item.label)).to.eql(['1.0 pt', '2.0 pt']);
311             });
312         });
313
314         it('should generate [0.4, 0.6, 0.8, 1.0, 1.2] for [0.3, 1.3] when maxLabels is 5', () => {
315             const context = new BrowsingContext;
316             return ChartTest.importChartScripts(context).then((TimeSeriesChart) => {
317                 const Metric = context.symbols.Metric;
318                 const grid = TimeSeriesChart.computeValueGrid(0.3, 1.3, 5, Metric.makeFormatter('pt', 2));
319                 expect(grid.map((item) => approximate(item.value))).to.eql([0.4, 0.6, 0.8, 1.0, 1.2]);
320                 expect(grid.map((item) => item.label)).to.eql(['0.4 pt', '0.6 pt', '0.8 pt', '1.0 pt', '1.2 pt']);
321             });
322         });
323
324         it('should generate [0.2, 0.4, 0.6, 0.8, 1, 1.2] for [0.2, 1.3] when maxLabels is 10', () => {
325             const context = new BrowsingContext;
326             return ChartTest.importChartScripts(context).then((TimeSeriesChart) => {
327                 const Metric = context.symbols.Metric;
328                 const grid = TimeSeriesChart.computeValueGrid(0.2, 1.3, 10, Metric.makeFormatter('pt', 2));
329                 expect(grid.map((item) => approximate(item.value))).to.eql([0.2, 0.4, 0.6, 0.8, 1, 1.2]);
330                 expect(grid.map((item) => item.label)).to.eql(['0.2 pt', '0.4 pt', '0.6 pt', '0.8 pt', '1.0 pt', '1.2 pt']);
331             });
332         });
333
334         it('should generate [0.5, 1.0] for [0.3, 1.3] when maxLabels is 4', () => {
335             const context = new BrowsingContext;
336             return ChartTest.importChartScripts(context).then((TimeSeriesChart) => {
337                 const Metric = context.symbols.Metric;
338                 const grid = TimeSeriesChart.computeValueGrid(0.3, 1.3, 4, Metric.makeFormatter('pt', 2));
339                 expect(grid.map((item) => approximate(item.value))).to.eql([0.5, 1.0]);
340                 expect(grid.map((item) => item.label)).to.eql(['0.5 pt', '1.0 pt']);
341             });
342         });
343
344         it('should generate [10, 20, 30] for [4, 35] when maxLabels is 4', () => {
345             const context = new BrowsingContext;
346             return ChartTest.importChartScripts(context).then((TimeSeriesChart) => {
347                 const Metric = context.symbols.Metric;
348                 const grid = TimeSeriesChart.computeValueGrid(4, 35, 4, Metric.makeFormatter('pt', 2));
349                 expect(grid.map((item) => item.value)).to.eql([10, 20, 30]);
350                 expect(grid.map((item) => item.label)).to.eql(['10 pt', '20 pt', '30 pt']);
351             });
352         });
353
354         it('should generate [10, 20, 30] for [4, 35] when maxLabels is 6', () => {
355             const context = new BrowsingContext;
356             return ChartTest.importChartScripts(context).then((TimeSeriesChart) => {
357                 const Metric = context.symbols.Metric;
358                 const grid = TimeSeriesChart.computeValueGrid(4, 35, 6, Metric.makeFormatter('pt', 2));
359                 expect(grid.map((item) => item.value)).to.eql([10, 20, 30]);
360                 expect(grid.map((item) => item.label)).to.eql(['10 pt', '20 pt', '30 pt']);
361             });
362         });
363
364         it('should generate [10, 15, 20, 25, 30, 35] for [6, 35] when maxLabels is 6', () => {
365             const context = new BrowsingContext;
366             return ChartTest.importChartScripts(context).then((TimeSeriesChart) => {
367                 const Metric = context.symbols.Metric;
368                 const grid = TimeSeriesChart.computeValueGrid(6, 35, 6, Metric.makeFormatter('pt', 2));
369                 expect(grid.map((item) => item.value)).to.eql([10, 15, 20, 25, 30, 35]);
370                 expect(grid.map((item) => item.label)).to.eql(['10 pt', '15 pt', '20 pt', '25 pt', '30 pt', '35 pt']);
371             });
372         });
373
374         it('should generate [110, 115, 120, 125, 130] for [107, 134] when maxLabels is 6', () => {
375             const context = new BrowsingContext;
376             return ChartTest.importChartScripts(context).then((TimeSeriesChart) => {
377                 const Metric = context.symbols.Metric;
378                 const grid = TimeSeriesChart.computeValueGrid(107, 134, 6, Metric.makeFormatter('pt', 3));
379                 expect(grid.map((item) => item.value)).to.eql([110, 115, 120, 125, 130]);
380                 expect(grid.map((item) => item.label)).to.eql(['110 pt', '115 pt', '120 pt', '125 pt', '130 pt']);
381             });
382         });
383
384         it('should generate [5e7, 10e7] for [1e7, 1e8] when maxLabels is 4', () => {
385             const context = new BrowsingContext;
386             return ChartTest.importChartScripts(context).then((TimeSeriesChart) => {
387                 const Metric = context.symbols.Metric;
388                 const grid = TimeSeriesChart.computeValueGrid(1e7, 1e8, 4, Metric.makeFormatter('pt', 3));
389                 expect(grid.map((item) => item.value)).to.eql([5e7, 10e7]);
390                 expect(grid.map((item) => item.label)).to.eql(['50.0 Mpt', '100 Mpt']);
391             });
392         });
393
394         it('should generate [2e7, 4e7, 6e7, 8e7, 10e7] for [1e7, 1e8] when maxLabels is 5', () => {
395             const context = new BrowsingContext;
396             return ChartTest.importChartScripts(context).then((TimeSeriesChart) => {
397                 const Metric = context.symbols.Metric;
398                 const grid = TimeSeriesChart.computeValueGrid(1e7, 1e8, 5, Metric.makeFormatter('pt', 3));
399                 expect(grid.map((item) => item.value)).to.eql([2e7, 4e7, 6e7, 8e7, 10e7]);
400                 expect(grid.map((item) => item.label)).to.eql(['20.0 Mpt', '40.0 Mpt', '60.0 Mpt', '80.0 Mpt', '100 Mpt']);
401             });
402         });
403
404         it('should generate [-1.5, -1.0, -0.5, 0.0, 0.5] for [-1.8, 0.7] when maxLabels is 5', () => {
405             const context = new BrowsingContext;
406             return ChartTest.importChartScripts(context).then((TimeSeriesChart) => {
407                 const Metric = context.symbols.Metric;
408                 const grid = TimeSeriesChart.computeValueGrid(-1.8, 0.7, 5, Metric.makeFormatter('pt', 2));
409                 expect(grid.map((item) => approximate(item.value))).to.eql([-1.5, -1.0, -0.5, 0.0, 0.5]);
410                 expect(grid.map((item) => item.label)).to.eql(['-1.5 pt', '-1.0 pt', '-0.5 pt', '0.0 pt', '0.5 pt']);
411             });
412         });
413
414         it('should generate [200ms, 400ms, 600ms, 800ms, 1.00s, 1.20s] for [0.2, 1.3] when maxLabels is 10 and unit is seconds', () => {
415             const context = new BrowsingContext;
416             return ChartTest.importChartScripts(context).then((TimeSeriesChart) => {
417                 const Metric = context.symbols.Metric;
418                 const grid = TimeSeriesChart.computeValueGrid(0.2, 1.3, 10, Metric.makeFormatter('s', 3));
419                 expect(grid.map((item) => approximate(item.value))).to.eql([0.2, 0.4, 0.6, 0.8, 1, 1.2]);
420                 expect(grid.map((item) => item.label)).to.eql(['200 ms', '400 ms', '600 ms', '800 ms', '1.00 s', '1.20 s']);
421             });
422         });
423
424         it('should generate [2.0GB, 4.0GB, 6.0GB] for [1.2GB, 7.2GB] when maxLabels is 4 and unit is bytes', () => {
425             const context = new BrowsingContext;
426             return ChartTest.importChartScripts(context).then((TimeSeriesChart) => {
427                 const Metric = context.symbols.Metric;
428                 const gigabytes = Math.pow(1024, 3);
429                 const grid = TimeSeriesChart.computeValueGrid(1.2 * gigabytes, 7.2 * gigabytes, 4, Metric.makeFormatter('B', 2));
430                 expect(grid.map((item) => approximate(item.value))).to.eql([2 * gigabytes, 4 * gigabytes, 6 * gigabytes]);
431                 expect(grid.map((item) => item.label)).to.eql(['2.0 GB', '4.0 GB', '6.0 GB']);
432             });
433         });
434
435         it('should generate [0.6GB, 0.8GB, 1.0GB, 1.2GB] for [0.53GB, 1.23GB] when maxLabels is 4 and unit is bytes', () => {
436             const context = new BrowsingContext;
437             return ChartTest.importChartScripts(context).then((TimeSeriesChart) => {
438                 const Metric = context.symbols.Metric;
439                 const gigabytes = Math.pow(1024, 3);
440                 const grid = TimeSeriesChart.computeValueGrid(0.53 * gigabytes, 1.23 * gigabytes, 4, Metric.makeFormatter('B', 2));
441                 expect(grid.map((item) => item.label)).to.eql(['0.6 GB', '0.8 GB', '1.0 GB', '1.2 GB']);
442             });
443         });
444
445     });
446
447     describe('fetchMeasurementSets', () => {
448
449         it('should fetch the measurement set and create a canvas element upon receiving the data', () => {
450             const context = new BrowsingContext();
451             return ChartTest.importChartScripts(context).then(() => {
452                 const chart = ChartTest.createChartWithSampleCluster(context);
453
454                 chart.setDomain(ChartTest.sampleCluster.startTime, ChartTest.sampleCluster.endTime);
455                 chart.fetchMeasurementSets();
456
457                 const requests = context.symbols.MockRemoteAPI.requests;
458                 expect(requests.length).to.be(1);
459                 ChartTest.respondWithSampleCluster(requests[0]);
460
461                 expect(chart.content().querySelector('canvas')).to.be(null);
462                 return waitForComponentsToRender(context).then(() => {
463                     expect(chart.content().querySelector('canvas')).to.not.be(null);
464                 });
465             });
466         });
467
468         it('should immediately enqueue to render when the measurement set had already been fetched', () => {
469             const context = new BrowsingContext();
470             return ChartTest.importChartScripts(context).then(() => {
471                 const chart = ChartTest.createChartWithSampleCluster(context);
472
473                 let set = context.symbols.MeasurementSet.findSet(1, 1, 0);
474                 let promise = set.fetchBetween(ChartTest.sampleCluster.startTime, ChartTest.sampleCluster.endTime);
475
476                 const requests = context.symbols.MockRemoteAPI.requests;
477                 expect(requests.length).to.be(1);
478                 ChartTest.respondWithSampleCluster(requests[0]);
479
480                 return promise.then(() => {
481                     expect(chart.content().querySelector('canvas')).to.be(null);
482                     chart.setDomain(ChartTest.sampleCluster.startTime, ChartTest.sampleCluster.endTime);
483                     chart.fetchMeasurementSets();
484                     return waitForComponentsToRender(context);
485                 }).then(() => {
486                     expect(requests.length).to.be(1);
487                     expect(chart.content().querySelector('canvas')).to.not.be(null);
488                 });
489             });
490         });
491
492         it('should dispatch "dataChange" action once the fetched data becomes available', () => {
493             const context = new BrowsingContext();
494             return ChartTest.importChartScripts(context).then(() => {
495                 const chart = ChartTest.createChartWithSampleCluster(context);
496
497                 let dataChangeCount = 0;
498                 chart.listenToAction('dataChange', () => dataChangeCount++);
499
500                 chart.setDomain(ChartTest.sampleCluster.startTime, ChartTest.sampleCluster.endTime);
501                 chart.fetchMeasurementSets();
502
503                 const requests = context.symbols.MockRemoteAPI.requests;
504                 expect(requests.length).to.be(1);
505                 ChartTest.respondWithSampleCluster(requests[0]);
506
507                 expect(dataChangeCount).to.be(0);
508                 expect(chart.sampledTimeSeriesData('current')).to.be(null);
509                 expect(chart.content().querySelector('canvas')).to.be(null);
510                 return waitForComponentsToRender(context).then(() => {
511                     expect(dataChangeCount).to.be(1);
512                     expect(chart.sampledTimeSeriesData('current')).to.not.be(null);
513                     expect(chart.content().querySelector('canvas')).to.not.be(null);
514                 });
515             });
516         });
517     });
518
519     describe('sampledTimeSeriesData', () => {
520         it('should not contain an outlier when includeOutliers is false', () => {
521             const context = new BrowsingContext();
522             return ChartTest.importChartScripts(context).then(() => {
523                 const chart = ChartTest.createChartWithSampleCluster(context, [{includeOutliers: false}])
524
525                 chart.setDomain(ChartTest.sampleCluster.startTime, ChartTest.sampleCluster.endTime);
526                 chart.fetchMeasurementSets();
527                 ChartTest.respondWithSampleCluster(context.symbols.MockRemoteAPI.requests[0]);
528
529                 return waitForComponentsToRender(context).then(() => {
530                     const view = chart.sampledTimeSeriesData('current');
531                     expect(view.length()).to.be(5);
532                     for (let point of view)
533                         expect(point.markedOutlier).to.be(false);
534                 });
535             });
536         });
537
538         it('should contain every outlier when includeOutliers is true', () => {
539             const context = new BrowsingContext();
540             return ChartTest.importChartScripts(context).then(() => {
541                 const chart = ChartTest.createChartWithSampleCluster(context, [{includeOutliers: true}])
542
543                 chart.setDomain(ChartTest.sampleCluster.startTime, ChartTest.sampleCluster.endTime);
544                 chart.fetchMeasurementSets();
545                 ChartTest.respondWithSampleCluster(context.symbols.MockRemoteAPI.requests[0]);
546
547                 return waitForComponentsToRender(context).then(() => {
548                     const view = chart.sampledTimeSeriesData('current');
549                     expect(view.length()).to.be(7);
550                     expect(view.findPointByIndex(1).markedOutlier).to.be(true);
551                     expect(view.findPointByIndex(5).markedOutlier).to.be(true);
552                 });
553             });
554         });
555
556         it('should only contain data points in the domain and one preceding point when there are no succeeding points', () => {
557             const context = new BrowsingContext();
558             return ChartTest.importChartScripts(context).then(() => {
559                 const chart = ChartTest.createChartWithSampleCluster(context, [{includeOutliers: true}])
560
561                 chart.setDomain(posixTime('2016-01-06T00:00:00Z'), posixTime('2016-01-07T00:00:00Z'));
562                 chart.fetchMeasurementSets();
563                 ChartTest.respondWithSampleCluster(context.symbols.MockRemoteAPI.requests[0]);
564
565                 return waitForComponentsToRender(context).then(() => {
566                     const view = chart.sampledTimeSeriesData('current');
567                     expect([...view].map((point) => point.id)).to.be.eql([1003, 1004, 1005, 1006]);
568                 });
569             });
570         });
571
572         it('should only contain data points in the domain and one succeeding point when there are no preceding points', () => {
573             const context = new BrowsingContext();
574             return ChartTest.importChartScripts(context).then(() => {
575                 const chart = ChartTest.createChartWithSampleCluster(context, [{includeOutliers: true}])
576
577                 chart.setDomain(posixTime('2016-01-05T00:00:00Z'), posixTime('2016-01-06T00:00:00Z'));
578                 chart.fetchMeasurementSets();
579                 chart.fetchMeasurementSets();
580                 ChartTest.respondWithSampleCluster(context.symbols.MockRemoteAPI.requests[0]);
581
582                 return waitForComponentsToRender(context).then(() => {
583                     const view = chart.sampledTimeSeriesData('current');
584                     expect([...view].map((point) => point.id)).to.be.eql([1000, 1001, 1002, 1003, 1004]);
585                 });
586             });
587         });
588
589         it('should only contain data points in the domain and one preceding point and one succeeding point', () => {
590             const context = new BrowsingContext();
591             return ChartTest.importChartScripts(context).then(() => {
592                 const chart = ChartTest.createChartWithSampleCluster(context, [{includeOutliers: true}])
593
594                 chart.setDomain(posixTime('2016-01-05T21:00:00Z'), posixTime('2016-01-06T02:00:00Z'));
595                 chart.fetchMeasurementSets();
596                 ChartTest.respondWithSampleCluster(context.symbols.MockRemoteAPI.requests[0]);
597
598                 return waitForComponentsToRender(context).then(() => {
599                     const view = chart.sampledTimeSeriesData('current');
600                     expect([...view].map((point) => point.id)).to.be.eql([1002, 1003, 1004, 1005]);
601                 });
602             });
603         });
604     });
605
606     describe('render', () => {
607         it('should update the canvas size and its content after the window has been resized', () => {
608             const context = new BrowsingContext();
609             return ChartTest.importChartScripts(context).then(() => {
610                 const chart = ChartTest.createChartWithSampleCluster(context, null, {width: '100%', height: '100%'});
611
612                 let dataChangeCount = 0;
613                 chart.listenToAction('dataChange', () => dataChangeCount++);
614
615                 chart.setDomain(ChartTest.sampleCluster.startTime, ChartTest.sampleCluster.endTime);
616                 chart.fetchMeasurementSets();
617
618                 const requests = context.symbols.MockRemoteAPI.requests;
619                 expect(requests.length).to.be(1);
620                 ChartTest.respondWithSampleCluster(requests[0]);
621
622                 expect(dataChangeCount).to.be(0);
623                 expect(chart.sampledTimeSeriesData('current')).to.be(null);
624                 expect(chart.content().querySelector('canvas')).to.be(null);
625                 let canvas;
626                 let originalWidth;
627                 let originalHeight;
628                 return waitForComponentsToRender(context).then(() => {
629                     expect(dataChangeCount).to.be(1);
630                     expect(chart.sampledTimeSeriesData('current')).to.not.be(null);
631                     canvas = chart.content().querySelector('canvas');
632                     expect(canvas).to.not.be(null);
633
634                     originalWidth = canvas.offsetWidth;
635                     originalHeight = canvas.offsetHeight;
636                     expect(originalWidth).to.be(context.document.body.offsetWidth);
637                     expect(originalHeight).to.be(context.document.body.offsetHeight);
638
639                     CanvasTest.fillCanvasBeforeRedrawCheck(canvas);
640                     context.iframe.style.width = context.iframe.offsetWidth * 2 + 'px';
641                     context.global.dispatchEvent(new Event('resize'));
642
643                     expect(canvas.offsetWidth).to.be(originalWidth);
644                     expect(canvas.offsetHeight).to.be(originalHeight);
645
646                     return waitForComponentsToRender(context);
647                 }).then(() => {
648                     expect(dataChangeCount).to.be(2);
649                     expect(canvas.offsetWidth).to.be.greaterThan(originalWidth);
650                     expect(canvas.offsetWidth).to.be(context.document.body.offsetWidth);
651                     expect(canvas.offsetHeight).to.be(originalHeight);
652                     expect(CanvasTest.hasCanvasBeenRedrawn(canvas)).to.be(true);
653                 });
654             });
655         });
656
657         it('should not update update the canvas when the window has been resized but its dimensions stays the same', () => {
658             const context = new BrowsingContext();
659             return ChartTest.importChartScripts(context).then(() => {
660                 const chart = ChartTest.createChartWithSampleCluster(context, null, {width: '100px', height: '100px'});
661
662                 let dataChangeCount = 0;
663                 chart.listenToAction('dataChange', () => dataChangeCount++);
664
665                 chart.setDomain(ChartTest.sampleCluster.startTime, ChartTest.sampleCluster.endTime);
666                 chart.fetchMeasurementSets();
667
668                 const requests = context.symbols.MockRemoteAPI.requests;
669                 expect(requests.length).to.be(1);
670                 ChartTest.respondWithSampleCluster(requests[0]);
671                 expect(dataChangeCount).to.be(0);
672
673                 let canvas;
674                 let data;
675                 return waitForComponentsToRender(context).then(() => {
676                     expect(dataChangeCount).to.be(1);
677                     data = chart.sampledTimeSeriesData('current');
678                     expect(data).to.not.be(null);
679                     canvas = chart.content().querySelector('canvas');
680                     expect(canvas).to.not.be(null);
681
682                     expect(canvas.offsetWidth).to.be(100);
683                     expect(canvas.offsetHeight).to.be(100);
684
685                     CanvasTest.fillCanvasBeforeRedrawCheck(canvas);
686                     context.iframe.style.width = context.iframe.offsetWidth * 2 + 'px';
687                     context.global.dispatchEvent(new Event('resize'));
688
689                     expect(canvas.offsetWidth).to.be(100);
690                     expect(canvas.offsetHeight).to.be(100);
691
692                     return waitForComponentsToRender(context);
693                 }).then(() => {
694                     expect(dataChangeCount).to.be(1);
695                     expect(chart.sampledTimeSeriesData('current')).to.be(data);
696                     expect(canvas.offsetWidth).to.be(100);
697                     expect(canvas.offsetHeight).to.be(100);
698                     expect(CanvasTest.hasCanvasBeenRedrawn(canvas)).to.be(false);
699                 });
700             });
701         });
702
703         it('should render Y-axis', () => {
704             const context = new BrowsingContext();
705             return ChartTest.importChartScripts(context).then(() => {
706                 const chartWithoutYAxis = ChartTest.createChartWithSampleCluster(context, null, {axis:
707                     {
708                         gridStyle: '#ccc',
709                         fontSize: 1,
710                         valueFormatter: context.symbols.Metric.makeFormatter('ms', 3),
711                     }
712                 });
713                 const chartWithYAxis1 = ChartTest.createChartWithSampleCluster(context, null, {axis:
714                     {
715                         yAxisWidth: 4,
716                         gridStyle: '#ccc',
717                         fontSize: 1,
718                         valueFormatter: context.symbols.Metric.makeFormatter('ms', 3),
719                     }
720                 });
721                 const chartWithYAxis2 = ChartTest.createChartWithSampleCluster(context, null, {axis:
722                     {
723                         yAxisWidth: 4,
724                         gridStyle: '#ccc',
725                         fontSize: 1,
726                         valueFormatter: context.symbols.Metric.makeFormatter('B', 3),
727                     }
728                 });
729
730                 chartWithoutYAxis.setDomain(ChartTest.sampleCluster.startTime, ChartTest.sampleCluster.endTime);
731                 chartWithoutYAxis.fetchMeasurementSets();
732                 chartWithYAxis1.setDomain(ChartTest.sampleCluster.startTime, ChartTest.sampleCluster.endTime);
733                 chartWithYAxis1.fetchMeasurementSets();
734                 chartWithYAxis2.setDomain(ChartTest.sampleCluster.startTime, ChartTest.sampleCluster.endTime);
735                 chartWithYAxis2.fetchMeasurementSets();
736
737                 const requests = context.symbols.MockRemoteAPI.requests;
738                 expect(requests.length).to.be(1);
739                 ChartTest.respondWithSampleCluster(requests[0]);
740
741                 return waitForComponentsToRender(context).then(() => {
742                     let canvasWithoutYAxis = chartWithoutYAxis.content().querySelector('canvas');
743                     let canvasWithYAxis1 = chartWithYAxis1.content().querySelector('canvas');
744                     let canvasWithYAxis2 = chartWithYAxis2.content().querySelector('canvas');
745                     CanvasTest.expectCanvasesMismatch(canvasWithoutYAxis, canvasWithYAxis1);
746                     CanvasTest.expectCanvasesMismatch(canvasWithoutYAxis, canvasWithYAxis1);
747                     CanvasTest.expectCanvasesMismatch(canvasWithYAxis1, canvasWithYAxis2);
748
749                     expect(CanvasTest.canvasContainsColor(canvasWithYAxis1, {r: 204, g: 204, b: 204},
750                         {x: canvasWithYAxis1.width - 1, width: 1, y: 0, height: canvasWithYAxis1.height})).to.be(true);
751                 });
752             });
753         });
754
755         it('should render the sampled time series', () => {
756             const context = new BrowsingContext();
757             return ChartTest.importChartScripts(context).then(() => {
758                 const lineStyle = 'rgb(0, 128, 255)';
759                 const lineColor = {r: 0, g: 128, b: 255};
760                 const chartOptions = {width: '100px', height: '100px'};
761                 const chartWithoutSampling = ChartTest.createChartWithSampleCluster(context, [{lineStyle, sampleData: false}], chartOptions);
762                 const chartWithSampling = ChartTest.createChartWithSampleCluster(context, [{lineStyle, sampleData: true}], chartOptions);
763
764                 chartWithoutSampling.setDomain(ChartTest.sampleCluster.startTime, ChartTest.sampleCluster.endTime);
765                 chartWithoutSampling.fetchMeasurementSets();
766                 ChartTest.respondWithSampleCluster(context.symbols.MockRemoteAPI.requests[0]);
767
768                 chartWithSampling.setDomain(ChartTest.sampleCluster.startTime, ChartTest.sampleCluster.endTime);
769                 chartWithSampling.fetchMeasurementSets();
770
771                 let canvasWithSampling;
772                 let canvasWithoutSampling;
773                 return waitForComponentsToRender(context).then(() => {
774                     canvasWithoutSampling = chartWithoutSampling.content().querySelector('canvas');
775                     canvasWithSampling = chartWithSampling.content().querySelector('canvas');
776
777                     CanvasTest.expectCanvasesMatch(canvasWithSampling, canvasWithoutSampling);
778                     expect(CanvasTest.canvasContainsColor(canvasWithoutSampling, lineColor)).to.be(true);
779                     expect(CanvasTest.canvasContainsColor(canvasWithSampling, lineColor)).to.be(true);
780
781                     const diff = ChartTest.sampleCluster.endTime - ChartTest.sampleCluster.startTime;
782                     chartWithoutSampling.setDomain(ChartTest.sampleCluster.startTime - 2 * diff, ChartTest.sampleCluster.endTime);
783                     chartWithSampling.setDomain(ChartTest.sampleCluster.startTime - 2 * diff, ChartTest.sampleCluster.endTime);
784
785                     CanvasTest.fillCanvasBeforeRedrawCheck(canvasWithoutSampling);
786                     CanvasTest.fillCanvasBeforeRedrawCheck(canvasWithSampling);
787                     return waitForComponentsToRender(context);
788                 }).then(() => {
789                     expect(CanvasTest.hasCanvasBeenRedrawn(canvasWithoutSampling)).to.be(true);
790                     expect(CanvasTest.hasCanvasBeenRedrawn(canvasWithSampling)).to.be(true);
791
792                     expect(CanvasTest.canvasContainsColor(canvasWithoutSampling, lineColor)).to.be(true);
793                     expect(CanvasTest.canvasContainsColor(canvasWithSampling, lineColor)).to.be(true);
794
795                     CanvasTest.expectCanvasesMismatch(canvasWithSampling, canvasWithoutSampling);
796                 });
797             });
798         });
799
800         it('should render annotations', () => {
801             const context = new BrowsingContext();
802             return ChartTest.importChartScripts(context).then(() => {
803                 const options = {annotations: { textStyle: '#000', textBackground: '#fff', minWidth: 3, barHeight: 7, barSpacing: 2}};
804                 const chartWithoutAnnotations = ChartTest.createChartWithSampleCluster(context, null, options);
805                 const chartWithAnnotations = ChartTest.createChartWithSampleCluster(context, null, options);
806
807                 chartWithoutAnnotations.setDomain(ChartTest.sampleCluster.startTime, ChartTest.sampleCluster.endTime);
808                 chartWithoutAnnotations.fetchMeasurementSets();
809                 ChartTest.respondWithSampleCluster(context.symbols.MockRemoteAPI.requests[0]);
810
811                 chartWithAnnotations.setDomain(ChartTest.sampleCluster.startTime, ChartTest.sampleCluster.endTime);
812                 chartWithAnnotations.fetchMeasurementSets();
813
814                 let canvasWithAnnotations;
815                 return waitForComponentsToRender(context).then(() => {
816                     const diff = ChartTest.sampleCluster.endTime - ChartTest.sampleCluster.startTime;
817                     chartWithAnnotations.setAnnotations([{
818                         startTime: ChartTest.sampleCluster.startTime + diff / 4,
819                         endTime: ChartTest.sampleCluster.startTime + diff / 2,
820                         label: 'hello, world',
821                         fillStyle: 'rgb(0, 0, 255)',
822                     }]);
823
824                     canvasWithAnnotations = chartWithAnnotations.content().querySelector('canvas');
825                     CanvasTest.fillCanvasBeforeRedrawCheck(canvasWithAnnotations);
826                     return waitForComponentsToRender(context);
827                 }).then(() => {
828                     expect(CanvasTest.hasCanvasBeenRedrawn(canvasWithAnnotations)).to.be(true);
829                 
830                     const canvasWithoutAnnotations = chartWithoutAnnotations.content().querySelector('canvas');
831                     CanvasTest.expectCanvasesMismatch(canvasWithAnnotations, canvasWithoutAnnotations);
832                 });
833             });
834         });
835     });
836
837 });