@@ -303,3 +303,115 @@ def test_webagg_toolbar_pan(random_port, page):
303
303
# Contracts in y-direction.
304
304
assert ax .viewLim .y1 == orig_lim .y1
305
305
assert ax .viewLim .y0 < orig_lim .y0 - orig_lim .height / 2
306
+
307
+
308
+ @pytest .mark .backend ('webagg' )
309
+ def test_webagg_toolbar_zoom (random_port , page ):
310
+ from playwright .sync_api import expect
311
+
312
+ fig , ax = plt .subplots (facecolor = 'w' )
313
+ ax .plot ([3 , 2 , 1 ])
314
+ orig_lim = ax .viewLim .frozen ()
315
+ # Make figure coords ~= axes coords, with ticks visible for inspection.
316
+ ax .set_position ([0 , 0 , 1 , 1 ])
317
+ ax .tick_params (axis = 'y' , direction = 'in' , pad = - 22 )
318
+ ax .tick_params (axis = 'x' , direction = 'in' , pad = - 15 )
319
+
320
+ # Don't start the Tornado event loop, but use the existing event loop
321
+ # started by the `page` fixture.
322
+ WebAggApplication .initialize ()
323
+ WebAggApplication .started = True
324
+
325
+ page .goto (f'http://{ WebAggApplication .address } :{ WebAggApplication .port } /' )
326
+
327
+ canvas = page .locator ('canvas.mpl-canvas' )
328
+ expect (canvas ).to_be_visible ()
329
+ home = page .locator ('button.mpl-widget' ).nth (0 )
330
+ expect (home ).to_be_visible ()
331
+ pan = page .locator ('button.mpl-widget' ).nth (3 )
332
+ expect (pan ).to_be_visible ()
333
+ zoom = page .locator ('button.mpl-widget' ).nth (4 )
334
+ expect (zoom ).to_be_visible ()
335
+
336
+ active_re = re .compile (r'active' )
337
+ expect (pan ).not_to_have_class (active_re )
338
+ expect (zoom ).not_to_have_class (active_re )
339
+ assert ax .get_navigate_mode () is None
340
+ zoom .click ()
341
+ expect (pan ).not_to_have_class (active_re )
342
+ expect (zoom ).to_have_class (active_re )
343
+ assert ax .get_navigate_mode () == 'ZOOM'
344
+
345
+ # Zoom 25% in on each side.
346
+ bbox = canvas .bounding_box ()
347
+ x , y = bbox ['x' ] + bbox ['width' ] / 4 , bbox ['y' ] + bbox ['height' ] / 4
348
+ page .mouse .move (x , y )
349
+ page .mouse .down ()
350
+ page .mouse .move (x + bbox ['width' ] / 2 , y + bbox ['height' ] / 2 ,
351
+ steps = 20 )
352
+ page .mouse .up ()
353
+
354
+ assert ax .get_xlim () == (orig_lim .x0 + orig_lim .width / 4 ,
355
+ orig_lim .x1 - orig_lim .width / 4 )
356
+ assert ax .get_ylim () == (orig_lim .y0 + orig_lim .height / 4 ,
357
+ orig_lim .y1 - orig_lim .height / 4 )
358
+
359
+ # Reset.
360
+ home .click ()
361
+
362
+ # Zoom 25% in on each side, while holding 'x' key, to constrain the zoom
363
+ # horizontally..
364
+ bbox = canvas .bounding_box ()
365
+ x , y = bbox ['x' ] + bbox ['width' ] / 4 , bbox ['y' ] + bbox ['height' ] / 4
366
+ page .mouse .move (x , y )
367
+ page .mouse .down ()
368
+ page .keyboard .down ('x' )
369
+ page .mouse .move (x + bbox ['width' ] / 2 , y + bbox ['height' ] / 2 ,
370
+ steps = 20 )
371
+ page .mouse .up ()
372
+ page .keyboard .up ('x' )
373
+
374
+ assert ax .get_xlim () == (orig_lim .x0 + orig_lim .width / 4 ,
375
+ orig_lim .x1 - orig_lim .width / 4 )
376
+ assert ax .get_ylim () == (orig_lim .y0 , orig_lim .y1 )
377
+
378
+ # Reset.
379
+ home .click ()
380
+
381
+ # Zoom 25% in on each side, while holding 'y' key, to constrain the zoom
382
+ # vertically.
383
+ bbox = canvas .bounding_box ()
384
+ x , y = bbox ['x' ] + bbox ['width' ] / 4 , bbox ['y' ] + bbox ['height' ] / 4
385
+ page .mouse .move (x , y )
386
+ page .mouse .down ()
387
+ page .keyboard .down ('y' )
388
+ page .mouse .move (x + bbox ['width' ] / 2 , y + bbox ['height' ] / 2 ,
389
+ steps = 20 )
390
+ page .mouse .up ()
391
+ page .keyboard .up ('y' )
392
+
393
+ assert ax .get_xlim () == (orig_lim .x0 , orig_lim .x1 )
394
+ assert ax .get_ylim () == (orig_lim .y0 + orig_lim .height / 4 ,
395
+ orig_lim .y1 - orig_lim .height / 4 )
396
+
397
+ # Reset.
398
+ home .click ()
399
+
400
+ # Zoom 25% out on each side.
401
+ bbox = canvas .bounding_box ()
402
+ x , y = bbox ['x' ] + bbox ['width' ] / 4 , bbox ['y' ] + bbox ['height' ] / 4
403
+ page .mouse .move (x , y )
404
+ page .mouse .down (button = 'right' )
405
+ page .mouse .move (x + bbox ['width' ] / 2 , y + bbox ['height' ] / 2 ,
406
+ steps = 20 )
407
+ page .mouse .up (button = 'right' )
408
+
409
+ # Limits were doubled, but based on the central point.
410
+ cx = orig_lim .x0 + orig_lim .width / 2
411
+ x0 = cx - orig_lim .width
412
+ x1 = cx + orig_lim .width
413
+ assert ax .get_xlim () == (x0 , x1 )
414
+ cy = orig_lim .y0 + orig_lim .height / 2
415
+ y0 = cy - orig_lim .height
416
+ y1 = cy + orig_lim .height
417
+ assert ax .get_ylim () == (y0 , y1 )
0 commit comments