@@ -319,3 +319,118 @@ def test_webagg_toolbar_pan(random_port, page):
319
319
# Contracts in y-direction.
320
320
assert ax .viewLim .y1 == orig_lim .y1
321
321
assert ax .viewLim .y0 < orig_lim .y0 - orig_lim .height / 2
322
+
323
+
324
+ @pytest .mark .backend ('webagg' )
325
+ def test_webagg_toolbar_zoom (random_port , page ):
326
+ from playwright .sync_api import expect
327
+
328
+ # Listen for all console logs.
329
+ page .on ('console' , lambda msg : print (f'CONSOLE: { msg .text } ' ))
330
+
331
+ fig , ax = plt .subplots (facecolor = 'w' )
332
+ ax .plot ([3 , 2 , 1 ])
333
+ orig_lim = ax .viewLim .frozen ()
334
+ # Make figure coords ~= axes coords, with ticks visible for inspection.
335
+ ax .set_position ([0 , 0 , 1 , 1 ])
336
+ ax .tick_params (axis = 'y' , direction = 'in' , pad = - 22 )
337
+ ax .tick_params (axis = 'x' , direction = 'in' , pad = - 15 )
338
+
339
+ # Don't start the Tornado event loop, but use the existing event loop
340
+ # started by the `page` fixture.
341
+ WebAggApplication .initialize ()
342
+ WebAggApplication .started = True
343
+
344
+ page .goto (f'http://{ WebAggApplication .address } :{ WebAggApplication .port } /' )
345
+
346
+ canvas = page .locator ('canvas.mpl-canvas' )
347
+ expect (canvas ).to_be_visible ()
348
+ home = page .locator ('button.mpl-widget' ).nth (0 )
349
+ expect (home ).to_be_visible ()
350
+ pan = page .locator ('button.mpl-widget' ).nth (3 )
351
+ expect (pan ).to_be_visible ()
352
+ zoom = page .locator ('button.mpl-widget' ).nth (4 )
353
+ expect (zoom ).to_be_visible ()
354
+
355
+ active_re = re .compile (r'active' )
356
+ expect (pan ).not_to_have_class (active_re )
357
+ expect (zoom ).not_to_have_class (active_re )
358
+ assert ax .get_navigate_mode () is None
359
+ zoom .click ()
360
+ expect (pan ).not_to_have_class (active_re )
361
+ expect (zoom ).to_have_class (active_re )
362
+ assert ax .get_navigate_mode () == 'ZOOM'
363
+
364
+ # Zoom 25% in on each side.
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 .mouse .move (x + bbox ['width' ] / 2 , y + bbox ['height' ] / 2 ,
370
+ steps = 20 )
371
+ page .mouse .up ()
372
+
373
+ assert ax .get_xlim () == (orig_lim .x0 + orig_lim .width / 4 ,
374
+ orig_lim .x1 - orig_lim .width / 4 )
375
+ assert ax .get_ylim () == (orig_lim .y0 + orig_lim .height / 4 ,
376
+ orig_lim .y1 - orig_lim .height / 4 )
377
+
378
+ # Reset.
379
+ home .click ()
380
+
381
+ # Zoom 25% in on each side, while holding 'x' key, to constrain the zoom
382
+ # horizontally..
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 ('x' )
388
+ page .mouse .move (x + bbox ['width' ] / 2 , y + bbox ['height' ] / 2 ,
389
+ steps = 20 )
390
+ page .mouse .up ()
391
+ page .keyboard .up ('x' )
392
+
393
+ assert ax .get_xlim () == (orig_lim .x0 + orig_lim .width / 4 ,
394
+ orig_lim .x1 - orig_lim .width / 4 )
395
+ assert ax .get_ylim () == (orig_lim .y0 , orig_lim .y1 )
396
+
397
+ # Reset.
398
+ home .click ()
399
+
400
+ # Zoom 25% in on each side, while holding 'y' key, to constrain the zoom
401
+ # vertically.
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 ()
406
+ page .keyboard .down ('y' )
407
+ page .mouse .move (x + bbox ['width' ] / 2 , y + bbox ['height' ] / 2 ,
408
+ steps = 20 )
409
+ page .mouse .up ()
410
+ page .keyboard .up ('y' )
411
+
412
+ assert ax .get_xlim () == (orig_lim .x0 , orig_lim .x1 )
413
+ assert ax .get_ylim () == (orig_lim .y0 + orig_lim .height / 4 ,
414
+ orig_lim .y1 - orig_lim .height / 4 )
415
+
416
+ # Reset.
417
+ home .click ()
418
+
419
+ # Zoom 25% out on each side.
420
+ bbox = canvas .bounding_box ()
421
+ x , y = bbox ['x' ] + bbox ['width' ] / 4 , bbox ['y' ] + bbox ['height' ] / 4
422
+ page .mouse .move (x , y )
423
+ page .mouse .down (button = 'right' )
424
+ page .mouse .move (x + bbox ['width' ] / 2 , y + bbox ['height' ] / 2 ,
425
+ steps = 20 )
426
+ page .mouse .up (button = 'right' )
427
+
428
+ # Limits were doubled, but based on the central point.
429
+ cx = orig_lim .x0 + orig_lim .width / 2
430
+ x0 = cx - orig_lim .width
431
+ x1 = cx + orig_lim .width
432
+ assert ax .get_xlim () == (x0 , x1 )
433
+ cy = orig_lim .y0 + orig_lim .height / 2
434
+ y0 = cy - orig_lim .height
435
+ y1 = cy + orig_lim .height
436
+ assert ax .get_ylim () == (y0 , y1 )
0 commit comments