4 '../shared/statistics.js',
6 'models/data-model.js',
8 'models/time-series.js',
9 'models/measurement-set.js',
10 'models/measurement-cluster.js',
11 'models/measurement-adaptor.js',
13 'components/time-series-chart.js',
14 'components/interactive-time-series-chart.js'];
16 function posixTime(string) { return +new Date(string); }
18 const dayInMilliseconds = 24 * 3600 * 1000;
20 const sampleCluster = {
21 "clusterStart": posixTime('2016-01-01T00:00:00Z'),
22 "clusterSize": 7 * dayInMilliseconds,
23 "startTime": posixTime('2016-01-01T00:00:00Z'),
24 "endTime": posixTime('2016-01-08T00:00:00Z'),
25 "lastModified": posixTime('2016-01-18T00:00:00Z'),
29 "id", "mean", "iterationCount", "sum", "squareSum", "markedOutlier",
31 "commitTime", "build", "buildTime", "buildNumber", "builder"
36 1000, 100, 1, 100, 100, false,
37 [ [ 2000, 1, "4000", posixTime('2016-01-05T17:35:00Z')] ],
38 posixTime('2016-01-05T17:35:00Z'), 5000, posixTime('2016-01-05T19:23:00Z'), "10", 7
41 1001, 131, 1, 131, 131, true,
42 [ [ 2001, 1, "4001", posixTime('2016-01-05T18:43:01Z')] ],
43 posixTime('2016-01-05T18:43:01Z'), 5001, posixTime('2016-01-05T20:58:01Z'), "11", 7
46 1002, 122, 1, 122, 122, false,
47 [ [ 2002, 1, "4002", posixTime('2016-01-05T20:01:02Z') ] ],
48 posixTime('2016-01-05T20:01:02Z'), 5002, posixTime('2016-01-05T22:37:02Z'), "12", 7
51 1003, 113, 1, 113, 113, false,
52 [ [ 2003, 1, "4003", posixTime('2016-01-05T23:19:03Z') ] ],
53 posixTime('2016-01-05T23:19:03Z'), 5003, posixTime('2016-01-06T23:19:03Z'), "13", 7
56 1004, 124, 1, 124, 124, false,
57 [ [ 2004, 1, "4004", posixTime('2016-01-06T01:52:04Z') ] ],
58 posixTime('2016-01-06T01:52:04Z'), 5004, posixTime('2016-01-06T02:42:04Z'), "14", 7
61 1005, 115, 1, 115, 115, true,
62 [ [ 2005, 1, "4005", posixTime('2016-01-06T03:22:05Z') ] ],
63 posixTime('2016-01-06T03:22:05Z'), 5005, posixTime('2016-01-06T06:01:05Z'), "15", 7
66 1006, 116, 1, 116, 116, false,
67 [ [ 2006, 1, "4006", posixTime('2016-01-06T05:59:06Z') ] ],
68 posixTime('2016-01-06T05:59:06Z'), 5006, posixTime('2016-01-06T08:34:06Z'), "16", 7
74 function createChartWithSampleCluster(context, chartOptions = {}, options = {})
76 const TimeSeriesChart = context.symbols[options.interactiveChart ? 'InteractiveTimeSeriesChart' : 'TimeSeriesChart'];
77 const MeasurementSet = context.symbols.MeasurementSet;
79 const chart = new TimeSeriesChart([
82 lineStyle: options.lineStyle || '#666',
83 measurementSet: MeasurementSet.findSet(1, 1, 0),
84 interactive: options.interactive || false,
85 includeOutliers: options.includeOutliers || false,
86 sampleData: options.sampleData || false,
88 const element = chart.element();
89 element.style.width = options.width || '300px';
90 element.style.height = options.height || '100px';
91 context.document.body.appendChild(element);
96 function respondWithSampleCluster(request)
98 expect(request.url).to.be('../data/measurement-set-1-1.json');
99 expect(request.method).to.be('GET');
100 request.resolve(sampleCluster);
103 describe('TimeSeriesChart', () => {
105 it('should be constructible with an empty sourec list and an empty options', () => {
106 return new BrowsingContext().importScripts(scripts, 'TimeSeriesChart').then((TimeSeriesChart) => {
107 new TimeSeriesChart([], {});
111 describe('computeTimeGrid', () => {
112 it('should return an empty array when the start and the end times are identical', () => {
113 return new BrowsingContext().importScripts(scripts, 'TimeSeriesChart').then((TimeSeriesChart) => {
114 const someTime = Date.now();
115 const labels = TimeSeriesChart.computeTimeGrid(someTime, someTime, 0);
116 expect(labels).to.be.a('array');
117 expect(labels.length).to.be(0);
121 const millisecondsPerHour = 3600 * 1000;
122 const millisecondsPerDay = 24 * millisecondsPerHour;
124 it('should return an empty array when maxLabels is 0', () => {
125 return new BrowsingContext().importScripts(scripts, 'TimeSeriesChart').then((TimeSeriesChart) => {
126 const endTime = Date.now();
127 const labels = TimeSeriesChart.computeTimeGrid(endTime - millisecondsPerDay, endTime, 0);
128 expect(labels).to.be.a('array');
129 expect(labels.length).to.be(0);
133 it('should return an empty array when maxLabels is 0 even when the interval spans multiple months', () => {
134 return new BrowsingContext().importScripts(scripts, 'TimeSeriesChart').then((TimeSeriesChart) => {
135 const endTime = Date.now();
136 const labels = TimeSeriesChart.computeTimeGrid(endTime - 120 * millisecondsPerDay, endTime, 0);
137 expect(labels).to.be.a('array');
138 expect(labels.length).to.be(0);
142 function checkGridItem(item, label, expectedDate)
144 expect(item.label).to.be(label);
145 expect(item.time.__proto__.constructor.name).to.be('Date');
146 expect(+item.time).to.be(+new Date(expectedDate));
149 it('should generate one hour label with just day for two hour interval when maxLabels is 1', () => {
150 return new BrowsingContext().importScripts(scripts, 'TimeSeriesChart').then((TimeSeriesChart) => {
151 const endTime = new Date('2017-01-15T07:53:00Z');
152 const labels = TimeSeriesChart.computeTimeGrid(endTime - 2 * millisecondsPerHour, +endTime, 1);
153 expect(labels).to.be.a('array');
154 expect(labels.length).to.be(1);
155 checkGridItem(labels[0], '6AM', '2017-01-15T06:00:00Z');
159 it('should generate two two-hour labels for four hour interval when maxLabels is 2', () => {
160 return new BrowsingContext().importScripts(scripts, 'TimeSeriesChart').then((TimeSeriesChart) => {
161 const endTime = new Date('2017-01-15T07:53:00Z');
162 const labels = TimeSeriesChart.computeTimeGrid(endTime - 4 * millisecondsPerHour, +endTime, 2);
163 expect(labels).to.be.a('array');
164 expect(labels.length).to.be(2);
165 checkGridItem(labels[0], '4AM', '2017-01-15T04:00:00Z');
166 checkGridItem(labels[1], '6AM', '2017-01-15T06:00:00Z');
170 it('should generate six two-hour labels for twelve hour interval when maxLabels is 6', () => {
171 return new BrowsingContext().importScripts(scripts, 'TimeSeriesChart').then((TimeSeriesChart) => {
172 const endTime = new Date('2017-01-15T07:53:00Z');
173 const labels = TimeSeriesChart.computeTimeGrid(+endTime, +endTime + 12 * millisecondsPerHour, 6);
174 expect(labels).to.be.a('array');
175 expect(labels.length).to.be(6);
176 checkGridItem(labels[0], '8AM', '2017-01-15T08:00:00Z');
177 checkGridItem(labels[1], '10AM', '2017-01-15T10:00:00Z');
178 checkGridItem(labels[2], '12PM', '2017-01-15T12:00:00Z');
179 checkGridItem(labels[3], '2PM', '2017-01-15T14:00:00Z');
180 checkGridItem(labels[4], '4PM', '2017-01-15T16:00:00Z');
181 checkGridItem(labels[5], '6PM', '2017-01-15T18:00:00Z');
185 it('should generate six two-hour labels with one date label for twelve hour interval that cross a day when maxLabels is 6', () => {
186 return new BrowsingContext().importScripts(scripts, 'TimeSeriesChart').then((TimeSeriesChart) => {
187 const endTime = new Date('2017-01-15T16:12:00Z');
188 const labels = TimeSeriesChart.computeTimeGrid(+endTime, +endTime + 12 * millisecondsPerHour, 6);
189 expect(labels).to.be.a('array');
190 expect(labels.length).to.be(6);
191 checkGridItem(labels[0], '6PM', '2017-01-15T18:00:00Z');
192 checkGridItem(labels[1], '8PM', '2017-01-15T20:00:00Z');
193 checkGridItem(labels[2], '10PM', '2017-01-15T22:00:00Z');
194 checkGridItem(labels[3], '1/16', '2017-01-16T00:00:00Z');
195 checkGridItem(labels[4], '2AM', '2017-01-16T02:00:00Z');
196 checkGridItem(labels[5], '4AM', '2017-01-16T04:00:00Z');
200 it('should generate three two-hour labels for six hour interval that cross a year when maxLabels is 5', () => {
201 return new BrowsingContext().importScripts(scripts, 'TimeSeriesChart').then((TimeSeriesChart) => {
202 const endTime = new Date('2016-12-31T21:37:00Z');
203 const labels = TimeSeriesChart.computeTimeGrid(+endTime, +endTime + 6 * millisecondsPerHour, 5);
204 expect(labels).to.be.a('array');
205 expect(labels.length).to.be(3);
206 checkGridItem(labels[0], '10PM', '2016-12-31T22:00:00Z');
207 checkGridItem(labels[1], '1/1', '2017-01-01T00:00:00Z');
208 checkGridItem(labels[2], '2AM', '2017-01-01T02:00:00Z');
212 it('should generate one one-day label for one day interval when maxLabels is 1', () => {
213 return new BrowsingContext().importScripts(scripts, 'TimeSeriesChart').then((TimeSeriesChart) => {
214 const endTime = new Date('2017-01-15T07:53:00Z');
215 const labels = TimeSeriesChart.computeTimeGrid(endTime - millisecondsPerDay, +endTime, 1);
216 expect(labels).to.be.a('array');
217 expect(labels.length).to.be(1);
218 checkGridItem(labels[0], '1/15', '2017-01-15T00:00:00Z');
222 it('should generate two one-day labels for one day interval when maxLabels is 2', () => {
223 return new BrowsingContext().importScripts(scripts, 'TimeSeriesChart').then((TimeSeriesChart) => {
224 const endTime = new Date('2017-01-15T07:53:00Z');
225 const labels = TimeSeriesChart.computeTimeGrid(endTime - millisecondsPerDay, +endTime, 2);
226 expect(labels).to.be.a('array');
227 expect(labels.length).to.be(2);
228 checkGridItem(labels[0], '1/14 12PM', '2017-01-14T12:00:00Z');
229 checkGridItem(labels[1], '1/15', '2017-01-15T00:00:00Z');
233 it('should generate four half-day labels for two day interval when maxLabels is 5', () => {
234 return new BrowsingContext().importScripts(scripts, 'TimeSeriesChart').then((TimeSeriesChart) => {
235 const endTime = new Date('2017-01-15T16:12:00Z');
236 const labels = TimeSeriesChart.computeTimeGrid(+endTime, +endTime + 2 * millisecondsPerDay, 5);
237 expect(labels).to.be.a('array');
238 expect(labels.length).to.be(4);
239 checkGridItem(labels[0], '1/16', '2017-01-16T00:00:00Z');
240 checkGridItem(labels[1], '12PM', '2017-01-16T12:00:00Z');
241 checkGridItem(labels[2], '1/17', '2017-01-17T00:00:00Z');
242 checkGridItem(labels[3], '12PM', '2017-01-17T12:00:00Z');
246 it('should generate four half-day labels for two day interval that cross a year when maxLabels is 5', () => {
247 return new BrowsingContext().importScripts(scripts, 'TimeSeriesChart').then((TimeSeriesChart) => {
248 const endTime = new Date('2016-12-31T09:12:00Z');
249 const labels = TimeSeriesChart.computeTimeGrid(+endTime, +endTime + 2 * millisecondsPerDay, 5);
250 expect(labels).to.be.a('array');
251 expect(labels.length).to.be(4);
252 checkGridItem(labels[0], '12/31 12PM', '2016-12-31T12:00:00Z');
253 checkGridItem(labels[1], '1/1', '2017-01-01T00:00:00Z');
254 checkGridItem(labels[2], '12PM', '2017-01-01T12:00:00Z');
255 checkGridItem(labels[3], '1/2', '2017-01-02T00:00:00Z');
259 it('should generate seven per-day labels for one week interval when maxLabels is 10', () => {
260 return new BrowsingContext().importScripts(scripts, 'TimeSeriesChart').then((TimeSeriesChart) => {
261 const endTime = new Date('2017-01-15T07:53:00Z');
262 const labels = TimeSeriesChart.computeTimeGrid(endTime - 7 * millisecondsPerDay, endTime, 10);
263 expect(labels).to.be.a('array');
264 expect(labels.length).to.be(7);
265 checkGridItem(labels[0], '1/9', '2017-01-09T00:00:00Z');
266 checkGridItem(labels[1], '1/10', '2017-01-10T00:00:00Z');
267 checkGridItem(labels[2], '1/11', '2017-01-11T00:00:00Z');
268 checkGridItem(labels[3], '1/12', '2017-01-12T00:00:00Z');
269 checkGridItem(labels[4], '1/13', '2017-01-13T00:00:00Z');
270 checkGridItem(labels[5], '1/14', '2017-01-14T00:00:00Z');
271 checkGridItem(labels[6], '1/15', '2017-01-15T00:00:00Z');
275 it('should generate three two-day labels for one week interval when maxLabels is 4', () => {
276 return new BrowsingContext().importScripts(scripts, 'TimeSeriesChart').then((TimeSeriesChart) => {
277 const endTime = new Date('2017-01-15T07:53:00Z');
278 const labels = TimeSeriesChart.computeTimeGrid(endTime - 7 * millisecondsPerDay, endTime, 4);
279 expect(labels).to.be.a('array');
280 expect(labels.length).to.be(3);
281 checkGridItem(labels[0], '1/10', '2017-01-10T00:00:00Z');
282 checkGridItem(labels[1], '1/12', '2017-01-12T00:00:00Z');
283 checkGridItem(labels[2], '1/14', '2017-01-14T00:00:00Z');
287 it('should generate seven one-day labels for two week interval when maxLabels is 8', () => {
288 return new BrowsingContext().importScripts(scripts, 'TimeSeriesChart').then((TimeSeriesChart) => {
289 const endTime = new Date('2017-01-15T18:53:00Z');
290 const labels = TimeSeriesChart.computeTimeGrid(+endTime, +endTime + 14 * millisecondsPerDay, 8);
291 expect(labels).to.be.a('array');
292 expect(labels.length).to.be(7);
293 checkGridItem(labels[0], '1/17', '2017-01-17T00:00:00Z');
294 checkGridItem(labels[1], '1/19', '2017-01-19T00:00:00Z');
295 checkGridItem(labels[2], '1/21', '2017-01-21T00:00:00Z');
296 checkGridItem(labels[3], '1/23', '2017-01-23T00:00:00Z');
297 checkGridItem(labels[4], '1/25', '2017-01-25T00:00:00Z');
298 checkGridItem(labels[5], '1/27', '2017-01-27T00:00:00Z');
299 checkGridItem(labels[6], '1/29', '2017-01-29T00:00:00Z');
303 it('should generate two one-week labels for two week interval when maxLabels is 3', () => {
304 return new BrowsingContext().importScripts(scripts, 'TimeSeriesChart').then((TimeSeriesChart) => {
305 const endTime = new Date('2017-01-15T18:53:00Z');
306 const labels = TimeSeriesChart.computeTimeGrid(+endTime, +endTime + 14 * millisecondsPerDay, 3);
307 expect(labels).to.be.a('array');
308 expect(labels.length).to.be(2);
309 checkGridItem(labels[0], '1/22', '2017-01-22T00:00:00Z');
310 checkGridItem(labels[1], '1/29', '2017-01-29T00:00:00Z');
314 it('should generate seven one-month labels for six and half months interval starting before 15th when maxLabels is 7', () => {
315 return new BrowsingContext().importScripts(scripts, 'TimeSeriesChart').then((TimeSeriesChart) => {
316 const endTime = new Date('2017-01-15T18:53:00Z');
317 const labels = TimeSeriesChart.computeTimeGrid(new Date('2016-07-12T18:53:00Z'), new Date('2017-01-18T08:17:53Z'), 7);
318 expect(labels).to.be.a('array');
319 expect(labels.length).to.be(7);
320 checkGridItem(labels[0], '7/15', '2016-07-15T00:00:00Z');
321 checkGridItem(labels[1], '8/15', '2016-08-15T00:00:00Z');
322 checkGridItem(labels[2], '9/15', '2016-09-15T00:00:00Z');
323 checkGridItem(labels[3], '10/15', '2016-10-15T00:00:00Z');
324 checkGridItem(labels[4], '11/15', '2016-11-15T00:00:00Z');
325 checkGridItem(labels[5], '12/15', '2016-12-15T00:00:00Z');
326 checkGridItem(labels[6], '1/15', '2017-01-15T00:00:00Z');
330 it('should generate seven one-month labels for six months interval staring after 15th when maxLabels is 7', () => {
331 return new BrowsingContext().importScripts(scripts, 'TimeSeriesChart').then((TimeSeriesChart) => {
332 const endTime = new Date('2017-01-15T18:53:00Z');
333 const labels = TimeSeriesChart.computeTimeGrid(new Date('2016-07-18T18:53:00Z'), new Date('2017-01-18T08:17:53Z'), 7);
334 expect(labels).to.be.a('array');
335 expect(labels.length).to.be(6);
336 checkGridItem(labels[0], '8/1', '2016-08-01T00:00:00Z');
337 checkGridItem(labels[1], '9/1', '2016-09-01T00:00:00Z');
338 checkGridItem(labels[2], '10/1', '2016-10-01T00:00:00Z');
339 checkGridItem(labels[3], '11/1', '2016-11-01T00:00:00Z');
340 checkGridItem(labels[4], '12/1', '2016-12-01T00:00:00Z');
344 it('should generate six two-months labels for one year interval when maxLabels is 7', () => {
345 return new BrowsingContext().importScripts(scripts, 'TimeSeriesChart').then((TimeSeriesChart) => {
346 const endTime = new Date('2017-01-15T18:53:00Z');
347 const labels = TimeSeriesChart.computeTimeGrid(new Date('2016-07-11T18:53:00Z'), new Date('2017-07-27T08:17:53Z'), 7);
348 expect(labels).to.be.a('array');
349 expect(labels.length).to.be(6);
350 checkGridItem(labels[0], '9/1', '2016-09-01T00:00:00Z');
351 checkGridItem(labels[1], '11/1', '2016-11-01T00:00:00Z');
352 checkGridItem(labels[2], '1/1', '2017-01-01T00:00:00Z');
353 checkGridItem(labels[3], '3/1', '2017-03-01T00:00:00Z');
354 checkGridItem(labels[4], '5/1', '2017-05-01T00:00:00Z');
355 checkGridItem(labels[5], '7/1', '2017-07-01T00:00:00Z');
359 it('should generate four three-months labels for one year interval when maxLabels is 5', () => {
360 return new BrowsingContext().importScripts(scripts, 'TimeSeriesChart').then((TimeSeriesChart) => {
361 const endTime = new Date('2017-01-15T18:53:00Z');
362 const labels = TimeSeriesChart.computeTimeGrid(new Date('2016-07-11T18:53:00Z'), new Date('2017-07-27T08:17:53Z'), 5);
363 expect(labels).to.be.a('array');
364 expect(labels.length).to.be(4);
365 checkGridItem(labels[0], '10/1', '2016-10-01T00:00:00Z');
366 checkGridItem(labels[1], '1/1', '2017-01-01T00:00:00Z');
367 checkGridItem(labels[2], '4/1', '2017-04-01T00:00:00Z');
368 checkGridItem(labels[3], '7/1', '2017-07-01T00:00:00Z');
373 describe('computeValueGrid', () => {
375 function checkValueGrid(actual, expected) {
376 expect(actual).to.be.a('array');
377 expect(JSON.stringify(actual)).to.be(JSON.stringify(expected));
380 function approximate(number)
382 return Math.round(number * 100000000) / 100000000;
385 it('should generate [0.5, 1.0, 1.5, 2.0] for [0.3, 2.3] when maxLabels is 5', () => {
386 return new BrowsingContext().importScripts(scripts, 'TimeSeriesChart', 'Metric').then((symbols) => {
387 const [TimeSeriesChart, Metric] = symbols;
388 const grid = TimeSeriesChart.computeValueGrid(0.3, 2.3, 5, Metric.makeFormatter('pt', 2));
389 expect(grid.map((item) => approximate(item.value))).to.eql([0.5, 1.0, 1.5, 2.0]);
390 expect(grid.map((item) => item.label)).to.eql(['0.5 pt', '1.0 pt', '1.5 pt', '2.0 pt']);
394 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', () => {
395 return new BrowsingContext().importScripts(scripts, 'TimeSeriesChart', 'Metric').then((symbols) => {
396 const [TimeSeriesChart, Metric] = symbols;
397 const grid = TimeSeriesChart.computeValueGrid(0.3, 2.3, 10, Metric.makeFormatter('pt', 2));
398 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]);
399 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']);
403 it('should generate [1, 2] for [0.3, 2.3] when maxLabels is 2', () => {
404 return new BrowsingContext().importScripts(scripts, 'TimeSeriesChart', 'Metric').then((symbols) => {
405 const [TimeSeriesChart, Metric] = symbols;
406 const grid = TimeSeriesChart.computeValueGrid(0.3, 2.3, 2, Metric.makeFormatter('pt', 2));
407 expect(grid.map((item) => item.value)).to.eql([1, 2]);
408 expect(grid.map((item) => item.label)).to.eql(['1.0 pt', '2.0 pt']);
412 it('should generate [0.4, 0.6, 0.8, 1.0, 1.2] for [0.3, 1.3] when maxLabels is 5', () => {
413 return new BrowsingContext().importScripts(scripts, 'TimeSeriesChart', 'Metric').then((symbols) => {
414 const [TimeSeriesChart, Metric] = symbols;
415 const grid = TimeSeriesChart.computeValueGrid(0.3, 1.3, 5, Metric.makeFormatter('pt', 2));
416 expect(grid.map((item) => approximate(item.value))).to.eql([0.4, 0.6, 0.8, 1.0, 1.2]);
417 expect(grid.map((item) => item.label)).to.eql(['0.4 pt', '0.6 pt', '0.8 pt', '1.0 pt', '1.2 pt']);
421 it('should generate [0.2, 0.4, 0.6, 0.8, 1, 1.2] for [0.2, 1.3] when maxLabels is 10', () => {
422 return new BrowsingContext().importScripts(scripts, 'TimeSeriesChart', 'Metric').then((symbols) => {
423 const [TimeSeriesChart, Metric] = symbols;
424 const grid = TimeSeriesChart.computeValueGrid(0.2, 1.3, 10, Metric.makeFormatter('pt', 2));
425 expect(grid.map((item) => approximate(item.value))).to.eql([0.2, 0.4, 0.6, 0.8, 1, 1.2]);
426 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']);
430 it('should generate [0.5, 1.0] for [0.3, 1.3] when maxLabels is 4', () => {
431 return new BrowsingContext().importScripts(scripts, 'TimeSeriesChart', 'Metric').then((symbols) => {
432 const [TimeSeriesChart, Metric] = symbols;
433 const grid = TimeSeriesChart.computeValueGrid(0.3, 1.3, 4, Metric.makeFormatter('pt', 2));
434 expect(grid.map((item) => approximate(item.value))).to.eql([0.5, 1.0]);
435 expect(grid.map((item) => item.label)).to.eql(['0.5 pt', '1.0 pt']);
439 it('should generate [10, 20, 30] for [4, 35] when maxLabels is 4', () => {
440 return new BrowsingContext().importScripts(scripts, 'TimeSeriesChart', 'Metric').then((symbols) => {
441 const [TimeSeriesChart, Metric] = symbols;
442 const grid = TimeSeriesChart.computeValueGrid(4, 35, 4, Metric.makeFormatter('pt', 2));
443 expect(grid.map((item) => item.value)).to.eql([10, 20, 30]);
444 expect(grid.map((item) => item.label)).to.eql(['10 pt', '20 pt', '30 pt']);
448 it('should generate [10, 20, 30] for [4, 35] when maxLabels is 6', () => {
449 return new BrowsingContext().importScripts(scripts, 'TimeSeriesChart', 'Metric').then((symbols) => {
450 const [TimeSeriesChart, Metric] = symbols;
451 const grid = TimeSeriesChart.computeValueGrid(4, 35, 6, Metric.makeFormatter('pt', 2));
452 expect(grid.map((item) => item.value)).to.eql([10, 20, 30]);
453 expect(grid.map((item) => item.label)).to.eql(['10 pt', '20 pt', '30 pt']);
457 it('should generate [10, 15, 20, 25, 30, 35] for [6, 35] when maxLabels is 6', () => {
458 return new BrowsingContext().importScripts(scripts, 'TimeSeriesChart', 'Metric').then((symbols) => {
459 const [TimeSeriesChart, Metric] = symbols;
460 const grid = TimeSeriesChart.computeValueGrid(6, 35, 6, Metric.makeFormatter('pt', 2));
461 expect(grid.map((item) => item.value)).to.eql([10, 15, 20, 25, 30, 35]);
462 expect(grid.map((item) => item.label)).to.eql(['10 pt', '15 pt', '20 pt', '25 pt', '30 pt', '35 pt']);
466 it('should generate [110, 115, 120, 125, 130] for [107, 134] when maxLabels is 6', () => {
467 return new BrowsingContext().importScripts(scripts, 'TimeSeriesChart', 'Metric').then((symbols) => {
468 const [TimeSeriesChart, Metric] = symbols;
469 const grid = TimeSeriesChart.computeValueGrid(107, 134, 6, Metric.makeFormatter('pt', 3));
470 expect(grid.map((item) => item.value)).to.eql([110, 115, 120, 125, 130]);
471 expect(grid.map((item) => item.label)).to.eql(['110 pt', '115 pt', '120 pt', '125 pt', '130 pt']);
475 it('should generate [5e7, 10e7] for [1e7, 1e8] when maxLabels is 4', () => {
476 return new BrowsingContext().importScripts(scripts, 'TimeSeriesChart', 'Metric').then((symbols) => {
477 const [TimeSeriesChart, Metric] = symbols;
478 const grid = TimeSeriesChart.computeValueGrid(1e7, 1e8, 4, Metric.makeFormatter('pt', 3));
479 expect(grid.map((item) => item.value)).to.eql([5e7, 10e7]);
480 expect(grid.map((item) => item.label)).to.eql(['50.0 Mpt', '100 Mpt']);
484 it('should generate [2e7, 4e7, 6e7, 8e7, 10e7] for [1e7, 1e8] when maxLabels is 5', () => {
485 return new BrowsingContext().importScripts(scripts, 'TimeSeriesChart', 'Metric').then((symbols) => {
486 const [TimeSeriesChart, Metric] = symbols;
487 const grid = TimeSeriesChart.computeValueGrid(1e7, 1e8, 5, Metric.makeFormatter('pt', 3));
488 expect(grid.map((item) => item.value)).to.eql([2e7, 4e7, 6e7, 8e7, 10e7]);
489 expect(grid.map((item) => item.label)).to.eql(['20.0 Mpt', '40.0 Mpt', '60.0 Mpt', '80.0 Mpt', '100 Mpt']);
493 it('should generate [-1.5, -1.0, -0.5, 0.0, 0.5] for [-1.8, 0.7] when maxLabels is 5', () => {
494 return new BrowsingContext().importScripts(scripts, 'TimeSeriesChart', 'Metric').then((symbols) => {
495 const [TimeSeriesChart, Metric] = symbols;
496 const grid = TimeSeriesChart.computeValueGrid(-1.8, 0.7, 5, Metric.makeFormatter('pt', 2));
497 expect(grid.map((item) => approximate(item.value))).to.eql([-1.5, -1.0, -0.5, 0.0, 0.5]);
498 expect(grid.map((item) => item.label)).to.eql(['-1.5 pt', '-1.0 pt', '-0.5 pt', '0.0 pt', '0.5 pt']);
502 it('should generate [200ms, 400ms, 600ms, 800ms, 1.00s, 1.20s] for [0.2, 1.3] when maxLabels is 10 and unit is seconds', () => {
503 return new BrowsingContext().importScripts(scripts, 'TimeSeriesChart', 'Metric').then((symbols) => {
504 const [TimeSeriesChart, Metric] = symbols;
505 const grid = TimeSeriesChart.computeValueGrid(0.2, 1.3, 10, Metric.makeFormatter('s', 3));
506 expect(grid.map((item) => approximate(item.value))).to.eql([0.2, 0.4, 0.6, 0.8, 1, 1.2]);
507 expect(grid.map((item) => item.label)).to.eql(['200 ms', '400 ms', '600 ms', '800 ms', '1.00 s', '1.20 s']);
511 it('should generate [2.0GB, 4.0GB, 6.0GB] for [1.2GB, 7.2GB] when maxLabels is 4 and unit is bytes', () => {
512 return new BrowsingContext().importScripts(scripts, 'TimeSeriesChart', 'Metric').then((symbols) => {
513 const [TimeSeriesChart, Metric] = symbols;
514 const gigabytes = Math.pow(1024, 3);
515 const grid = TimeSeriesChart.computeValueGrid(1.2 * gigabytes, 7.2 * gigabytes, 4, Metric.makeFormatter('B', 2));
516 expect(grid.map((item) => approximate(item.value))).to.eql([2 * gigabytes, 4 * gigabytes, 6 * gigabytes]);
517 expect(grid.map((item) => item.label)).to.eql(['2.0 GB', '4.0 GB', '6.0 GB']);
521 it('should generate [0.6GB, 0.8GB, 1.0GB, 1.2GB] for [0.53GB, 1.23GB] when maxLabels is 4 and unit is bytes', () => {
522 return new BrowsingContext().importScripts(scripts, 'TimeSeriesChart', 'Metric').then((symbols) => {
523 const [TimeSeriesChart, Metric] = symbols;
524 const gigabytes = Math.pow(1024, 3);
525 const grid = TimeSeriesChart.computeValueGrid(0.53 * gigabytes, 1.23 * gigabytes, 4, Metric.makeFormatter('B', 2));
526 expect(grid.map((item) => item.label)).to.eql(['0.6 GB', '0.8 GB', '1.0 GB', '1.2 GB']);
532 describe('fetchMeasurementSets', () => {
534 it('should fetch the measurement set and create a canvas element upon receiving the data', () => {
535 const context = new BrowsingContext();
536 return context.importScripts(scripts, 'ComponentBase', 'TimeSeriesChart', 'MeasurementSet', 'MockRemoteAPI').then(() => {
537 const chart = createChartWithSampleCluster(context);
539 chart.setDomain(sampleCluster.startTime, sampleCluster.endTime);
540 chart.fetchMeasurementSets();
542 const requests = context.symbols.MockRemoteAPI.requests;
543 expect(requests.length).to.be(1);
544 respondWithSampleCluster(requests[0]);
546 expect(chart.content().querySelector('canvas')).to.be(null);
547 return waitForComponentsToRender(context).then(() => {
548 expect(chart.content().querySelector('canvas')).to.not.be(null);
553 it('should immediately enqueue to render when the measurement set had already been fetched', () => {
554 const context = new BrowsingContext();
555 return context.importScripts(scripts, 'ComponentBase', 'TimeSeriesChart', 'MeasurementSet', 'MockRemoteAPI').then(() => {
556 const chart = createChartWithSampleCluster(context);
558 let set = context.symbols.MeasurementSet.findSet(1, 1, 0);
559 let promise = set.fetchBetween(sampleCluster.startTime, sampleCluster.endTime);
561 const requests = context.symbols.MockRemoteAPI.requests;
562 expect(requests.length).to.be(1);
563 respondWithSampleCluster(requests[0]);
565 return promise.then(() => {
566 expect(chart.content().querySelector('canvas')).to.be(null);
567 chart.setDomain(sampleCluster.startTime, sampleCluster.endTime);
568 chart.fetchMeasurementSets();
569 return waitForComponentsToRender(context);
571 expect(requests.length).to.be(1);
572 expect(chart.content().querySelector('canvas')).to.not.be(null);
577 it('should dispatch "dataChange" action once the fetched data becomes available', () => {
578 const context = new BrowsingContext();
579 return context.importScripts(scripts, 'ComponentBase', 'TimeSeriesChart', 'MeasurementSet', 'MockRemoteAPI').then(() => {
580 const chart = createChartWithSampleCluster(context);
582 let dataChangeCount = 0;
583 chart.listenToAction('dataChange', () => dataChangeCount++);
585 chart.setDomain(sampleCluster.startTime, sampleCluster.endTime);
586 chart.fetchMeasurementSets();
588 const requests = context.symbols.MockRemoteAPI.requests;
589 expect(requests.length).to.be(1);
590 respondWithSampleCluster(requests[0]);
592 expect(dataChangeCount).to.be(0);
593 expect(chart.sampledTimeSeriesData('current')).to.be(null);
594 expect(chart.content().querySelector('canvas')).to.be(null);
595 return waitForComponentsToRender(context).then(() => {
596 expect(dataChangeCount).to.be(1);
597 expect(chart.sampledTimeSeriesData('current')).to.not.be(null);
598 expect(chart.content().querySelector('canvas')).to.not.be(null);
604 describe('sampledTimeSeriesData', () => {
605 it('should not contain an outlier when includeOutliers is false', () => {
606 const context = new BrowsingContext();
607 return context.importScripts(scripts, 'ComponentBase', 'TimeSeriesChart', 'MeasurementSet', 'MockRemoteAPI').then(() => {
608 const chart = createChartWithSampleCluster(context, {}, {includeOutliers: false});
610 chart.setDomain(sampleCluster.startTime, sampleCluster.endTime);
611 chart.fetchMeasurementSets();
612 respondWithSampleCluster(context.symbols.MockRemoteAPI.requests[0]);
614 return waitForComponentsToRender(context).then(() => {
615 const view = chart.sampledTimeSeriesData('current');
616 expect(view.length()).to.be(5);
617 for (let point of view)
618 expect(point.markedOutlier).to.be(false);
623 it('should contain every outlier when includeOutliers is true', () => {
624 const context = new BrowsingContext();
625 return context.importScripts(scripts, 'ComponentBase', 'TimeSeriesChart', 'MeasurementSet', 'MockRemoteAPI').then(() => {
626 const chart = createChartWithSampleCluster(context, {}, {includeOutliers: true});
628 chart.setDomain(sampleCluster.startTime, sampleCluster.endTime);
629 chart.fetchMeasurementSets();
630 respondWithSampleCluster(context.symbols.MockRemoteAPI.requests[0]);
632 return waitForComponentsToRender(context).then(() => {
633 const view = chart.sampledTimeSeriesData('current');
634 expect(view.length()).to.be(7);
635 expect(view.findPointByIndex(1).markedOutlier).to.be(true);
636 expect(view.findPointByIndex(5).markedOutlier).to.be(true);
641 it('should only contain data points in the domain and one preceding point when there are no succeeding points', () => {
642 const context = new BrowsingContext();
643 return context.importScripts(scripts, 'ComponentBase', 'TimeSeriesChart', 'MeasurementSet', 'MockRemoteAPI').then(() => {
644 const chart = createChartWithSampleCluster(context, {}, {includeOutliers: true});
646 chart.setDomain(posixTime('2016-01-06T00:00:00Z'), posixTime('2016-01-07T00:00:00Z'));
647 chart.fetchMeasurementSets();
648 respondWithSampleCluster(context.symbols.MockRemoteAPI.requests[0]);
650 return waitForComponentsToRender(context).then(() => {
651 const view = chart.sampledTimeSeriesData('current');
652 expect([...view].map((point) => point.id)).to.be.eql([1003, 1004, 1005, 1006]);
657 it('should only contain data points in the domain and one succeeding point when there are no preceding points', () => {
658 const context = new BrowsingContext();
659 return context.importScripts(scripts, 'ComponentBase', 'TimeSeriesChart', 'MeasurementSet', 'MockRemoteAPI').then(() => {
660 const chart = createChartWithSampleCluster(context, {}, {includeOutliers: true});
662 chart.setDomain(posixTime('2016-01-05T00:00:00Z'), posixTime('2016-01-06T00:00:00Z'));
663 chart.fetchMeasurementSets();
664 chart.fetchMeasurementSets();
665 respondWithSampleCluster(context.symbols.MockRemoteAPI.requests[0]);
667 return waitForComponentsToRender(context).then(() => {
668 const view = chart.sampledTimeSeriesData('current');
669 expect([...view].map((point) => point.id)).to.be.eql([1000, 1001, 1002, 1003, 1004]);
674 it('should only contain data points in the domain and one preceding point and one succeeding point', () => {
675 const context = new BrowsingContext();
676 return context.importScripts(scripts, 'ComponentBase', 'TimeSeriesChart', 'MeasurementSet', 'MockRemoteAPI').then(() => {
677 const chart = createChartWithSampleCluster(context, {}, {includeOutliers: true});
679 chart.setDomain(posixTime('2016-01-05T21:00:00Z'), posixTime('2016-01-06T02:00:00Z'));
680 chart.fetchMeasurementSets();
681 respondWithSampleCluster(context.symbols.MockRemoteAPI.requests[0]);
683 return waitForComponentsToRender(context).then(() => {
684 const view = chart.sampledTimeSeriesData('current');
685 expect([...view].map((point) => point.id)).to.be.eql([1002, 1003, 1004, 1005]);
691 describe('render', () => {
692 it('should update the canvas size and its content after the window has been resized', () => {
693 const context = new BrowsingContext();
694 return context.importScripts(scripts, 'ComponentBase', 'TimeSeriesChart', 'MeasurementSet', 'MockRemoteAPI').then(() => {
695 const chart = createChartWithSampleCluster(context, {}, {width: '100%', height: '100%'});
697 let dataChangeCount = 0;
698 chart.listenToAction('dataChange', () => dataChangeCount++);
700 chart.setDomain(sampleCluster.startTime, sampleCluster.endTime);
701 chart.fetchMeasurementSets();
703 const requests = context.symbols.MockRemoteAPI.requests;
704 expect(requests.length).to.be(1);
705 respondWithSampleCluster(requests[0]);
707 expect(dataChangeCount).to.be(0);
708 expect(chart.sampledTimeSeriesData('current')).to.be(null);
709 expect(chart.content().querySelector('canvas')).to.be(null);
713 return waitForComponentsToRender(context).then(() => {
714 expect(dataChangeCount).to.be(1);
715 expect(chart.sampledTimeSeriesData('current')).to.not.be(null);
716 canvas = chart.content().querySelector('canvas');
717 expect(canvas).to.not.be(null);
719 originalWidth = canvas.offsetWidth;
720 originalHeight = canvas.offsetHeight;
721 expect(originalWidth).to.be(context.document.body.offsetWidth);
722 expect(originalHeight).to.be(context.document.body.offsetHeight);
724 CanvasTest.fillCanvasBeforeRedrawCheck(canvas);
725 context.iframe.style.width = context.iframe.offsetWidth * 2 + 'px';
726 context.global.dispatchEvent(new Event('resize'));
728 expect(canvas.offsetWidth).to.be(originalWidth);
729 expect(canvas.offsetHeight).to.be(originalHeight);
731 return waitForComponentsToRender(context);
733 expect(dataChangeCount).to.be(2);
734 expect(canvas.offsetWidth).to.be.greaterThan(originalWidth);
735 expect(canvas.offsetWidth).to.be(context.document.body.offsetWidth);
736 expect(canvas.offsetHeight).to.be(originalHeight);
737 expect(CanvasTest.hasCanvasBeenRedrawn(canvas)).to.be(true);
742 it('should not update update the canvas when the window has been resized but its dimensions stays the same', () => {
743 const context = new BrowsingContext();
744 return context.importScripts(scripts, 'ComponentBase', 'TimeSeriesChart', 'MeasurementSet', 'MockRemoteAPI').then(() => {
745 const chart = createChartWithSampleCluster(context, {}, {width: '100px', height: '100px'});
747 let dataChangeCount = 0;
748 chart.listenToAction('dataChange', () => dataChangeCount++);
750 chart.setDomain(sampleCluster.startTime, sampleCluster.endTime);
751 chart.fetchMeasurementSets();
753 const requests = context.symbols.MockRemoteAPI.requests;
754 expect(requests.length).to.be(1);
755 respondWithSampleCluster(requests[0]);
756 expect(dataChangeCount).to.be(0);
760 return waitForComponentsToRender(context).then(() => {
761 expect(dataChangeCount).to.be(1);
762 data = chart.sampledTimeSeriesData('current');
763 expect(data).to.not.be(null);
764 canvas = chart.content().querySelector('canvas');
765 expect(canvas).to.not.be(null);
767 expect(canvas.offsetWidth).to.be(100);
768 expect(canvas.offsetHeight).to.be(100);
770 CanvasTest.fillCanvasBeforeRedrawCheck(canvas);
771 context.iframe.style.width = context.iframe.offsetWidth * 2 + 'px';
772 context.global.dispatchEvent(new Event('resize'));
774 expect(canvas.offsetWidth).to.be(100);
775 expect(canvas.offsetHeight).to.be(100);
777 return waitForComponentsToRender(context);
779 expect(dataChangeCount).to.be(1);
780 expect(chart.sampledTimeSeriesData('current')).to.be(data);
781 expect(canvas.offsetWidth).to.be(100);
782 expect(canvas.offsetHeight).to.be(100);
783 expect(CanvasTest.hasCanvasBeenRedrawn(canvas)).to.be(false);
788 it('should render Y-axis', () => {
789 const context = new BrowsingContext();
790 return context.importScripts(scripts, 'ComponentBase', 'TimeSeriesChart', 'MeasurementSet', 'MockRemoteAPI', 'Metric').then(() => {
791 const chartWithoutYAxis = createChartWithSampleCluster(context, {axis:
795 valueFormatter: context.symbols.Metric.makeFormatter('ms', 3),
798 const chartWithYAxis1 = createChartWithSampleCluster(context, {axis:
803 valueFormatter: context.symbols.Metric.makeFormatter('ms', 3),
806 const chartWithYAxis2 = createChartWithSampleCluster(context, {axis:
811 valueFormatter: context.symbols.Metric.makeFormatter('B', 3),
815 chartWithoutYAxis.setDomain(sampleCluster.startTime, sampleCluster.endTime);
816 chartWithoutYAxis.fetchMeasurementSets();
817 chartWithYAxis1.setDomain(sampleCluster.startTime, sampleCluster.endTime);
818 chartWithYAxis1.fetchMeasurementSets();
819 chartWithYAxis2.setDomain(sampleCluster.startTime, sampleCluster.endTime);
820 chartWithYAxis2.fetchMeasurementSets();
822 const requests = context.symbols.MockRemoteAPI.requests;
823 expect(requests.length).to.be(1);
824 respondWithSampleCluster(requests[0]);
826 return waitForComponentsToRender(context).then(() => {
827 let canvasWithoutYAxis = chartWithoutYAxis.content().querySelector('canvas');
828 let canvasWithYAxis1 = chartWithYAxis1.content().querySelector('canvas');
829 let canvasWithYAxis2 = chartWithYAxis2.content().querySelector('canvas');
830 CanvasTest.expectCanvasesMismatch(canvasWithoutYAxis, canvasWithYAxis1);
831 CanvasTest.expectCanvasesMismatch(canvasWithoutYAxis, canvasWithYAxis1);
832 CanvasTest.expectCanvasesMismatch(canvasWithYAxis1, canvasWithYAxis2);
834 expect(CanvasTest.canvasContainsColor(canvasWithYAxis1, {r: 204, g: 204, b: 204},
835 {x: canvasWithYAxis1.width - 1, width: 1, y: 0, height: canvasWithYAxis1.height})).to.be(true);
840 it('should render the sampled time series', () => {
841 const context = new BrowsingContext();
842 return context.importScripts(scripts, 'ComponentBase', 'TimeSeriesChart', 'InteractiveTimeSeriesChart', 'MeasurementSet', 'MockRemoteAPI').then(() => {
843 const chartWithoutSampling = createChartWithSampleCluster(context, {}, {lineStyle: 'rgb(0, 128, 255)', width: '100px', height: '100px', sampleData: false});
844 const chartWithSampling = createChartWithSampleCluster(context, {}, {lineStyle: 'rgb(0, 128, 255)', width: '100px', height: '100px', sampleData: true});
846 chartWithoutSampling.setDomain(sampleCluster.startTime, sampleCluster.endTime);
847 chartWithoutSampling.fetchMeasurementSets();
848 respondWithSampleCluster(context.symbols.MockRemoteAPI.requests[0]);
850 chartWithSampling.setDomain(sampleCluster.startTime, sampleCluster.endTime);
851 chartWithSampling.fetchMeasurementSets();
853 let canvasWithSampling;
854 let canvasWithoutSampling;
855 return waitForComponentsToRender(context).then(() => {
856 canvasWithoutSampling = chartWithoutSampling.content().querySelector('canvas');
857 canvasWithSampling = chartWithSampling.content().querySelector('canvas');
859 CanvasTest.expectCanvasesMatch(canvasWithSampling, canvasWithoutSampling);
860 expect(CanvasTest.canvasContainsColor(canvasWithoutSampling, {r: 0, g: 128, b: 255})).to.be(true);
861 expect(CanvasTest.canvasContainsColor(canvasWithSampling, {r: 0, g: 128, b: 255})).to.be(true);
863 const diff = sampleCluster.endTime - sampleCluster.startTime;
864 chartWithoutSampling.setDomain(sampleCluster.startTime - 2 * diff, sampleCluster.endTime);
865 chartWithSampling.setDomain(sampleCluster.startTime - 2 * diff, sampleCluster.endTime);
867 CanvasTest.fillCanvasBeforeRedrawCheck(canvasWithoutSampling);
868 CanvasTest.fillCanvasBeforeRedrawCheck(canvasWithSampling);
869 return waitForComponentsToRender(context);
871 expect(CanvasTest.hasCanvasBeenRedrawn(canvasWithoutSampling)).to.be(true);
872 expect(CanvasTest.hasCanvasBeenRedrawn(canvasWithSampling)).to.be(true);
874 expect(CanvasTest.canvasContainsColor(canvasWithoutSampling, {r: 0, g: 128, b: 255})).to.be(true);
875 expect(CanvasTest.canvasContainsColor(canvasWithSampling, {r: 0, g: 128, b: 255})).to.be(true);
877 CanvasTest.expectCanvasesMismatch(canvasWithSampling, canvasWithoutSampling);
882 it('should render annotations', () => {
883 const context = new BrowsingContext();
884 return context.importScripts(scripts, 'ComponentBase', 'TimeSeriesChart', 'InteractiveTimeSeriesChart', 'MeasurementSet', 'MockRemoteAPI').then(() => {
885 const options = {annotations: {
887 textBackground: '#fff',
891 const chartWithoutAnnotations = createChartWithSampleCluster(context, options);
892 const chartWithAnnotations = createChartWithSampleCluster(context, options);
894 chartWithoutAnnotations.setDomain(sampleCluster.startTime, sampleCluster.endTime);
895 chartWithoutAnnotations.fetchMeasurementSets();
896 respondWithSampleCluster(context.symbols.MockRemoteAPI.requests[0]);
898 chartWithAnnotations.setDomain(sampleCluster.startTime, sampleCluster.endTime);
899 chartWithAnnotations.fetchMeasurementSets();
901 let canvasWithAnnotations;
902 return waitForComponentsToRender(context).then(() => {
903 const diff = sampleCluster.endTime - sampleCluster.startTime;
904 chartWithAnnotations.setAnnotations([{
905 startTime: sampleCluster.startTime + diff / 4,
906 endTime: sampleCluster.startTime + diff / 2,
907 label: 'hello, world',
908 fillStyle: 'rgb(0, 0, 255)',
911 canvasWithAnnotations = chartWithAnnotations.content().querySelector('canvas');
912 CanvasTest.fillCanvasBeforeRedrawCheck(canvasWithAnnotations);
913 return waitForComponentsToRender(context);
915 expect(CanvasTest.hasCanvasBeenRedrawn(canvasWithAnnotations)).to.be(true);
917 const canvasWithoutAnnotations = chartWithoutAnnotations.content().querySelector('canvas');
918 CanvasTest.expectCanvasesMismatch(canvasWithAnnotations, canvasWithoutAnnotations);
926 describe('InteractiveTimeSeriesChart', () => {
928 it('should change the unlocked indicator to the point closest to the last mouse move position', () => {
929 const context = new BrowsingContext();
930 return context.importScripts(scripts, 'ComponentBase', 'TimeSeriesChart', 'InteractiveTimeSeriesChart', 'MeasurementSet', 'MockRemoteAPI').then(() => {
931 const chart = createChartWithSampleCluster(context, {}, {interactiveChart: true, interactive: true});
933 chart.setDomain(sampleCluster.startTime, sampleCluster.endTime);
934 chart.fetchMeasurementSets();
935 respondWithSampleCluster(context.symbols.MockRemoteAPI.requests[0]);
937 const indicatorChangeCalls = [];
938 chart.listenToAction('indicatorChange', (...args) => indicatorChangeCalls.push(args));
940 let selectionChangeCount = 0;
941 chart.listenToAction('selectionChange', () => selectionChangeCount++);
944 return waitForComponentsToRender(context).then(() => {
945 expect(chart.currentSelection()).to.be(null);
946 expect(chart.currentIndicator()).to.be(null);
947 expect(indicatorChangeCalls).to.be.eql([]);
949 canvas = chart.content().querySelector('canvas');
950 const rect = canvas.getBoundingClientRect();
951 canvas.dispatchEvent(new MouseEvent('mousemove', {target: canvas, clientX: rect.right - 1, clientY: rect.top + rect.height / 2, composed: true, bubbles: true}));
953 CanvasTest.fillCanvasBeforeRedrawCheck(canvas);
954 return waitForComponentsToRender(context);
956 expect(CanvasTest.hasCanvasBeenRedrawn(canvas)).to.be(true);
958 expect(chart.currentSelection()).to.be(null);
959 const indicator = chart.currentIndicator();
960 expect(indicator).to.not.be(null);
961 const currentView = chart.sampledTimeSeriesData('current');
962 const lastPoint = currentView.lastPoint();
963 expect(indicator.view).to.be(currentView);
964 expect(indicator.point).to.be(lastPoint);
965 expect(indicator.isLocked).to.be(false);
966 expect(indicatorChangeCalls).to.be.eql([[lastPoint.id, false]]);
968 expect(selectionChangeCount).to.be(0);
973 it('should lock the indicator to the point closest to the clicked position', () => {
974 const context = new BrowsingContext();
975 return context.importScripts(scripts, 'ComponentBase', 'TimeSeriesChart', 'InteractiveTimeSeriesChart', 'MeasurementSet', 'MockRemoteAPI').then(() => {
976 const chart = createChartWithSampleCluster(context, {}, {interactiveChart: true, interactive: true});
978 chart.setDomain(sampleCluster.startTime, sampleCluster.endTime);
979 chart.fetchMeasurementSets();
980 respondWithSampleCluster(context.symbols.MockRemoteAPI.requests[0]);
982 const indicatorChangeCalls = [];
983 chart.listenToAction('indicatorChange', (...args) => indicatorChangeCalls.push(args));
985 let selectionChangeCount = 0;
986 chart.listenToAction('selectionChange', () => selectionChangeCount++);
989 return waitForComponentsToRender(context).then(() => {
990 expect(chart.currentSelection()).to.be(null);
991 expect(chart.currentIndicator()).to.be(null);
992 expect(indicatorChangeCalls).to.be.eql([]);
993 canvas = chart.content().querySelector('canvas');
994 const rect = canvas.getBoundingClientRect();
996 const x = rect.right - 1;
997 const y = rect.top + rect.height / 2;
998 canvas.dispatchEvent(new MouseEvent('mousemove', {target: canvas, clientX: x, clientY: y, composed: true, bubbles: true}));
999 canvas.dispatchEvent(new MouseEvent('mousedown', {target: canvas, clientX: x, clientY: y, composed: true, bubbles: true}));
1000 canvas.dispatchEvent(new MouseEvent('mousemove', {target: canvas, clientX: x - 0.5, clientY: y + 0.5, composed: true, bubbles: true}));
1001 canvas.dispatchEvent(new MouseEvent('mouseup', {target: canvas, clientX: x - 0.5, clientY: y + 0.5, composed: true, bubbles: true}));
1002 canvas.dispatchEvent(new MouseEvent('click', {target: canvas, clientX: x - 0.5, clientY: y + 0.5, composed: true, bubbles: true}));
1004 CanvasTest.fillCanvasBeforeRedrawCheck(canvas);
1005 return waitForComponentsToRender(context);
1007 expect(CanvasTest.hasCanvasBeenRedrawn(canvas)).to.be(true);
1009 const currentView = chart.sampledTimeSeriesData('current');
1010 const lastPoint = currentView.lastPoint();
1011 expect(chart.currentSelection()).to.be(null);
1012 const indicator = chart.currentIndicator();
1013 expect(indicator.view).to.be(currentView);
1014 expect(indicator.point).to.be(lastPoint);
1015 expect(indicator.isLocked).to.be(true);
1016 expect(indicatorChangeCalls).to.be.eql([[lastPoint.id, false], [lastPoint.id, true]]);
1018 expect(selectionChangeCount).to.be(0);
1023 it('should clear the unlocked indicator when the mouse cursor exits the chart', () => {
1024 const context = new BrowsingContext();
1025 return context.importScripts(scripts, 'ComponentBase', 'TimeSeriesChart', 'InteractiveTimeSeriesChart', 'MeasurementSet', 'MockRemoteAPI').then(() => {
1026 const chart = createChartWithSampleCluster(context, {}, {interactiveChart: true, interactive: true});
1028 chart.setDomain(sampleCluster.startTime, sampleCluster.endTime);
1029 chart.fetchMeasurementSets();
1030 respondWithSampleCluster(context.symbols.MockRemoteAPI.requests[0]);
1032 const indicatorChangeCalls = [];
1033 chart.listenToAction('indicatorChange', (...args) => indicatorChangeCalls.push(args));
1035 let selectionChangeCount = 0;
1036 chart.listenToAction('selectionChange', () => selectionChangeCount++);
1041 return waitForComponentsToRender(context).then(() => {
1042 expect(chart.currentSelection()).to.be(null);
1043 expect(chart.currentIndicator()).to.be(null);
1044 expect(indicatorChangeCalls).to.be.eql([]);
1046 canvas = chart.content().querySelector('canvas');
1047 rect = canvas.getBoundingClientRect();
1048 canvas.dispatchEvent(new MouseEvent('mousemove', {target: canvas, clientX: rect.right - 1, clientY: rect.top + rect.height / 2, composed: true, bubbles: true}));
1050 CanvasTest.fillCanvasBeforeRedrawCheck(canvas);
1051 return waitForComponentsToRender(context);
1053 expect(CanvasTest.hasCanvasBeenRedrawn(canvas)).to.be(true);
1055 const currentView = chart.sampledTimeSeriesData('current');
1056 lastPoint = currentView.lastPoint();
1057 expect(chart.currentSelection()).to.be(null);
1058 const indicator = chart.currentIndicator();
1059 expect(indicator.view).to.be(currentView);
1060 expect(indicator.point).to.be(lastPoint);
1061 expect(indicator.isLocked).to.be(false);
1062 expect(indicatorChangeCalls).to.be.eql([[lastPoint.id, false]]);
1064 canvas.parentNode.dispatchEvent(new MouseEvent('mousemove', {target: canvas, clientX: rect.right + 50, clientY: rect.bottom + 50, composed: true, bubbles: true}));
1065 canvas.dispatchEvent(new MouseEvent('mouseleave', {target: canvas, clientX: rect.right + 50, clientY: rect.bottom + 50, composed: true, bubbles: true}));
1067 CanvasTest.fillCanvasBeforeRedrawCheck(canvas);
1068 return waitForComponentsToRender(context);
1070 expect(CanvasTest.hasCanvasBeenRedrawn(canvas)).to.be(true);
1072 expect(chart.currentSelection()).to.be(null);
1073 expect(chart.currentIndicator()).to.be(null);
1074 expect(indicatorChangeCalls).to.be.eql([[lastPoint.id, false], [null, false]]);
1076 expect(selectionChangeCount).to.be(0);
1081 it('should not clear the locked indicator when the mouse cursor exits the chart', () => {
1082 const context = new BrowsingContext();
1083 return context.importScripts(scripts, 'ComponentBase', 'TimeSeriesChart', 'InteractiveTimeSeriesChart', 'MeasurementSet', 'MockRemoteAPI').then(() => {
1084 const chart = createChartWithSampleCluster(context, {}, {interactiveChart: true, interactive: true});
1086 chart.setDomain(sampleCluster.startTime, sampleCluster.endTime);
1087 chart.fetchMeasurementSets();
1088 respondWithSampleCluster(context.symbols.MockRemoteAPI.requests[0]);
1090 const indicatorChangeCalls = [];
1091 chart.listenToAction('indicatorChange', (...args) => indicatorChangeCalls.push(args));
1093 let selectionChangeCount = 0;
1094 chart.listenToAction('selectionChange', () => selectionChangeCount++);
1100 return waitForComponentsToRender(context).then(() => {
1101 expect(chart.currentSelection()).to.be(null);
1102 expect(chart.currentIndicator()).to.be(null);
1103 expect(indicatorChangeCalls).to.be.eql([]);
1105 canvas = chart.content().querySelector('canvas');
1106 rect = canvas.getBoundingClientRect();
1107 canvas.dispatchEvent(new MouseEvent('click', {target: canvas, clientX: rect.right - 1, clientY: rect.top + rect.height / 2, composed: true, bubbles: true}));
1109 CanvasTest.fillCanvasBeforeRedrawCheck(canvas);
1110 return waitForComponentsToRender(context);
1112 expect(CanvasTest.hasCanvasBeenRedrawn(canvas)).to.be(true);
1114 currentView = chart.sampledTimeSeriesData('current');
1115 lastPoint = currentView.lastPoint();
1116 expect(chart.currentSelection()).to.be(null);
1117 const indicator = chart.currentIndicator();
1118 expect(indicator.view).to.be(currentView);
1119 expect(indicator.point).to.be(lastPoint);
1120 expect(indicator.isLocked).to.be(true);
1121 expect(indicatorChangeCalls).to.be.eql([[lastPoint.id, true]]);
1123 canvas.parentNode.dispatchEvent(new MouseEvent('mousemove', {target: canvas, clientX: rect.right + 50, clientY: rect.bottom + 50, composed: true, bubbles: true}));
1124 canvas.dispatchEvent(new MouseEvent('mouseleave', {target: canvas, clientX: rect.right + 50, clientY: rect.bottom + 50, composed: true, bubbles: true}));
1126 CanvasTest.fillCanvasBeforeRedrawCheck(canvas);
1127 return waitForComponentsToRender(context);
1129 expect(CanvasTest.hasCanvasBeenRedrawn(canvas)).to.be(false);
1131 expect(chart.currentSelection()).to.be(null);
1132 const indicator = chart.currentIndicator();
1133 expect(indicator.view).to.be(currentView);
1134 expect(indicator.point).to.be(lastPoint);
1135 expect(indicator.isLocked).to.be(true);
1136 expect(indicatorChangeCalls).to.be.eql([[lastPoint.id, true]]);
1138 expect(selectionChangeCount).to.be(0);
1143 it('should clear the locked indicator when clicked', () => {
1144 const context = new BrowsingContext();
1145 return context.importScripts(scripts, 'ComponentBase', 'TimeSeriesChart', 'InteractiveTimeSeriesChart', 'MeasurementSet', 'MockRemoteAPI').then(() => {
1146 const chart = createChartWithSampleCluster(context, {}, {interactiveChart: true, interactive: true});
1148 chart.setDomain(sampleCluster.startTime, sampleCluster.endTime);
1149 chart.fetchMeasurementSets();
1150 respondWithSampleCluster(context.symbols.MockRemoteAPI.requests[0]);
1152 const indicatorChangeCalls = [];
1153 chart.listenToAction('indicatorChange', (...args) => indicatorChangeCalls.push(args));
1155 let selectionChangeCount = 0;
1156 chart.listenToAction('selectionChange', () => selectionChangeCount++);
1163 return waitForComponentsToRender(context).then(() => {
1164 expect(chart.currentSelection()).to.be(null);
1165 expect(chart.currentIndicator()).to.be(null);
1166 expect(indicatorChangeCalls).to.be.eql([]);
1168 canvas = chart.content().querySelector('canvas');
1169 rect = canvas.getBoundingClientRect();
1170 y = rect.top + rect.height / 2;
1171 canvas.dispatchEvent(new MouseEvent('click', {target: canvas, clientX: rect.right - 1, clientY: y, composed: true, bubbles: true}));
1173 CanvasTest.fillCanvasBeforeRedrawCheck(canvas);
1174 return waitForComponentsToRender(context);
1176 expect(CanvasTest.hasCanvasBeenRedrawn(canvas)).to.be(true);
1178 currentView = chart.sampledTimeSeriesData('current');
1179 lastPoint = currentView.lastPoint();
1180 expect(chart.currentSelection()).to.be(null);
1181 const indicator = chart.currentIndicator();
1182 expect(indicator.view).to.be(currentView);
1183 expect(indicator.point).to.be(lastPoint);
1184 expect(indicator.isLocked).to.be(true);
1185 expect(indicatorChangeCalls).to.be.eql([[lastPoint.id, true]]);
1187 canvas.dispatchEvent(new MouseEvent('click', {target: canvas, clientX: rect.left + 1, clientY: y, composed: true, bubbles: true}));
1189 CanvasTest.fillCanvasBeforeRedrawCheck(canvas);
1190 return waitForComponentsToRender(context);
1192 expect(CanvasTest.hasCanvasBeenRedrawn(canvas)).to.be(true);
1194 expect(chart.currentSelection()).to.be(null);
1195 const firstPoint = currentView.firstPoint();
1196 const indicator = chart.currentIndicator();
1197 expect(indicator.view).to.be(currentView);
1198 expect(indicator.point).to.be(firstPoint);
1199 expect(indicator.isLocked).to.be(false);
1200 expect(indicatorChangeCalls).to.be.eql([[lastPoint.id, true], [firstPoint.id, false]]);
1202 expect(selectionChangeCount).to.be(0);
1207 it('should change the selection when the mouse cursor is dragged', () => {
1208 const context = new BrowsingContext();
1209 return context.importScripts(scripts, 'ComponentBase', 'TimeSeriesChart', 'InteractiveTimeSeriesChart', 'MeasurementSet', 'MockRemoteAPI').then(() => {
1210 const chart = createChartWithSampleCluster(context,
1211 {selection: {lineStyle: '#f93', lineWidth: 2, fillStyle: '#ccc'}},
1212 {interactiveChart: true, interactive: true});
1214 chart.setDomain(sampleCluster.startTime, sampleCluster.endTime);
1215 chart.fetchMeasurementSets();
1216 respondWithSampleCluster(context.symbols.MockRemoteAPI.requests[0]);
1218 const indicatorChangeCalls = [];
1219 chart.listenToAction('indicatorChange', (...args) => indicatorChangeCalls.push(args));
1221 const selectionChangeCalls = [];
1222 chart.listenToAction('selectionChange', (...args) => selectionChangeCalls.push(args));
1224 const zoomButton = chart.content('zoom-button');
1233 return waitForComponentsToRender(context).then(() => {
1234 expect(chart.currentSelection()).to.be(null);
1235 expect(chart.currentIndicator()).to.be(null);
1236 expect(selectionChangeCalls).to.be.eql([]);
1238 canvas = chart.content().querySelector('canvas');
1239 rect = canvas.getBoundingClientRect();
1240 y = rect.top + rect.height / 2;
1241 canvas.dispatchEvent(new MouseEvent('mousemove', {target: canvas, clientX: rect.left + 5, clientY: y, composed: true, bubbles: true}));
1243 CanvasTest.fillCanvasBeforeRedrawCheck(canvas);
1244 return waitForComponentsToRender(context);
1246 expect(CanvasTest.hasCanvasBeenRedrawn(canvas)).to.be(true);
1248 currentView = chart.sampledTimeSeriesData('current');
1249 firstPoint = currentView.firstPoint();
1250 expect(chart.currentSelection()).to.be(null);
1251 let indicator = chart.currentIndicator();
1252 expect(indicator.view).to.be(currentView);
1253 expect(indicator.point).to.be(firstPoint);
1254 expect(indicator.isLocked).to.be(false);
1255 expect(indicatorChangeCalls).to.be.eql([[firstPoint.id, false]]);
1256 expect(zoomButton.offsetHeight).to.be(0);
1258 canvas.dispatchEvent(new MouseEvent('mousedown', {target: canvas, clientX: rect.left + 5, clientY: y, composed: true, bubbles: true}));
1260 CanvasTest.fillCanvasBeforeRedrawCheck(canvas);
1261 return waitForComponentsToRender(context);
1263 expect(CanvasTest.hasCanvasBeenRedrawn(canvas)).to.be(false);
1265 expect(chart.currentSelection()).to.be(null);
1266 let indicator = chart.currentIndicator();
1267 expect(indicator.view).to.be(currentView);
1268 expect(indicator.point).to.be(firstPoint);
1269 expect(indicator.isLocked).to.be(false);
1270 expect(selectionChangeCalls).to.be.eql([]);
1271 expect(indicatorChangeCalls).to.be.eql([[firstPoint.id, false]]);
1272 expect(zoomButton.offsetHeight).to.be(0);
1274 canvas.dispatchEvent(new MouseEvent('mousemove', {target: canvas, clientX: rect.left + 15, clientY: y + 5, composed: true, bubbles: true}));
1276 CanvasTest.fillCanvasBeforeRedrawCheck(canvas);
1277 return waitForComponentsToRender(context);
1279 expect(CanvasTest.hasCanvasBeenRedrawn(canvas)).to.be(true);
1281 expect(chart.currentSelection()).to.not.be(null);
1282 expect(chart.currentIndicator()).to.be(null);
1283 expect(selectionChangeCalls.length).to.be(1);
1284 oldRange = selectionChangeCalls[0][0];
1285 expect(oldRange).to.be.eql(chart.currentSelection());
1286 expect(selectionChangeCalls[0][1]).to.be(false);
1287 expect(indicatorChangeCalls).to.be.eql([[firstPoint.id, false], [null, false]]);
1288 expect(zoomButton.offsetHeight).to.be(0);
1290 canvas.dispatchEvent(new MouseEvent('mousemove', {target: canvas, clientX: rect.right - 5, clientY: y + 5, composed: true, bubbles: true}));
1292 CanvasTest.fillCanvasBeforeRedrawCheck(canvas);
1293 return waitForComponentsToRender(context);
1295 expect(CanvasTest.hasCanvasBeenRedrawn(canvas)).to.be(true);
1297 expect(chart.currentSelection()).to.not.be(null);
1298 expect(chart.currentIndicator()).to.be(null);
1299 expect(selectionChangeCalls.length).to.be(2);
1300 newRange = selectionChangeCalls[1][0];
1301 expect(newRange).to.be.eql(chart.currentSelection());
1302 expect(newRange[0]).to.be(oldRange[0]);
1303 expect(newRange[1]).to.be.greaterThan(oldRange[1]);
1304 expect(selectionChangeCalls[1][1]).to.be(false);
1305 expect(zoomButton.offsetHeight).to.be(0);
1307 canvas.dispatchEvent(new MouseEvent('mouseup', {target: canvas, clientX: rect.right - 5, clientY: y + 5, composed: true, bubbles: true}));
1308 canvas.dispatchEvent(new MouseEvent('click', {target: canvas, clientX: rect.right - 5, clientY: y + 5, composed: true, bubbles: true}));
1310 CanvasTest.fillCanvasBeforeRedrawCheck(canvas);
1311 return waitForComponentsToRender(context);
1313 expect(CanvasTest.hasCanvasBeenRedrawn(canvas)).to.be(true);
1315 expect(chart.currentSelection()).to.be.eql(newRange);
1316 expect(chart.currentIndicator()).to.be(null);
1317 expect(selectionChangeCalls.length).to.be(3);
1318 expect(selectionChangeCalls[2][0]).to.be.eql(newRange);
1319 expect(selectionChangeCalls[2][1]).to.be(true);
1320 expect(zoomButton.offsetHeight).to.be(0);
1325 it('should dispatch the "zoom" action when the zoom button is clicked', () => {
1326 const context = new BrowsingContext();
1327 return context.importScripts(scripts, 'ComponentBase', 'TimeSeriesChart', 'InteractiveTimeSeriesChart', 'MeasurementSet', 'MockRemoteAPI').then(() => {
1328 const chart = createChartWithSampleCluster(context,
1329 {selection: {lineStyle: '#f93', lineWidth: 2, fillStyle: '#ccc'}, zoomButton: true},
1330 {interactiveChart: true, interactive: true});
1332 chart.setDomain(sampleCluster.startTime, sampleCluster.endTime);
1333 chart.fetchMeasurementSets();
1334 respondWithSampleCluster(context.symbols.MockRemoteAPI.requests[0]);
1336 const zoomCalls = [];
1337 chart.listenToAction('zoom', (...args) => zoomCalls.push(args));
1338 const zoomButton = chart.content('zoom-button');
1342 return waitForComponentsToRender(context).then(() => {
1343 expect(zoomButton.offsetHeight).to.be(0);
1344 canvas = chart.content().querySelector('canvas');
1345 const rect = canvas.getBoundingClientRect();
1346 const y = rect.top + rect.height / 2;
1347 canvas.dispatchEvent(new MouseEvent('mousemove', {target: canvas, clientX: rect.left + 5, clientY: y, composed: true, bubbles: true}));
1348 canvas.dispatchEvent(new MouseEvent('mousedown', {target: canvas, clientX: rect.left + 5, clientY: y, composed: true, bubbles: true}));
1349 canvas.dispatchEvent(new MouseEvent('mousemove', {target: canvas, clientX: rect.right - 10, clientY: y + 5, composed: true, bubbles: true}));
1350 canvas.dispatchEvent(new MouseEvent('mouseup', {target: canvas, clientX: rect.right - 10, clientY: y + 5, composed: true, bubbles: true}));
1352 CanvasTest.fillCanvasBeforeRedrawCheck(canvas);
1353 return waitForComponentsToRender(context);
1355 expect(CanvasTest.hasCanvasBeenRedrawn(canvas)).to.be(true);
1357 selection = chart.currentSelection();
1358 expect(selection).to.not.be(null);
1359 expect(chart.currentIndicator()).to.be(null);
1360 expect(zoomButton.offsetHeight).to.not.be(0);
1361 expect(zoomCalls).to.be.eql([]);
1364 expect(zoomCalls).to.be.eql([[selection]]);
1366 CanvasTest.fillCanvasBeforeRedrawCheck(canvas);
1367 return waitForComponentsToRender(context);
1369 expect(CanvasTest.hasCanvasBeenRedrawn(canvas)).to.be(false);
1374 it('should clear the selection when clicked', () => {
1375 const context = new BrowsingContext();
1376 return context.importScripts(scripts, 'ComponentBase', 'TimeSeriesChart', 'InteractiveTimeSeriesChart', 'MeasurementSet', 'MockRemoteAPI').then(() => {
1377 const chart = createChartWithSampleCluster(context,
1378 {selection: {lineStyle: '#f93', lineWidth: 2, fillStyle: '#ccc'}},
1379 {interactiveChart: true, interactive: true});
1381 chart.setDomain(sampleCluster.startTime, sampleCluster.endTime);
1382 chart.fetchMeasurementSets();
1383 respondWithSampleCluster(context.symbols.MockRemoteAPI.requests[0]);
1388 return waitForComponentsToRender(context).then(() => {
1389 canvas = chart.content().querySelector('canvas');
1390 rect = canvas.getBoundingClientRect();
1391 y = rect.top + rect.height / 2;
1392 canvas.dispatchEvent(new MouseEvent('mousemove', {target: canvas, clientX: rect.left + 5, clientY: y, composed: true, bubbles: true}));
1393 canvas.dispatchEvent(new MouseEvent('mousedown', {target: canvas, clientX: rect.left + 5, clientY: y, composed: true, bubbles: true}));
1394 canvas.dispatchEvent(new MouseEvent('mousemove', {target: canvas, clientX: rect.right - 10, clientY: y + 5, composed: true, bubbles: true}));
1395 canvas.dispatchEvent(new MouseEvent('mouseup', {target: canvas, clientX: rect.right - 10, clientY: y + 5, composed: true, bubbles: true}));
1396 canvas.dispatchEvent(new MouseEvent('click', {target: canvas, clientX: rect.right - 10, clientY: y + 5, composed: true, bubbles: true}));
1398 CanvasTest.fillCanvasBeforeRedrawCheck(canvas);
1399 return waitForComponentsToRender(context);
1401 expect(CanvasTest.hasCanvasBeenRedrawn(canvas)).to.be(true);
1403 expect(chart.currentSelection()).to.not.be(null);
1404 expect(chart.currentIndicator()).to.be(null);
1406 canvas.dispatchEvent(new MouseEvent('click', {target: canvas, clientX: rect.left + 1, clientY: y + 5, composed: true, bubbles: true}));
1408 CanvasTest.fillCanvasBeforeRedrawCheck(canvas);
1409 return waitForComponentsToRender(context);
1411 expect(CanvasTest.hasCanvasBeenRedrawn(canvas)).to.be(true);
1413 expect(chart.currentSelection()).to.be(null);
1414 const currentView = chart.sampledTimeSeriesData('current');
1415 const indicator = chart.currentIndicator();
1416 expect(indicator.view).to.be(currentView);
1417 expect(indicator.point).to.be(currentView.firstPoint());
1418 expect(indicator.isLocked).to.be(false);
1423 it('should dispatch "annotationClick" action when an annotation is clicked', () => {
1424 const context = new BrowsingContext();
1425 return context.importScripts(scripts, 'ComponentBase', 'TimeSeriesChart', 'InteractiveTimeSeriesChart', 'MeasurementSet', 'MockRemoteAPI').then(() => {
1426 const options = {annotations: {
1428 textBackground: '#fff',
1432 const chart = createChartWithSampleCluster(context, options, {interactiveChart: true});
1434 chart.setDomain(sampleCluster.startTime, sampleCluster.endTime);
1435 chart.fetchMeasurementSets();
1436 respondWithSampleCluster(context.symbols.MockRemoteAPI.requests[0]);
1438 const diff = sampleCluster.endTime - sampleCluster.startTime;
1439 const annotations = [{
1440 startTime: sampleCluster.startTime + diff / 2,
1441 endTime: sampleCluster.endTime - diff / 4,
1442 label: 'hello, world',
1443 fillStyle: 'rgb(0, 0, 255)',
1445 chart.setAnnotations(annotations);
1447 const annotationClickCalls = [];
1448 chart.listenToAction('annotationClick', (...args) => annotationClickCalls.push(args));
1452 return waitForComponentsToRender(context).then(() => {
1453 expect(annotationClickCalls).to.be.eql([]);
1454 expect(chart.content('annotation-label').textContent).to.not.contain('hello, world');
1456 canvas = chart.content().querySelector('canvas');
1457 const rect = canvas.getBoundingClientRect();
1458 init = {target: canvas, clientX: rect.right - rect.width / 4, clientY: rect.bottom - 5, composed: true, bubbles: true};
1459 canvas.dispatchEvent(new MouseEvent('mousemove', init));
1461 CanvasTest.fillCanvasBeforeRedrawCheck(canvas);
1462 return waitForComponentsToRender(context);
1464 expect(CanvasTest.hasCanvasBeenRedrawn(canvas)).to.be(true);
1466 expect(chart.content('annotation-label').textContent).to.contain('hello, world');
1467 expect(annotationClickCalls).to.be.eql([]);
1468 canvas.dispatchEvent(new MouseEvent('mousedown', init));
1469 canvas.dispatchEvent(new MouseEvent('mouseup', init));
1470 canvas.dispatchEvent(new MouseEvent('click', init));
1472 expect(annotationClickCalls).to.be.eql([[annotations[0]]]);
1474 CanvasTest.fillCanvasBeforeRedrawCheck(canvas);
1475 return waitForComponentsToRender(context);
1477 expect(CanvasTest.hasCanvasBeenRedrawn(canvas)).to.be(false);