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