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