Fork off Karma file for in-browser debug
[dygraphs.git] / auto_tests / tests / interaction_model.js
1 /**
2 * @fileoverview Test cases for the interaction model.
3 *
4 * @author konigsberg@google.com (Robert Konigsbrg)
5 */
6 describe("interaction-model", function() {
7
8 beforeEach(function() {
9 document.body.innerHTML = "<div id='graph'></div>";
10 });
11
12 afterEach(function() {
13 });
14
15 var data1 = "X,Y\n" +
16 "20,-1\n" +
17 "21,0\n" +
18 "22,1\n" +
19 "23,0\n";
20
21 var data2 =
22 "X,Y\n" +
23 "1,10\n" +
24 "2,20\n" +
25 "3,30\n" +
26 "4,40\n" +
27 "5,120\n" +
28 "6,50\n" +
29 "7,70\n" +
30 "8,90\n" +
31 "9,50\n";
32
33 function getXLabels() {
34 var x_labels = document.getElementsByClassName("dygraph-axis-label-x");
35 var ary = [];
36 for (var i = 0; i < x_labels.length; i++) {
37 ary.push(x_labels[i].innerHTML);
38 }
39 return ary;
40 }
41
42 /*
43 it('testPan', function() {
44 var originalXRange = g.xAxisRange();
45 var originalYRange = g.yAxisRange(0);
46
47 DygraphOps.dispatchMouseDown(g, xRange[0], yRange[0]);
48 DygraphOps.dispatchMouseMove(g, xRange[1], yRange[0]); // this is really necessary.
49 DygraphOps.dispatchMouseUp(g, xRange[1], yRange[0]);
50
51 assert.closeTo(xRange, g.xAxisRange(), 0.2);
52 // assert.closeTo(originalYRange, g.yAxisRange(0), 0.2); // Not true, it's something in the middle.
53
54 var midX = (xRange[1] - xRange[0]) / 2;
55 DygraphOps.dispatchMouseDown(g, midX, yRange[0]);
56 DygraphOps.dispatchMouseMove(g, midX, yRange[1]); // this is really necessary.
57 DygraphOps.dispatchMouseUp(g, midX, yRange[1]);
58
59 assert.closeTo(xRange, g.xAxisRange(), 0.2);
60 assert.closeTo(yRange, g.yAxisRange(0), 0.2);
61 });
62 */
63
64 /**
65 * This tests that when changing the interaction model so pan is used instead
66 * of zoom as the default behavior, a standard click method is still called.
67 */
68 it('testClickCallbackIsCalled', function() {
69 var clicked;
70
71 var clickCallback = function(event, x) {
72 clicked = x;
73 };
74
75 var graph = document.getElementById("graph");
76 var g = new Dygraph(graph, data1,
77 {
78 width: 100,
79 height : 100,
80 clickCallback : clickCallback
81 });
82
83 DygraphOps.dispatchMouseDown_Point(g, 10, 10);
84 DygraphOps.dispatchMouseMove_Point(g, 10, 10);
85 DygraphOps.dispatchMouseUp_Point(g, 10, 10);
86
87 assert.equal(20, clicked);
88 });
89
90 /**
91 * This tests that when changing the interaction model so pan is used instead
92 * of zoom as the default behavior, a standard click method is still called.
93 */
94 it('testClickCallbackIsCalledOnCustomPan', function() {
95 var clicked;
96
97 var clickCallback = function(event, x) {
98 clicked = x;
99 };
100
101 function customDown(event, g, context) {
102 context.initializeMouseDown(event, g, context);
103 Dygraph.startPan(event, g, context);
104 }
105
106 function customMove(event, g, context) {
107 Dygraph.movePan(event, g, context);
108 }
109
110 function customUp(event, g, context) {
111 Dygraph.endPan(event, g, context);
112 }
113
114 var opts = {
115 width: 100,
116 height : 100,
117 clickCallback : clickCallback,
118 interactionModel : {
119 'mousedown' : customDown,
120 'mousemove' : customMove,
121 'mouseup' : customUp,
122 }
123 };
124
125 var graph = document.getElementById("graph");
126 var g = new Dygraph(graph, data1, opts);
127
128 DygraphOps.dispatchMouseDown_Point(g, 10, 10);
129 DygraphOps.dispatchMouseMove_Point(g, 10, 10);
130 DygraphOps.dispatchMouseUp_Point(g, 10, 10);
131
132 assert.equal(20, clicked);
133 });
134
135 var clickAt = function(g, x, y) {
136 DygraphOps.dispatchMouseDown(g, x, y);
137 DygraphOps.dispatchMouseMove(g, x, y);
138 DygraphOps.dispatchMouseUp(g, x, y);
139 };
140
141 /**
142 * This tests that clickCallback is still called with the nonInteractiveModel.
143 */
144 it('testClickCallbackIsCalledWithNonInteractiveModel', function() {
145 var clicked;
146
147 // TODO(danvk): also test pointClickCallback here.
148 var clickCallback = function(event, x) {
149 clicked = x;
150 };
151
152 var opts = {
153 width: 100,
154 height : 100,
155 clickCallback : clickCallback,
156 interactionModel : Dygraph.Interaction.nonInteractiveModel_
157 };
158
159 var graph = document.getElementById("graph");
160 var g = new Dygraph(graph, data1, opts);
161
162 DygraphOps.dispatchMouseDown_Point(g, 10, 10);
163 DygraphOps.dispatchMouseMove_Point(g, 10, 10);
164 DygraphOps.dispatchMouseUp_Point(g, 10, 10);
165
166 assert.equal(20, clicked);
167 });
168
169 /**
170 * A sanity test to ensure pointClickCallback is called.
171 */
172 it('testPointClickCallback', function() {
173 var clicked = null;
174 var g = new Dygraph('graph', data2, {
175 pointClickCallback: function(event, point) {
176 clicked = point;
177 }
178 });
179
180 clickAt(g, 4, 40);
181
182 assert.isNotNull(clicked);
183 assert.equal(4, clicked.xval);
184 assert.equal(40, clicked.yval);
185 });
186
187 /**
188 * A sanity test to ensure pointClickCallback is not called when out of range.
189 */
190 it('testNoPointClickCallbackWhenOffPoint', function() {
191 var clicked;
192 var g = new Dygraph(document.getElementById("graph"), data2, {
193 pointClickCallback : function(event, point) {
194 clicked = point;
195 }
196 });
197
198 clickAt(g, 5, 40);
199
200 assert.isUndefined(clicked);
201 });
202
203 /**
204 * Ensures pointClickCallback circle size is taken into account.
205 */
206 it('testPointClickCallback_circleSize', function() {
207 // TODO(konigsberg): Implement.
208 });
209
210 /**
211 * Ensures that pointClickCallback is called prior to clickCallback
212 */
213 it('testPointClickCallbackCalledPriorToClickCallback', function() {
214 var counter = 0;
215 var pointClicked;
216 var clicked;
217 var g = new Dygraph(document.getElementById("graph"), data2, {
218 pointClickCallback : function(event, point) {
219 counter++;
220 pointClicked = counter;
221 },
222 clickCallback : function(event, point) {
223 counter++;
224 clicked = counter;
225 }
226 });
227
228 clickAt(g, 4, 40);
229 assert.equal(1, pointClicked);
230 assert.equal(2, clicked);
231 });
232
233 /**
234 * Ensures that when there's no pointClickCallback, clicking on a point still calls
235 * clickCallback
236 */
237 it('testClickCallback_clickOnPoint', function() {
238 var clicked;
239 var g = new Dygraph(document.getElementById("graph"), data2, {
240 clickCallback : function(event, point) {
241 clicked = 1;
242 }
243 });
244
245 clickAt(g, 4, 40);
246 assert.equal(1, clicked);
247 });
248
249 it('testIsZoomed_none', function() {
250 var g = new Dygraph(document.getElementById("graph"), data2, {});
251
252 assert.isFalse(g.isZoomed());
253 assert.isFalse(g.isZoomed("x"));
254 assert.isFalse(g.isZoomed("y"));
255 });
256
257 it('testIsZoomed_x', function() {
258 var g = new Dygraph(document.getElementById("graph"), data2, {});
259
260 DygraphOps.dispatchMouseDown_Point(g, 100, 100);
261 DygraphOps.dispatchMouseMove_Point(g, 130, 100);
262 DygraphOps.dispatchMouseUp_Point(g, 130, 100);
263
264 assert.isTrue(g.isZoomed());
265 assert.isTrue(g.isZoomed("x"));
266 assert.isFalse(g.isZoomed("y"));
267 });
268
269 it('testIsZoomed_y', function() {
270 var g = new Dygraph(document.getElementById("graph"), data2, {});
271
272 DygraphOps.dispatchMouseDown_Point(g, 10, 10);
273 DygraphOps.dispatchMouseMove_Point(g, 10, 30);
274 DygraphOps.dispatchMouseUp_Point(g, 10, 30);
275
276 assert.isTrue(g.isZoomed());
277 assert.isFalse(g.isZoomed("x"));
278 assert.isTrue(g.isZoomed("y"));
279 });
280
281 it('testIsZoomed_both', function() {
282 var g = new Dygraph(document.getElementById("graph"), data2, {});
283
284 // Zoom x axis
285 DygraphOps.dispatchMouseDown_Point(g, 100, 100);
286 DygraphOps.dispatchMouseMove_Point(g, 130, 100);
287 DygraphOps.dispatchMouseUp_Point(g, 130, 100);
288
289 // Now zoom y axis
290 DygraphOps.dispatchMouseDown_Point(g, 100, 100);
291 DygraphOps.dispatchMouseMove_Point(g, 100, 130);
292 DygraphOps.dispatchMouseUp_Point(g, 100, 130);
293
294
295 assert.isTrue(g.isZoomed());
296 assert.isTrue(g.isZoomed("x"));
297 assert.isTrue(g.isZoomed("y"));
298 });
299
300 it('testIsZoomed_updateOptions_none', function() {
301 var g = new Dygraph(document.getElementById("graph"), data2, {});
302
303 g.updateOptions({});
304
305 assert.isFalse(g.isZoomed());
306 assert.isFalse(g.isZoomed("x"));
307 assert.isFalse(g.isZoomed("y"));
308 });
309
310 it('testIsZoomed_updateOptions_x', function() {
311 var g = new Dygraph(document.getElementById("graph"), data2, {});
312
313 g.updateOptions({dateWindow: [-.5, .3]});
314 assert.isTrue(g.isZoomed());
315 assert.isTrue(g.isZoomed("x"));
316 assert.isFalse(g.isZoomed("y"));
317 });
318
319 it('testIsZoomed_updateOptions_y', function() {
320 var g = new Dygraph(document.getElementById("graph"), data2, {});
321
322 g.updateOptions({valueRange: [1, 10]});
323
324 assert.isTrue(g.isZoomed());
325 assert.isFalse(g.isZoomed("x"));
326 assert.isTrue(g.isZoomed("y"));
327 });
328
329 it('testIsZoomed_updateOptions_both', function() {
330 var g = new Dygraph(document.getElementById("graph"), data2, {});
331
332 g.updateOptions({dateWindow: [-1, 1], valueRange: [1, 10]});
333
334 assert.isTrue(g.isZoomed());
335 assert.isTrue(g.isZoomed("x"));
336 assert.isTrue(g.isZoomed("y"));
337 });
338
339
340 it('testCorrectAxisValueRangeAfterUnzoom', function() {
341 var g = new Dygraph(document.getElementById("graph"),
342 data2, {
343 valueRange: [1, 50],
344 dateWindow: [1, 9],
345 animatedZooms:false
346 });
347
348 // Zoom x axis
349 DygraphOps.dispatchMouseDown_Point(g, 100, 100);
350 DygraphOps.dispatchMouseMove_Point(g, 130, 100);
351 DygraphOps.dispatchMouseUp_Point(g, 130, 100);
352
353 // Zoom y axis
354 DygraphOps.dispatchMouseDown_Point(g, 100, 100);
355 DygraphOps.dispatchMouseMove_Point(g, 100, 130);
356 DygraphOps.dispatchMouseUp_Point(g, 100, 130);
357 var currentYAxisRange = g.yAxisRange();
358 var currentXAxisRange = g.xAxisRange();
359
360 //check that the range for the axis has changed
361 assert.notEqual(1, currentXAxisRange[0]);
362 assert.notEqual(10, currentXAxisRange[1]);
363 assert.notEqual(1, currentYAxisRange[0]);
364 assert.notEqual(50, currentYAxisRange[1]);
365
366 // unzoom by doubleclick. This is really the order in which a browser
367 // generates events, and we depend on it.
368 DygraphOps.dispatchMouseDown_Point(g, 10, 10);
369 DygraphOps.dispatchMouseUp_Point(g, 10, 10);
370 DygraphOps.dispatchMouseDown_Point(g, 10, 10);
371 DygraphOps.dispatchMouseUp_Point(g, 10, 10);
372 DygraphOps.dispatchDoubleClick(g, null);
373
374 // check if range for y-axis was reset to original value
375 // TODO check if range for x-axis is correct.
376 // Currently not possible because dateRange is set to null and extremes are returned
377 var newYAxisRange = g.yAxisRange();
378 assert.equal(1, newYAxisRange[0]);
379 assert.equal(50, newYAxisRange[1]);
380 });
381
382 /**
383 * Ensures pointClickCallback is called when some points along the y-axis don't
384 * exist.
385 */
386 it('testPointClickCallback_missingData', function() {
387
388 // There's a B-value at 2, but no A-value.
389 var data =
390 "X,A,B\n" +
391 "1,,100\n"+
392 "2,,110\n"+
393 "3,140,120\n"+
394 "4,130,110\n"+
395 "";
396
397 var clicked;
398 var g = new Dygraph(document.getElementById("graph"), data, {
399 pointClickCallback : function(event, point) {
400 clicked = point;
401 }
402 });
403
404 clickAt(g, 2, 110);
405
406 assert.equal(2, clicked.xval);
407 assert.equal(110, clicked.yval);
408 });
409
410 describe('animated zooms', function() {
411 var oldDuration;
412
413 before(function() {
414 oldDuration = Dygraph.ANIMATION_DURATION;
415 Dygraph.ANIMATION_DURATION = 100; // speed up the animation for testing
416 });
417 after(function() {
418 Dygraph.ANIMATION_DURATION = oldDuration;
419 });
420
421 it('should support animated zooms', function(done) {
422 var data =
423 "X,A,B\n" +
424 "1,120,100\n"+
425 "2,110,110\n"+
426 "3,140,120\n"+
427 "4,130,110\n";
428
429 var ranges = [];
430
431 var g = new Dygraph('graph', data, {
432 animatedZooms: true,
433 });
434
435 // updating the dateWindow does not result in an animation.
436 assert.deepEqual([1, 4], g.xAxisRange());
437 g.updateOptions({dateWindow: [2, 4]});
438 assert.deepEqual([2, 4], g.xAxisRange());
439
440 g.updateOptions({
441 // zoomCallback is called once when the animation is complete.
442 zoomCallback: function(xMin, xMax) {
443 assert.equal(1, xMin);
444 assert.equal(4, xMax);
445 assert.deepEqual([1, 4], g.xAxisRange());
446 done();
447 }
448 }, false);
449
450 // Zoom out -- resetZoom() _does_ produce an animation.
451 g.resetZoom();
452 assert.notDeepEqual([2, 4], g.xAxisRange()); // first frame is synchronous
453 assert.notDeepEqual([1, 4], g.xAxisRange());
454
455 // at this point control flow goes up to zoomCallback
456 });
457
458 });
459
460 });