1
# -*- Mode: Python; coding: utf-8; indent-tabs-mode: nil; tab-width: 4 -*-
2
# Copyright 2012 Canonical
3
# Author: Marco Trevisan (Treviño)
5
# This program is free software: you can redistribute it and/or modify it
6
# under the terms of the GNU General Public License version 3, as published
7
# by the Free Software Foundation.
11
from testtools.matchers import Equals, GreaterThan, NotEquals
12
from time import sleep
14
from autopilot.emulators.X11 import ScreenGeometry
15
from autopilot.emulators.bamf import BamfWindow
16
from autopilot.emulators.unity.panel import IndicatorEntry
17
from autopilot.tests import AutopilotTestCase
20
logger = logging.getLogger(__name__)
23
def _make_monitor_scenarios():
24
num_monitors = ScreenGeometry().get_num_monitors()
28
scenarios = [('Single Monitor', {'panel_monitor': 0})]
30
for i in range(num_monitors):
31
scenarios += [('Monitor %d' % (i), {'panel_monitor': i})]
36
class PanelTestsBase(AutopilotTestCase):
41
super(PanelTestsBase, self).setUp()
42
self.panel = self.panels.get_panel_for_monitor(self.panel_monitor)
43
self.panel.move_mouse_below_the_panel()
44
self.addCleanup(self.panel.move_mouse_below_the_panel)
46
def open_new_application_window(self, app_name, maximized=False, move_to_monitor=True):
47
"""Opens a new instance of the requested application, ensuring that only
50
Returns the opened BamfWindow
53
self.close_all_app(app_name)
54
app = self.start_app(app_name, locale="C")
56
[app_win] = app.get_windows()
59
self.assertTrue(app.is_active)
60
self.assertTrue(app_win.is_focused)
61
self.assertThat(app.desktop_file, Equals(app_win.application.desktop_file))
64
self.move_window_to_panel_monitor(app_win)
66
if maximized and not app_win.is_maximized:
67
self.keybinding("window/maximize")
68
self.addCleanup(self.keybinding, "window/restore")
69
elif not maximized and app_win.is_maximized:
70
self.keybinding("window/restore")
71
self.addCleanup(self.keybinding, "window/maximize")
76
self.assertThat(app_win.is_maximized, Equals(maximized))
80
def move_window_to_panel_monitor(self, window, restore_position=True):
81
"""Drags a window to another monitor, eventually restoring it before"""
82
if not isinstance(window, BamfWindow):
83
raise TypeError("Window must be a BamfWindow")
85
if window.monitor == self.panel_monitor:
88
if window.is_maximized:
89
self.keybinding("window/restore")
90
self.addCleanup(self.keybinding, "window/maximize")
94
self.addCleanup(self.screen_geo.drag_window_to_monitor, window, window.monitor)
96
self.screen_geo.drag_window_to_monitor(window, self.panel_monitor)
98
self.assertThat(window.monitor, Equals(self.panel_monitor))
100
def mouse_open_indicator(self, indicator):
101
"""This is an utility function that safely opens an indicator,
102
ensuring that it is closed at the end of the test and that the pointer
103
is moved outside the panel area (to make the panel hide the menus)
105
if not isinstance(indicator, IndicatorEntry):
106
raise TypeError("Window must be a IndicatorEntry")
108
indicator.mouse_click()
109
self.addCleanup(self.panel.move_mouse_below_the_panel)
110
self.addCleanup(self.keyboard.press_and_release, "Escape")
112
self.assertTrue(indicator.active)
115
class PanelTitleTests(PanelTestsBase):
117
scenarios = _make_monitor_scenarios()
119
def test_panel_title_on_empty_desktop(self):
120
"""With no windows shown, the panel must display the default title."""
121
self.keybinding("window/show_desktop")
122
# We need this sleep to give the time to showdesktop to properly resume
123
# the initial status without getting a false-negative result
124
self.addCleanup(sleep, 2)
125
self.addCleanup(self.keybinding, "window/show_desktop")
128
self.assertTrue(self.panel.desktop_is_active)
130
def test_panel_title_with_restored_application(self):
131
"""Panel must display application name for a non-maximised application."""
132
calc_win = self.open_new_application_window("Calculator")
134
self.assertFalse(calc_win.is_maximized)
135
self.assertThat(self.panel.title, Equals(calc_win.application.name))
137
def test_panel_title_with_maximized_application(self):
138
"""Panel must display application name for a maximised application."""
139
text_win = self.open_new_application_window("Text Editor", maximized=True)
141
self.assertTrue(text_win.is_maximized)
142
self.assertThat(self.panel.title, Equals(text_win.title))
144
def test_panel_title_with_maximized_window_restored_child(self):
145
"""Tests the title shown in the panel when opening the restored child of
146
a maximized application.
148
text_win = self.open_new_application_window("Text Editor", maximized=True)
150
self.assertTrue(text_win.is_maximized)
151
self.assertThat(self.panel.title, Equals(text_win.title))
153
self.keyboard.press_and_release("Ctrl+h")
154
self.addCleanup(self.keyboard.press_and_release, "Escape")
156
self.assertThat(self.panel.title, Equals(text_win.application.name))
158
def test_panel_title_switching_active_window(self):
159
"""Tests the title shown in the panel with a maximized application."""
160
# Locked Launchers on all monitors
161
self.set_unity_option('num_launchers', 0)
162
self.set_unity_option('launcher_hide_mode', 0)
164
text_win = self.open_new_application_window("Text Editor", maximized=True)
166
self.assertTrue(text_win.is_maximized)
167
self.assertThat(self.panel.title, Equals(text_win.title))
170
calc_win = self.open_new_application_window("Calculator")
171
self.assertThat(self.panel.title, Equals(calc_win.application.name))
173
icon = self.launcher.model.get_icon_by_desktop_id(text_win.application.desktop_file)
174
launcher = self.launcher.get_launcher_for_monitor(self.panel_monitor)
175
launcher.click_launcher_icon(icon)
177
self.assertTrue(text_win.is_focused)
178
self.assertThat(self.panel.title, Equals(text_win.title))
180
def test_panel_title_updates_on_maximized_window_title_changes(self):
181
"""Tests that the title of a maximized application updates with
182
window title changes.
184
text_win = self.open_new_application_window("Text Editor", maximized=True)
186
self.assertThat(self.panel.title, Equals(text_win.title))
187
old_title = text_win.title
191
self.keyboard.type("Unity rocks!")
192
self.addCleanup(os.remove, "/tmp/autopilot-awesome-test.txt")
193
self.keyboard.press_and_release("Ctrl+S")
195
self.keyboard.type("/tmp/autopilot-awesome-test.txt")
196
self.keyboard.press_and_release("Return")
199
self.assertThat(old_title, NotEquals(text_win.title))
200
self.assertThat(self.panel.title, Equals(text_win.title))
203
class PanelWindowButtonsTests(PanelTestsBase):
205
scenarios = _make_monitor_scenarios()
208
super(PanelWindowButtonsTests, self).setUp()
209
# Locked Launchers on all monitors
210
self.set_unity_option('num_launchers', 0)
211
self.set_unity_option('launcher_hide_mode', 0)
213
def test_window_buttons_dont_show_on_empty_desktop(self):
214
"""Tests that the window buttons are not shown on clean desktop."""
215
# THis initially used Show Desktop mode, but it's very buggy from within
216
# autopilot. We assume that workspace 2 is empty (which is safe for the
217
# jenkins runs at least.)
218
self.workspace.switch_to(2)
220
self.assertFalse(self.panel.window_buttons_shown)
221
self.panel.move_mouse_over_window_buttons()
222
sleep(self.panel.menus.fadein_duration / 1000.0)
223
self.assertFalse(self.panel.window_buttons_shown)
225
def test_window_buttons_dont_show_for_restored_window(self):
226
"""Tests that the window buttons are not shown for a restored window."""
227
self.open_new_application_window("Calculator")
229
self.assertFalse(self.panel.window_buttons_shown)
231
self.panel.move_mouse_over_window_buttons()
232
sleep(self.panel.menus.fadein_duration / 1000.0)
233
self.assertFalse(self.panel.window_buttons_shown)
235
def test_window_buttons_dont_show_for_maximized_window_on_mouse_out(self):
236
"""Tests that the windows button arenot shown for a maximized window
237
when the mouse is outside the panel.
239
self.open_new_application_window("Text Editor", maximized=True)
241
sleep(self.panel.menus.discovery_duration)
242
sleep(self.panel.menus.discovery_fadein_duration / 1000.0)
243
sleep(self.panel.menus.discovery_fadeout_duration / 1000.0)
244
self.assertFalse(self.panel.window_buttons_shown)
246
def test_window_buttons_show_for_maximized_window_on_mouse_in(self):
247
"""Tests that the window buttons are shown when a maximized window
248
is focused and the mouse is over the menu-view panel areas.
250
self.open_new_application_window("Text Editor", maximized=True)
252
sleep(self.panel.menus.discovery_duration)
254
self.panel.move_mouse_over_window_buttons()
255
sleep(self.panel.menus.fadein_duration / 1000.0)
256
self.assertTrue(self.panel.window_buttons_shown)
258
buttons = self.panel.window_buttons.get_buttons()
259
self.assertThat(len(buttons), Equals(3))
260
for button in buttons:
261
self.assertFalse(button.overlay_mode)
263
def test_window_buttons_show_with_dash(self):
264
"""Tests that the window buttons are shown when opening the dash."""
265
self.dash.ensure_visible()
266
self.addCleanup(self.dash.ensure_hidden)
268
self.assertTrue(self.panel.window_buttons_shown)
270
buttons = self.panel.window_buttons.get_buttons()
271
self.assertThat(len(buttons), Equals(3))
272
for button in buttons:
273
self.assertTrue(button.overlay_mode)
275
def test_window_buttons_show_with_hud(self):
276
"""Tests that the window buttons are shown when opening the HUD."""
277
self.hud.ensure_visible()
278
self.addCleanup(self.hud.ensure_hidden)
280
self.assertTrue(self.panel.window_buttons_shown)
282
buttons = self.panel.window_buttons.get_buttons()
283
self.assertThat(len(buttons), Equals(3))
284
for button in buttons:
285
self.assertTrue(button.overlay_mode)
287
def test_window_buttons_update_visual_state(self):
288
"""Tests that the window button updates its visual state."""
289
self.hud.ensure_visible()
290
self.addCleanup(self.hud.ensure_hidden)
291
button = self.panel.window_buttons.close
293
self.assertThat(button.visual_state, Equals("normal"))
295
button.mouse_move_to()
296
self.assertTrue(button.visible)
297
self.assertTrue(button.enabled)
299
self.assertThat(button.visual_state, Equals("prelight"))
302
self.addCleanup(self.mouse.release)
304
self.assertThat(button.visual_state, Equals("pressed"))
306
def test_window_buttons_cancel(self):
307
"""Tests how the buttons ignore clicks when the mouse is pressed over
308
them and released outside their area.
310
self.hud.ensure_visible()
311
self.addCleanup(self.hud.ensure_hidden)
312
button = self.panel.window_buttons.close
314
button.mouse_move_to()
316
self.addCleanup(self.mouse.release)
318
self.panel.move_mouse_below_the_panel()
320
self.assertTrue(self.hud.visible)
322
def test_window_buttons_close_button_works_for_window(self):
323
"""Tests that the window button 'Close' actually closes a window."""
324
text_win = self.open_new_application_window("Text Editor", maximized=True, move_to_monitor=False)
325
self.move_window_to_panel_monitor(text_win, restore_position=False)
326
self.keybinding("window/maximize")
328
self.panel.move_mouse_over_window_buttons()
329
self.panel.window_buttons.close.mouse_click()
332
self.assertTrue(text_win.closed)
334
def test_window_buttons_close_follows_fitts_law(self):
335
"""Tests that the 'Close' button is activated when clicking at 0,0.
339
text_win = self.open_new_application_window("Text Editor", maximized=True, move_to_monitor=False)
340
self.move_window_to_panel_monitor(text_win, restore_position=False)
341
self.keybinding("window/maximize")
343
self.panel.move_mouse_over_window_buttons()
344
screen_x, screen_y, _, _ = self.screen_geo.get_monitor_geometry(self.panel_monitor)
345
self.mouse.move(screen_x, screen_y, rate=20, time_between_events=0.005)
347
self.mouse.click(press_duration=.1)
350
self.assertTrue(text_win.closed)
352
def test_window_buttons_minimize_button_works_for_window(self):
353
"""Tests that the window button 'Minimize' actually minimizes a window."""
354
text_win = self.open_new_application_window("Text Editor", maximized=True)
356
self.panel.window_buttons.minimize.mouse_click()
359
self.assertTrue(text_win.is_hidden)
361
icon = self.launcher.model.get_icon_by_desktop_id(text_win.application.desktop_file)
362
launcher = self.launcher.get_launcher_for_monitor(self.panel_monitor)
363
launcher.click_launcher_icon(icon)
365
def test_window_buttons_minimize_follows_fitts_law(self):
366
"""Tests that the 'Minimize' button is conform to Fitts's Law.
370
text_win = self.open_new_application_window("Text Editor", maximized=True)
372
button = self.panel.window_buttons.minimize
373
button.mouse_move_to()
374
target_x = button.x + button.width / 2
375
target_y = self.screen_geo.get_monitor_geometry(self.panel_monitor)[1]
376
self.mouse.move(target_x, target_y, rate=20, time_between_events=0.005)
378
self.mouse.click(press_duration=.1)
381
self.assertTrue(text_win.is_hidden)
382
icon = self.launcher.model.get_icon_by_desktop_id(text_win.application.desktop_file)
383
launcher = self.launcher.get_launcher_for_monitor(self.panel_monitor)
384
launcher.click_launcher_icon(icon)
386
def test_window_buttons_unmaximize_button_works_for_window(self):
387
"""Tests that the window button 'Unmaximize' actually unmaximizes a window."""
388
text_win = self.open_new_application_window("Text Editor", maximized=True)
390
self.panel.window_buttons.unmaximize.mouse_click()
393
self.assertFalse(text_win.is_maximized)
394
self.assertTrue(text_win.is_focused)
395
sleep(self.panel.menus.fadeout_duration / 1000.0)
396
self.assertFalse(self.panel.window_buttons_shown)
398
def test_window_buttons_unmaximize_follows_fitts_law(self):
399
"""Tests that the 'Unmaximize' button is conform to Fitts's Law.
403
text_win = self.open_new_application_window("Text Editor", maximized=True)
405
button = self.panel.window_buttons.unmaximize
406
button.mouse_move_to()
407
target_x = button.x + button.width / 2
408
target_y = self.screen_geo.get_monitor_geometry(self.panel_monitor)[1]
409
self.mouse.move(target_x, target_y, rate=20, time_between_events=0.005)
411
self.mouse.click(press_duration=.1)
414
self.assertFalse(text_win.is_maximized)
416
def test_window_buttons_close_button_works_for_hud(self):
417
"""Tests that the window 'Close' actually closes the HUD."""
418
self.hud.ensure_visible()
419
self.addCleanup(self.hud.ensure_hidden)
422
self.panel.window_buttons.close.mouse_click()
425
self.assertFalse(self.hud.visible)
427
def test_window_buttons_minimize_button_disabled_for_hud(self):
428
"""Tests that the window 'Minimize' does nothing to the HUD."""
429
self.hud.ensure_visible()
430
self.addCleanup(self.hud.ensure_hidden)
433
button = self.panel.window_buttons.minimize
437
self.assertFalse(button.enabled)
438
self.assertTrue(self.hud.visible)
440
def test_window_buttons_maximize_button_disabled_for_hud(self):
441
"""Tests that the window 'Maximize' does nothing to the HUD."""
442
self.hud.ensure_visible()
443
self.addCleanup(self.hud.ensure_hidden)
446
button = self.panel.window_buttons.maximize
450
self.assertFalse(button.enabled)
451
self.assertTrue(self.hud.visible)
453
def test_window_buttons_maximize_in_hud_does_not_change_dash_form_factor(self):
454
"""Clicking on the 'Maximize' button of the HUD must do nothing.
458
inital_form_factor = self.dash.view.form_factor
459
self.hud.ensure_visible()
460
self.addCleanup(self.hud.ensure_hidden)
463
self.panel.window_buttons.maximize.mouse_click()
466
self.assertThat(self.dash.view.form_factor, Equals(inital_form_factor))
468
def test_window_buttons_close_button_works_for_dash(self):
469
"""Tests that the window 'Close' actually closes the Dash."""
470
self.dash.ensure_visible()
471
self.addCleanup(self.dash.ensure_hidden)
474
self.panel.window_buttons.close.mouse_click()
477
self.assertFalse(self.dash.visible)
479
def test_window_buttons_minimize_button_disabled_for_dash(self):
480
"""Tests that the 'Minimize' button is disabled for the dash."""
481
self.dash.ensure_visible()
482
self.addCleanup(self.dash.ensure_hidden)
485
button = self.panel.window_buttons.minimize
489
self.assertFalse(button.enabled)
490
self.assertTrue(self.dash.visible)
492
def test_window_buttons_maximization_buttons_works_for_dash(self):
493
"""'Maximize' and 'Restore' buttons (when both enabled) must work as expected."""
494
self.dash.ensure_visible()
495
self.addCleanup(self.panel.window_buttons.close.mouse_click)
498
unmaximize = self.panel.window_buttons.unmaximize
499
maximize = self.panel.window_buttons.maximize
500
netbook_dash = (self.dash.view.form_factor == "netbook")
502
if netbook_dash and not unmaximize.enabled:
503
unmaximize.mouse_click()
505
self.assertThat(self.dash.view.form_factor, Equals("netbook"))
508
active_button = maximize
509
unactive_button = unmaximize
511
active_button = unmaximize
512
unactive_button = maximize
514
self.assertTrue(active_button.visible)
515
self.assertTrue(active_button.sensitive)
516
self.assertTrue(active_button.enabled)
517
self.assertFalse(unactive_button.visible)
519
self.addCleanup(unactive_button.mouse_click)
520
active_button.mouse_click()
523
self.assertTrue(unactive_button.visible)
524
self.assertTrue(unactive_button.sensitive)
525
self.assertTrue(unactive_button.enabled)
526
self.assertFalse(active_button.visible)
530
self.assertThat(self.dash.view.form_factor, Equals("desktop"))
532
self.assertThat(self.dash.view.form_factor, Equals("netbook"))
534
self.addCleanup(active_button.mouse_click)
535
unactive_button.mouse_click()
538
self.assertTrue(active_button.visible)
539
self.assertFalse(unactive_button.visible)
542
self.assertThat(self.dash.view.form_factor, Equals("netbook"))
544
self.assertThat(self.dash.view.form_factor, Equals("desktop"))
546
def test_window_buttons_minimize_button_disabled_for_non_minimizable_windows(self):
547
"""Tests that if a maximized window doesn't support the minimization,
548
then the 'Minimize' window button should be disabled.
550
text_win = self.open_new_application_window("Text Editor")
552
self.keyboard.press_and_release("Ctrl+S")
553
self.addCleanup(self.keyboard.press_and_release, "Escape")
555
wins = text_win.application.get_windows()
556
self.assertThat(len(wins), Equals(2))
558
if win.x_id != text_win.x_id:
562
target_win.set_focus()
563
self.assertTrue(target_win.is_focused)
564
self.move_window_to_panel_monitor(target_win, restore_position=False)
566
self.keybinding("window/maximize")
568
self.assertThat(target_win.monitor, Equals(self.panel_monitor))
569
self.assertTrue(target_win.is_maximized)
571
self.assertTrue(self.panel.window_buttons.close.enabled)
572
self.assertFalse(self.panel.window_buttons.minimize.enabled)
574
def test_window_buttons_show_when_indicator_active_and_mouse_over_panel(self):
575
"""Tests that when an indicator is opened, and the mouse goes over the
576
panel view, then the window buttons are revealed.
578
self.open_new_application_window("Text Editor", maximized=True)
579
sleep(self.panel.menus.fadein_duration / 1000.0)
580
sleep(self.panel.menus.discovery_duration)
581
sleep(self.panel.menus.fadeout_duration / 1000.0)
583
indicator = self.panel.indicators.get_indicator_by_name_hint("indicator-session-devices")
584
self.mouse_open_indicator(indicator)
586
self.assertFalse(self.panel.window_buttons_shown)
587
self.panel.move_mouse_below_the_panel()
588
sleep(self.panel.menus.fadeout_duration / 1000.0)
590
self.assertFalse(self.panel.window_buttons_shown)
591
self.panel.move_mouse_over_grab_area()
592
sleep(self.panel.menus.fadein_duration / 1000.0)
593
self.assertTrue(self.panel.window_buttons_shown)
595
def test_window_buttons_show_when_holding_show_menu_key(self):
596
self.open_new_application_window("Text Editor", maximized=True)
597
sleep(self.panel.menus.fadein_duration / 1000.0)
598
sleep(self.panel.menus.discovery_duration)
599
sleep(self.panel.menus.fadeout_duration / 1000.0)
601
self.keybinding_hold("panel/show_menus")
602
self.addCleanup(self.keybinding_release, "panel/show_menus")
604
self.assertTrue(self.panel.window_buttons_shown)
606
self.keybinding_release("panel/show_menus")
607
sleep(self.panel.menus.fadeout_duration / 1000.0)
609
self.assertFalse(self.panel.window_buttons_shown)
612
class PanelHoveringTests(PanelTestsBase):
613
"""Tests with the mouse pointer hovering the panel area."""
615
scenarios = _make_monitor_scenarios()
617
def test_only_menus_show_for_restored_window_on_mouse_in(self):
618
"""Tests that only menus of a restored window are shown only when
619
the mouse pointer is over the panel menu area.
621
self.open_new_application_window("Calculator")
622
sleep(self.panel.menus.fadein_duration / 1000.0)
623
sleep(self.panel.menus.discovery_duration)
624
sleep(self.panel.menus.fadeout_duration / 1000.0)
626
self.panel.move_mouse_over_window_buttons()
627
sleep(self.panel.menus.fadeout_duration / 1000.0)
628
self.assertFalse(self.panel.window_buttons_shown)
629
self.assertTrue(self.panel.menus_shown)
631
self.panel.move_mouse_over_menus()
632
sleep(self.panel.menus.fadein_duration / 1000.0)
633
self.assertFalse(self.panel.window_buttons_shown)
634
self.assertTrue(self.panel.menus_shown)
636
if self.panel.grab_area.width > 0:
637
self.panel.move_mouse_over_grab_area()
638
sleep(self.panel.menus.fadeout_duration / 1000.0)
639
self.assertFalse(self.panel.window_buttons_shown)
640
self.assertTrue(self.panel.menus_shown)
642
self.panel.move_mouse_over_indicators()
643
sleep(self.panel.menus.fadeout_duration / 1000.0)
644
self.assertFalse(self.panel.menus_shown)
646
def test_menus_show_for_maximized_window_on_mouse_in(self):
647
"""Tests that menus and window buttons of a maximized window are shown
648
only when the mouse pointer is over the panel menu area.
650
self.open_new_application_window("Text Editor", maximized=True)
651
sleep(self.panel.menus.fadein_duration / 1000.0)
652
sleep(self.panel.menus.discovery_duration)
653
sleep(self.panel.menus.fadeout_duration / 1000.0)
655
self.panel.move_mouse_over_window_buttons()
656
sleep(self.panel.menus.fadeout_duration / 1000.0)
657
self.assertTrue(self.panel.window_buttons_shown)
658
self.assertTrue(self.panel.menus_shown)
660
self.panel.move_mouse_over_menus()
661
sleep(self.panel.menus.fadein_duration / 1000.0)
662
self.assertTrue(self.panel.window_buttons_shown)
663
self.assertTrue(self.panel.menus_shown)
665
if self.panel.grab_area.width > 0:
666
self.panel.move_mouse_over_grab_area()
667
sleep(self.panel.menus.fadeout_duration / 1000.0)
668
self.assertTrue(self.panel.window_buttons_shown)
669
self.assertTrue(self.panel.menus_shown)
671
self.panel.move_mouse_over_indicators()
672
sleep(self.panel.menus.fadeout_duration / 1000.0)
673
self.assertFalse(self.panel.window_buttons_shown)
674
self.assertFalse(self.panel.menus_shown)
676
def test_hovering_indicators_open_menus(self):
677
"""Opening an indicator entry, and then hovering on other entries must
680
self.open_new_application_window("Text Editor")
681
entries = self.panel.get_indicator_entries(include_hidden_menus=True)
683
self.assertThat(len(entries), GreaterThan(0))
684
self.mouse_open_indicator(entries[0])
686
for entry in entries:
687
entry.mouse_move_to()
689
self.assertTrue(entry.active)
690
self.assertThat(entry.menu_y, NotEquals(0))
693
class PanelMenuTests(PanelTestsBase):
695
scenarios = _make_monitor_scenarios()
697
def test_menus_are_added_on_new_application(self):
698
"""Tests that menus are added when a new application is opened."""
699
self.open_new_application_window("Calculator")
701
menu_entries = self.panel.menus.get_entries()
702
self.assertThat(len(menu_entries), Equals(3))
704
menu_view = self.panel.menus
705
self.assertThat(menu_view.get_menu_by_label("_Calculator"), NotEquals(None))
706
self.assertThat(menu_view.get_menu_by_label("_Mode"), NotEquals(None))
707
self.assertThat(menu_view.get_menu_by_label("_Help"), NotEquals(None))
709
def test_menus_are_not_shown_if_the_application_has_no_menus(self):
710
"""Tests that if an application has no menus, then they are not
713
# TODO: This doesn't test what it says on the tin. Setting MENUPROXY to ''
714
# just makes the menu appear inside the app. That's fine, but it's not
715
# what is described in the docstring or test id.
716
old_env = os.environ["UBUNTU_MENUPROXY"]
717
os.putenv("UBUNTU_MENUPROXY", "")
718
self.addCleanup(os.putenv, "UBUNTU_MENUPROXY", old_env)
719
calc_win = self.open_new_application_window("Calculator")
722
self.assertThat(len(self.panel.menus.get_entries()), Equals(0))
724
self.panel.move_mouse_over_grab_area()
725
sleep(self.panel.menus.fadein_duration / 1000.0)
726
self.assertThat(self.panel.title, Equals(calc_win.application.name))
728
def test_menus_shows_when_new_application_is_opened(self):
729
"""This tests the menu discovery feature on new application."""
731
self.open_new_application_window("Calculator")
732
sleep(self.panel.menus.fadein_duration / 1000.0)
734
self.assertTrue(self.panel.menus_shown)
736
sleep(self.panel.menus.discovery_duration)
737
sleep(self.panel.menus.fadeout_duration / 1000.0)
739
self.assertFalse(self.panel.menus_shown)
741
def test_menus_dont_show_if_a_new_application_window_is_opened(self):
742
"""This tests the menu discovery feature on new window for a know application."""
743
self.open_new_application_window("Calculator")
744
sleep(self.panel.menus.fadein_duration / 1000.0)
746
self.assertTrue(self.panel.menus_shown)
748
sleep(self.panel.menus.discovery_duration)
749
sleep(self.panel.menus.fadeout_duration / 1000.0)
751
self.start_app("Calculator")
752
sleep(self.panel.menus.fadein_duration / 1000.0)
753
self.assertFalse(self.panel.menus_shown)
755
def test_menus_dont_show_for_restored_window_on_mouse_out(self):
756
"""Restored window menus must not show when the mouse is outside the
759
self.open_new_application_window("Calculator")
760
sleep(self.panel.menus.fadein_duration / 1000.0)
761
sleep(self.panel.menus.discovery_duration)
762
sleep(self.panel.menus.fadeout_duration / 1000.0)
764
self.assertFalse(self.panel.menus_shown)
766
def test_menus_show_for_restored_window_on_mouse_in(self):
767
"""Restored window menus must show only when the mouse is over the panel
770
self.open_new_application_window("Calculator")
771
sleep(self.panel.menus.fadein_duration / 1000.0)
772
sleep(self.panel.menus.discovery_duration)
773
sleep(self.panel.menus.fadeout_duration / 1000.0)
775
self.panel.move_mouse_over_menus()
776
sleep(self.panel.menus.fadein_duration / 1000.0)
777
self.assertTrue(self.panel.menus_shown)
779
def test_menus_dont_show_for_maximized_window_on_mouse_out(self):
780
"""Maximized window menus must not show when the mouse is outside the
783
self.open_new_application_window("Text Editor", maximized=True)
784
sleep(self.panel.menus.fadein_duration / 1000.0)
785
sleep(self.panel.menus.discovery_duration)
786
sleep(self.panel.menus.fadeout_duration / 1000.0)
788
self.assertFalse(self.panel.menus_shown)
790
def test_menus_show_for_maximized_window_on_mouse_in(self):
791
"""Maximized window menus must only show when the mouse is over the
794
self.open_new_application_window("Text Editor", maximized=True)
795
sleep(self.panel.menus.fadein_duration / 1000.0)
796
sleep(self.panel.menus.discovery_duration)
797
sleep(self.panel.menus.fadeout_duration / 1000.0)
799
self.panel.move_mouse_over_menus()
800
sleep(self.panel.menus.fadein_duration / 1000.0)
801
self.assertTrue(self.panel.menus_shown)
803
def test_menus_dont_show_with_dash(self):
804
"""Tests that menus are not showing when opening the dash."""
805
self.dash.ensure_visible()
806
self.addCleanup(self.dash.ensure_hidden)
809
self.assertFalse(self.panel.menus_shown)
811
def test_menus_dont_show_with_hud(self):
812
"""Tests that menus are not showing when opening the HUD."""
813
self.hud.ensure_visible()
814
self.addCleanup(self.hud.ensure_hidden)
817
self.assertFalse(self.panel.menus_shown)
819
def test_menus_show_after_closing_an_entry(self):
820
"""Opening a menu entry, and then hovering on other entries must open them.
822
We also check that the menus are still drawn when closed.
824
# TODO - should be split into multiple tests.
825
self.open_new_application_window("Calculator")
826
entries = self.panel.menus.get_entries()
828
self.assertThat(len(entries), GreaterThan(0))
829
self.mouse_open_indicator(entries[0])
831
for entry in entries:
832
entry.mouse_move_to()
834
self.assertTrue(entry.active)
835
self.assertThat(entry.menu_y, NotEquals(0))
838
last_entry.mouse_click()
840
self.assertFalse(last_entry.active)
841
self.assertThat(last_entry.menu_y, Equals(0))
843
def test_menus_show_when_indicator_active_and_mouse_over_panel(self):
844
"""When an indicator is opened, and the mouse goes over the panel view,
845
the menus must be revealed.
847
self.open_new_application_window("Calculator")
848
indicator = self.panel.indicators.get_indicator_by_name_hint("indicator-session-devices")
849
self.mouse_open_indicator(indicator)
850
sleep(self.panel.menus.fadein_duration / 1000.0)
851
sleep(self.panel.menus.discovery_duration)
852
sleep(self.panel.menus.fadeout_duration / 1000.0)
854
self.assertFalse(self.panel.menus_shown)
855
self.panel.move_mouse_below_the_panel()
856
sleep(self.panel.menus.fadeout_duration / 1000.0)
858
self.assertFalse(self.panel.menus_shown)
859
self.panel.move_mouse_over_grab_area()
860
sleep(self.panel.menus.fadein_duration / 1000.0)
861
self.assertTrue(self.panel.menus_shown)
863
def test_menus_show_when_holding_show_menu_key(self):
864
self.open_new_application_window("Calculator")
865
sleep(self.panel.menus.fadein_duration / 1000.0)
866
sleep(self.panel.menus.discovery_duration)
867
sleep(self.panel.menus.fadeout_duration / 1000.0)
869
self.keybinding_hold("panel/show_menus")
870
self.addCleanup(self.keybinding_release, "panel/show_menus")
872
self.assertTrue(self.panel.menus_shown)
874
self.keybinding_release("panel/show_menus")
875
sleep(self.panel.menus.fadeout_duration / 1000.0)
877
self.assertFalse(self.panel.menus_shown)
880
class PanelIndicatorEntriesTests(PanelTestsBase):
881
"""Tests for the indicator entries, including both menu and indicators."""
883
scenarios = _make_monitor_scenarios()
885
def test_menu_opens_on_click(self):
886
"""Tests that clicking on a menu entry, opens a menu."""
887
self.open_new_application_window("Calculator")
890
menu_entry = self.panel.menus.get_entries()[0]
891
self.mouse_open_indicator(menu_entry)
893
self.assertTrue(menu_entry.active)
894
self.assertThat(menu_entry.menu_x, Equals(menu_entry.x))
895
self.assertThat(menu_entry.menu_y, Equals(self.panel.height))
897
def test_menu_opens_closes_on_click(self):
898
"""Clicking on an open menu entru must close it again."""
899
self.open_new_application_window("Calculator")
901
menu_entry = self.panel.menus.get_entries()[0]
902
self.mouse_open_indicator(menu_entry)
906
self.assertFalse(menu_entry.active)
907
self.assertThat(menu_entry.menu_x, Equals(0))
908
self.assertThat(menu_entry.menu_y, Equals(0))
910
def test_menu_closes_on_click_outside(self):
911
"""Clicking outside an open menu must close it."""
912
self.open_new_application_window("Calculator")
914
menu_entry = self.panel.menus.get_entries()[0]
915
self.mouse_open_indicator(menu_entry)
917
self.assertTrue(menu_entry.active)
918
target_x = menu_entry.menu_x + menu_entry.menu_width/2
919
target_y = menu_entry.menu_y + menu_entry.menu_height + 10
920
self.mouse.move(target_x, target_y)
924
self.assertFalse(menu_entry.active)
925
self.assertThat(menu_entry.menu_x, Equals(0))
926
self.assertThat(menu_entry.menu_y, Equals(0))
929
class PanelKeyNavigationTests(PanelTestsBase):
931
scenarios = _make_monitor_scenarios()
933
def test_panel_first_menu_show_works(self):
934
"""Pressing the open-menus keybinding must open the first indicator."""
935
self.open_new_application_window("Calculator")
937
self.keybinding("panel/open_first_menu")
938
self.addCleanup(self.keyboard.press_and_release, "Escape")
941
open_indicator = self.panel.get_active_indicator()
942
expected_indicator = self.panel.get_indicator_entries(include_hidden_menus=True)[0]
943
self.assertThat(open_indicator, NotEquals(None))
944
self.assertThat(open_indicator.entry_id, Equals(expected_indicator.entry_id))
946
self.keybinding("panel/open_first_menu")
948
self.assertThat(self.panel.get_active_indicator(), Equals(None))
950
def test_panel_menu_accelerators_work(self):
951
self.open_new_application_window("Calculator")
953
self.keyboard.press_and_release("Alt+c")
954
self.addCleanup(self.keyboard.press_and_release, "Escape")
957
open_indicator = self.panel.get_active_indicator()
958
self.assertThat(open_indicator, NotEquals(None))
959
self.assertThat(open_indicator.label, Equals("_Calculator"))
961
def test_panel_indicators_key_navigation_next_works(self):
962
self.open_new_application_window("Calculator")
963
available_indicators = self.panel.get_indicator_entries(include_hidden_menus=True)
966
self.keybinding("panel/open_first_menu")
967
self.addCleanup(self.keyboard.press_and_release, "Escape")
970
open_indicator = self.panel.get_active_indicator()
971
expected_indicator = available_indicators[0]
972
self.assertThat(open_indicator.entry_id, Equals(expected_indicator.entry_id))
975
self.keybinding("panel/next_indicator")
976
open_indicator = self.panel.get_active_indicator()
977
expected_indicator = available_indicators[1]
979
self.assertThat(open_indicator.entry_id, Equals(expected_indicator.entry_id))
981
def test_panel_indicators_key_navigation_prev_works(self):
982
self.open_new_application_window("Calculator")
983
available_indicators = self.panel.get_indicator_entries(include_hidden_menus=True)
986
self.keybinding("panel/open_first_menu")
987
self.addCleanup(self.keyboard.press_and_release, "Escape")
990
open_indicator = self.panel.get_active_indicator()
991
expected_indicator = available_indicators[0]
992
self.assertThat(open_indicator.entry_id, Equals(expected_indicator.entry_id))
995
self.keybinding("panel/prev_indicator")
996
open_indicator = self.panel.get_active_indicator()
997
expected_indicator = available_indicators[-1]
999
self.assertThat(open_indicator.entry_id, Equals(expected_indicator.entry_id))
1001
def test_mouse_does_not_break_key_navigation(self):
1002
self.open_new_application_window("Calculator")
1003
available_indicators = self.panel.get_indicator_entries(include_hidden_menus=True)
1006
self.keybinding("panel/open_first_menu")
1007
self.addCleanup(self.keyboard.press_and_release, "Escape")
1010
available_indicators[2].mouse_move_to()
1011
self.addCleanup(self.panel.move_mouse_below_the_panel)
1013
self.assertTrue(available_indicators[2].active)
1016
self.keybinding("panel/prev_indicator")
1017
self.assertTrue(available_indicators[1].active)
1020
class PanelGrabAreaTests(PanelTestsBase):
1021
"""Panel grab area tests."""
1023
scenarios = _make_monitor_scenarios()
1025
def move_mouse_over_grab_area(self):
1026
self.panel.move_mouse_over_grab_area()
1027
self.addCleanup(self.panel.move_mouse_below_the_panel)
1030
def test_unmaximize_from_grab_area_works(self):
1031
"""Dragging a window down from the panel must unmaximize it."""
1032
text_win = self.open_new_application_window("Text Editor", maximized=True)
1034
self.move_mouse_over_grab_area()
1036
self.panel.move_mouse_below_the_panel()
1037
self.mouse.release()
1040
self.assertFalse(text_win.is_maximized)
1042
def test_focus_the_maximized_window_works(self):
1043
"""Clicking on the grab area must put a maximized window in focus."""
1044
text_win = self.open_new_application_window("Text Editor", maximized=True)
1046
self.open_new_application_window("Calculator")
1047
self.assertFalse(text_win.is_focused)
1049
self.move_mouse_over_grab_area()
1053
self.assertTrue(text_win.is_focused)
1055
def test_lower_the_maximized_window_works(self):
1056
"""Middle-clicking on the panel grab area must lower a maximized window."""
1057
calc_win = self.open_new_application_window("Calculator")
1059
self.open_new_application_window("Text Editor", maximized=True)
1060
self.assertFalse(calc_win.is_focused)
1062
self.move_mouse_over_grab_area()
1066
self.assertTrue(calc_win.is_focused)
1069
class PanelCrossMonitorsTests(PanelTestsBase):
1070
"""Multimonitor panel tests."""
1073
super(PanelCrossMonitorsTests, self).setUp()
1074
if self.screen_geo.get_num_monitors() < 2:
1075
self.skipTest("This test requires a multimonitor setup")
1077
def test_panel_title_updates_moving_window(self):
1078
"""Tests the title shown in the panel, moving a restored window around them."""
1079
calc_win = self.open_new_application_window("Calculator")
1082
for monitor in range(0, self.screen_geo.get_num_monitors()):
1083
if calc_win.monitor != monitor:
1084
self.addCleanup(self.screen_geo.drag_window_to_monitor, calc_win, calc_win.monitor)
1085
self.screen_geo.drag_window_to_monitor(calc_win, monitor)
1088
if prev_monitor >= 0:
1089
self.assertFalse(self.panels.get_panel_for_monitor(prev_monitor).active)
1091
panel = self.panels.get_panel_for_monitor(monitor)
1092
self.assertTrue(panel.active)
1093
self.assertThat(panel.title, Equals(calc_win.application.name))
1095
prev_monitor = monitor
1097
def test_window_buttons_dont_show_for_maximized_window_on_mouse_in(self):
1098
"""Window buttons must not show when the mouse is hovering the panel in
1101
self.open_new_application_window("Text Editor", maximized=True)
1103
sleep(self.panel.menus.discovery_duration)
1105
for monitor in range(0, self.screen_geo.get_num_monitors()):
1106
panel = self.panels.get_panel_for_monitor(monitor)
1107
panel.move_mouse_over_window_buttons()
1108
sleep(self.panel.menus.fadein_duration / 1000.0)
1110
if self.panel_monitor == monitor:
1111
self.assertTrue(panel.window_buttons_shown)
1113
self.assertFalse(panel.window_buttons_shown)
1115
def test_window_buttons_dont_show_in_other_monitors_when_dash_is_open(self):
1116
"""Window buttons must not show on the panels other than the one where
1119
self.dash.ensure_visible()
1120
self.addCleanup(self.dash.ensure_hidden)
1122
for monitor in range(0, self.screen_geo.get_num_monitors()):
1123
panel = self.panels.get_panel_for_monitor(monitor)
1125
if self.dash.monitor == monitor:
1126
self.assertTrue(panel.window_buttons_shown)
1128
self.assertFalse(panel.window_buttons_shown)
1130
def test_window_buttons_dont_show_in_other_monitors_when_hud_is_open(self):
1131
"""Window buttons must not show on the panels other than the one where
1134
self.hud.ensure_visible()
1135
self.addCleanup(self.hud.ensure_hidden)
1137
for monitor in range(0, self.screen_geo.get_num_monitors()):
1138
panel = self.panels.get_panel_for_monitor(monitor)
1140
if self.hud.monitor == monitor:
1141
self.assertTrue(panel.window_buttons_shown)
1143
self.assertFalse(panel.window_buttons_shown)
1145
def test_window_buttons_close_inactive_when_clicked_in_another_monitor(self):
1146
"""Clicking the close button must not affect the active maximized window on another monitor.
1150
text_win = self.open_new_application_window("Text Editor", maximized=True)
1152
for monitor in range(self.screen_geo.get_num_monitors()):
1153
panel = self.panels.get_panel_for_monitor(monitor)
1155
if monitor != text_win.monitor:
1156
panel.window_buttons.close.mouse_click()
1158
self.assertFalse(text_win.closed)
1160
def test_window_buttons_minimize_inactive_when_clicked_in_another_monitor(self):
1161
"""Clicking the minimise button must not affect the active maximized window on another monitor.
1165
text_win = self.open_new_application_window("Text Editor", maximized=True)
1167
for monitor in range(self.screen_geo.get_num_monitors()):
1168
panel = self.panels.get_panel_for_monitor(monitor)
1170
if monitor != text_win.monitor:
1171
panel.window_buttons.minimize.mouse_click()
1173
self.assertFalse(text_win.is_hidden)
1175
def test_window_buttons_unmaximize_inactive_when_clicked_in_another_monitor(self):
1176
"""Clicking the restore button must not affect the active maximized window on another monitor.
1180
text_win = self.open_new_application_window("Text Editor", maximized=True)
1182
for monitor in range(0, self.screen_geo.get_num_monitors()):
1183
panel = self.panels.get_panel_for_monitor(monitor)
1185
if monitor != text_win.monitor:
1186
panel.window_buttons.unmaximize.mouse_click()
1188
self.assertTrue(text_win.is_maximized)
1190
def test_hovering_indicators_on_multiple_monitors(self):
1191
"""Opening an indicator entry and then hovering others entries must open them."""
1192
text_win = self.open_new_application_window("Text Editor")
1193
panel = self.panels.get_panel_for_monitor(text_win.monitor)
1194
indicator = panel.indicators.get_indicator_by_name_hint("indicator-session-devices")
1195
self.mouse_open_indicator(indicator)
1197
for monitor in range(0, self.screen_geo.get_num_monitors()):
1198
panel = self.panels.get_panel_for_monitor(monitor)
1200
entries = panel.get_indicator_entries(include_hidden_menus=True)
1201
self.assertThat(len(entries), GreaterThan(0))
1203
for entry in entries:
1204
entry.mouse_move_to()
1207
if monitor != self.panel_monitor and entry.type == "menu":
1208
self.assertFalse(entry.active)
1209
self.assertFalse(entry.visible)
1210
self.assertThat(entry.menu_y, Equals(0))
1212
self.assertTrue(entry.visible)
1213
self.assertTrue(entry.active)
1214
self.assertThat(entry.menu_y, NotEquals(0))