Merge branch 'master' of https://github.com/danvk/dygraphs into no-width-no-work
[dygraphs.git] / auto_tests / tests / callback.js
1 /**
2 * @fileoverview Test cases for the callbacks.
3 *
4 * @author uemit.seren@gmail.com (Ümit Seren)
5 */
6
7 var CallbackTestCase = TestCase("callback");
8
9 CallbackTestCase.prototype.setUp = function() {
10 document.body.innerHTML = "<div id='graph'></div><div id='selection'></div>";
11 this.styleSheet = document.createElement("style");
12 this.styleSheet.type = "text/css";
13 document.getElementsByTagName("head")[0].appendChild(this.styleSheet);
14 };
15
16 CallbackTestCase.prototype.tearDown = function() {
17 };
18
19 var data = "X,a\,b,c\n" +
20 "10,-1,1,2\n" +
21 "11,0,3,1\n" +
22 "12,1,4,2\n" +
23 "13,0,2,3\n";
24
25
26 /**
27 * This tests that when the function idxToRow_ returns the proper row and the onHiglightCallback
28 * is properly called when the first series is hidden (setVisibility = false)
29 *
30 */
31 CallbackTestCase.prototype.testHighlightCallbackIsCalled = function() {
32 var h_row;
33 var h_pts;
34
35 var highlightCallback = function(e, x, pts, row) {
36 h_row = row;
37 h_pts = pts;
38 };
39
40 var graph = document.getElementById("graph");
41 var g = new Dygraph(graph, data,
42 {
43 width: 100,
44 height: 100,
45 visibility: [false, true, true],
46 highlightCallback: highlightCallback
47 });
48
49 DygraphOps.dispatchMouseMove(g, 13, 10);
50
51 //check correct row is returned
52 assertEquals(3, h_row);
53 //check there are only two points (because first series is hidden)
54 assertEquals(2, h_pts.length);
55 };
56
57
58 /**
59 * Test that drawPointCallback isn't called when drawPoints is false
60 */
61 CallbackTestCase.prototype.testDrawPointCallback_disabled = function() {
62 var called = false;
63
64 var callback = function() {
65 called = true;
66 };
67
68 var graph = document.getElementById("graph");
69 var g = new Dygraph(graph, data, {
70 drawPointCallback : callback,
71 });
72
73 assertFalse(called);
74 };
75
76 /**
77 * Test that drawPointCallback is called when drawPoints is true
78 */
79 CallbackTestCase.prototype.testDrawPointCallback_enabled = function() {
80 var called = false;
81
82 var callback = function() {
83 called = true;
84 };
85
86 var graph = document.getElementById("graph");
87 var g = new Dygraph(graph, data, {
88 drawPoints : true,
89 drawPointCallback : callback
90 });
91
92 assertTrue(called);
93 };
94
95 /**
96 * Test that drawPointCallback is called when drawPoints is true
97 */
98 CallbackTestCase.prototype.testDrawPointCallback_pointSize = function() {
99 var pointSize = 0;
100 var count = 0;
101
102 var callback = function(g, seriesName, canvasContext, cx, cy, color, pointSizeParam) {
103 pointSize = pointSizeParam;
104 count++;
105 };
106
107 var graph = document.getElementById("graph");
108 var g = new Dygraph(graph, data, {
109 drawPoints : true,
110 drawPointCallback : callback
111 });
112
113 assertEquals(1.5, pointSize);
114 assertEquals(12, count); // one call per data point.
115
116 var g = new Dygraph(graph, data, {
117 drawPoints : true,
118 drawPointCallback : callback,
119 pointSize : 8
120 });
121
122 assertEquals(8, pointSize);
123 };
124
125 /**
126 * Test that drawPointCallback is called for isolated points when
127 * drawPoints is false, and also for gap points if that's enabled.
128 */
129 CallbackTestCase.prototype.testDrawPointCallback_isolated = function() {
130 var xvalues = [];
131
132 var g;
133 var callback = function(g, seriesName, canvasContext, cx, cy, color, pointSizeParam) {
134 var dx = g.toDataXCoord(cx);
135 xvalues.push(dx);
136 Dygraph.Circles.DEFAULT.apply(this, arguments);
137 };
138
139 var graph = document.getElementById("graph");
140 var testdata = [[10, 2], [11, 3], [12, NaN], [13, 2], [14, NaN], [15, 3]];
141 var graphOpts = {
142 labels: ['X', 'Y'],
143 valueRange: [0, 4],
144 drawPoints : false,
145 drawPointCallback : callback,
146 pointSize : 8
147 };
148
149 // Test that isolated points get drawn
150 g = new Dygraph(graph, testdata, graphOpts);
151 assertEquals(2, xvalues.length);
152 assertEquals(13, xvalues[0]);
153 assertEquals(15, xvalues[1]);
154
155 // Test that isolated points + gap points get drawn when
156 // drawGapEdgePoints is set. This should add one point at the right
157 // edge of the segment at x=11, but not at the graph edge at x=10.
158 xvalues = []; // Reset for new test
159 graphOpts.drawGapEdgePoints = true;
160 g = new Dygraph(graph, testdata, graphOpts);
161 assertEquals(3, xvalues.length);
162 assertEquals(11, xvalues[0]);
163 assertEquals(13, xvalues[1]);
164 assertEquals(15, xvalues[2]);
165 };
166
167 /**
168 * This tests that when the function idxToRow_ returns the proper row and the onHiglightCallback
169 * is properly called when the first series is hidden (setVisibility = false)
170 *
171 */
172 CallbackTestCase.prototype.testDrawHighlightPointCallbackIsCalled = function() {
173 var called = false;
174
175 var drawHighlightPointCallback = function() {
176 called = true;
177 };
178
179 var graph = document.getElementById("graph");
180 var g = new Dygraph(graph, data,
181 {
182 width: 100,
183 height : 100,
184 drawHighlightPointCallback : drawHighlightPointCallback
185 });
186
187 assertFalse(called);
188 DygraphOps.dispatchMouseMove(g, 13, 10);
189 assertTrue(called);
190 };
191
192 /**
193 * Test the closest-series highlighting methods for normal and stacked modes.
194 * Also pass in line widths for plain and highlighted lines for easier visual
195 * confirmation that the highlighted line is drawn on top of the others.
196 */
197 var runClosestTest = function(isStacked, widthNormal, widthHighlighted) {
198 var h_row;
199 var h_pts;
200 var h_series;
201
202 var graph = document.getElementById("graph");
203 var g = new Dygraph(graph, data,
204 {
205 width: 600,
206 height: 400,
207 visibility: [false, true, true],
208 stackedGraph: isStacked,
209 strokeWidth: widthNormal,
210 strokeBorderWidth: 2,
211 highlightCircleSize: widthNormal * 2,
212 highlightSeriesBackgroundAlpha: 0.3,
213
214 highlightSeriesOpts: {
215 strokeWidth: widthHighlighted,
216 highlightCircleSize: widthHighlighted * 2
217 }
218 });
219
220 var highlightCallback = function(e, x, pts, row, set) {
221 h_row = row;
222 h_pts = pts;
223 h_series = set;
224 document.getElementById('selection').innerHTML='row=' + row + ', set=' + set;
225 };
226
227 g.updateOptions({highlightCallback: highlightCallback}, true);
228
229 if (isStacked) {
230 DygraphOps.dispatchMouseMove(g, 11.45, 1.4);
231 assertEquals(1, h_row);
232 assertEquals('c', h_series);
233
234 //now move up in the same row
235 DygraphOps.dispatchMouseMove(g, 11.45, 1.5);
236 assertEquals(1, h_row);
237 assertEquals('b', h_series);
238
239 //and a bit to the right
240 DygraphOps.dispatchMouseMove(g, 11.55, 1.5);
241 assertEquals(2, h_row);
242 assertEquals('c', h_series);
243 } else {
244 DygraphOps.dispatchMouseMove(g, 11, 1.5);
245 assertEquals(1, h_row);
246 assertEquals('c', h_series);
247
248 //now move up in the same row
249 DygraphOps.dispatchMouseMove(g, 11, 2.5);
250 assertEquals(1, h_row);
251 assertEquals('b', h_series);
252 }
253
254 return g;
255 };
256
257 /**
258 * Test basic closest-point highlighting.
259 */
260 CallbackTestCase.prototype.testClosestPointCallback = function() {
261 runClosestTest(false, 1, 3);
262 }
263
264 /**
265 * Test setSelection() with series name
266 */
267 CallbackTestCase.prototype.testSetSelection = function() {
268 var g = runClosestTest(false, 1, 3);
269 assertEquals(1, g.attr_('strokeWidth', 'c'));
270 g.setSelection(false, 'c');
271 assertEquals(3, g.attr_('strokeWidth', 'c'));
272 }
273
274 /**
275 * Test closest-point highlighting for stacked graph
276 */
277 CallbackTestCase.prototype.testClosestPointStackedCallback = function() {
278 runClosestTest(true, 1, 3);
279 }
280
281 /**
282 * Closest-point highlighting with legend CSS - border around active series.
283 */
284 CallbackTestCase.prototype.testClosestPointCallbackCss1 = function() {
285 var css = "div.dygraph-legend > span { display: block; }\n" +
286 "div.dygraph-legend > span.highlight { border: 1px solid grey; }\n";
287 this.styleSheet.innerHTML = css;
288 runClosestTest(false, 2, 4);
289 this.styleSheet.innerHTML = '';
290 }
291
292 /**
293 * Closest-point highlighting with legend CSS - show only closest series.
294 */
295 CallbackTestCase.prototype.testClosestPointCallbackCss2 = function() {
296 var css = "div.dygraph-legend > span { display: none; }\n" +
297 "div.dygraph-legend > span.highlight { display: inline; }\n";
298 this.styleSheet.innerHTML = css;
299 runClosestTest(false, 10, 15);
300 this.styleSheet.innerHTML = '';
301 // TODO(klausw): verify that the highlighted line is drawn on top?
302 }
303
304 /**
305 * Closest-point highlighting with locked series.
306 */
307 CallbackTestCase.prototype.testSetSelectionLocking = function() {
308 var g = runClosestTest(false, 2, 4);
309
310 // Default behavior, 'b' is closest
311 DygraphOps.dispatchMouseMove(g, 11, 4);
312 assertEquals('b', g.getHighlightSeries());
313
314 // Now lock selection to 'c'
315 g.setSelection(false, 'c', true);
316 DygraphOps.dispatchMouseMove(g, 11, 4);
317 assertEquals('c', g.getHighlightSeries());
318
319 // Unlock, should be back to 'b'
320 g.clearSelection();
321 DygraphOps.dispatchMouseMove(g, 11, 4);
322 assertEquals('b', g.getHighlightSeries());
323 }
324
325 /**
326 * This tests that closest point searches work for data containing NaNs.
327 *
328 * It's intended to catch a regression where a NaN Y value confuses the
329 * closest-point algorithm, treating it as closer as any previous point.
330 */
331 CallbackTestCase.prototype.testNaNData = function() {
332 var dataNaN = [
333 [9, -1, NaN, NaN],
334 [10, -1, 1, 2],
335 [11, 0, 3, 1],
336 [12, 1, 4, NaN],
337 [13, 0, 2, 3],
338 [14, -1, 1, 4]];
339
340 var h_row;
341 var h_pts;
342
343 var highlightCallback = function(e, x, pts, row) {
344 h_row = row;
345 h_pts = pts;
346 };
347
348 var graph = document.getElementById("graph");
349 var g = new Dygraph(graph, dataNaN,
350 {
351 width: 600,
352 height: 400,
353 labels: ['x', 'a', 'b', 'c'],
354 visibility: [false, true, true],
355 highlightCallback: highlightCallback
356 });
357
358 DygraphOps.dispatchMouseMove(g, 10.1, 0.9);
359 //check correct row is returned
360 assertEquals(1, h_row);
361
362 // Explicitly test closest point algorithms
363 var dom = g.toDomCoords(10.1, 0.9);
364 assertEquals(1, g.findClosestRow(dom[0]));
365
366 var res = g.findClosestPoint(dom[0], dom[1]);
367 assertEquals(1, res.row);
368 assertEquals('b', res.seriesName);
369
370 res = g.findStackedPoint(dom[0], dom[1]);
371 assertEquals(1, res.row);
372 assertEquals('c', res.seriesName);
373 };
374
375 /**
376 * This tests that stacked point searches work for data containing NaNs.
377 */
378 CallbackTestCase.prototype.testNaNDataStack = function() {
379 var dataNaN = [
380 [9, -1, NaN, NaN],
381 [10, -1, 1, 2],
382 [11, 0, 3, 1],
383 [12, 1, NaN, 2],
384 [13, 0, 2, 3],
385 [14, -1, 1, 4],
386 [15, 0, 2, NaN],
387 [16, 1, 1, 3],
388 [17, 1, NaN, 3],
389 [18, 0, 2, 5],
390 [19, 0, 1, 4]];
391
392 var h_row;
393 var h_pts;
394
395 var highlightCallback = function(e, x, pts, row) {
396 h_row = row;
397 h_pts = pts;
398 };
399
400 var graph = document.getElementById("graph");
401 var g = new Dygraph(graph, dataNaN,
402 {
403 width: 600,
404 height: 400,
405 labels: ['x', 'a', 'b', 'c'],
406 visibility: [false, true, true],
407 stackedGraph: true,
408 highlightCallback: highlightCallback
409 });
410
411 DygraphOps.dispatchMouseMove(g, 10.1, 0.9);
412 //check correct row is returned
413 assertEquals(1, h_row);
414
415 // Explicitly test stacked point algorithm.
416 var dom = g.toDomCoords(10.1, 0.9);
417 var res = g.findStackedPoint(dom[0], dom[1]);
418 assertEquals(1, res.row);
419 assertEquals('c', res.seriesName);
420
421 // First gap, no data due to NaN contagion.
422 dom = g.toDomCoords(12.1, 0.9);
423 res = g.findStackedPoint(dom[0], dom[1]);
424 assertEquals(3, res.row);
425 assertEquals(undefined, res.seriesName);
426
427 // Second gap, no data due to NaN contagion.
428 dom = g.toDomCoords(15.1, 0.9);
429 res = g.findStackedPoint(dom[0], dom[1]);
430 assertEquals(6, res.row);
431 assertEquals(undefined, res.seriesName);
432
433 // Isolated points should work, finding series b in this case.
434 dom = g.toDomCoords(15.9, 3.1);
435 res = g.findStackedPoint(dom[0], dom[1]);
436 assertEquals(7, res.row);
437 assertEquals('b', res.seriesName);
438 };
439
440 CallbackTestCase.prototype.testGapHighlight = function() {
441 var dataGap = [
442 [1, null, 3],
443 [2, 2, null],
444 [3, null, 5],
445 [4, 4, null],
446 [5, null, 7],
447 [6, NaN, null],
448 [8, 8, null],
449 [10, 10, null]];
450
451 var h_row;
452 var h_pts;
453
454 var highlightCallback = function(e, x, pts, row) {
455 h_row = row;
456 h_pts = pts;
457 };
458
459 var graph = document.getElementById("graph");
460 var g = new Dygraph(graph, dataGap, {
461 width: 400,
462 height: 300,
463 //stackedGraph: true,
464 connectSeparatedPoints: true,
465 drawPoints: true,
466 labels: ['x', 'A', 'B'],
467 highlightCallback : highlightCallback
468 });
469
470 DygraphOps.dispatchMouseMove(g, 1.1, 10);
471 //point from series B
472 assertEquals(0, h_row);
473 assertEquals(1, h_pts.length);
474 assertEquals(3, h_pts[0].yval);
475 assertEquals('B', h_pts[0].name);
476
477 DygraphOps.dispatchMouseMove(g, 6.1, 10);
478 // A is NaN at x=6
479 assertEquals(1, h_pts.length);
480 assert(isNaN(h_pts[0].yval));
481 assertEquals('A', h_pts[0].name);
482
483 DygraphOps.dispatchMouseMove(g, 8.1, 10);
484 //point from series A
485 assertEquals(6, h_row);
486 assertEquals(1, h_pts.length);
487 assertEquals(8, h_pts[0].yval);
488 assertEquals('A', h_pts[0].name);
489 };