Unity 8
dash.py
1 # -*- Mode: Python; coding: utf-8; indent-tabs-mode: nil; tab-width: 4 -*-
2 #
3 # Unity Autopilot Test Suite
4 # Copyright (C) 2012, 2013, 2014, 2015 Canonical
5 #
6 # This program is free software: you can redistribute it and/or modify
7 # it under the terms of the GNU General Public License as published by
8 # the Free Software Foundation, either version 3 of the License, or
9 # (at your option) any later version.
10 #
11 # This program is distributed in the hope that it will be useful,
12 # but WITHOUT ANY WARRANTY; without even the implied warranty of
13 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 # GNU General Public License for more details.
15 #
16 # You should have received a copy of the GNU General Public License
17 # along with this program. If not, see <http://www.gnu.org/licenses/>.
18 #
19 
20 import logging
21 
22 import ubuntuuitoolkit
23 from autopilot import logging as autopilot_logging
24 from autopilot.introspection import dbus
25 
26 import unity8
27 
28 
29 logger = logging.getLogger(__name__)
30 
31 
32 class DashApp():
33 
34  """Autopilot helper for the Dash app."""
35 
36  def __init__(self, app_proxy):
37  self.app_proxy = app_proxy
38  self.main_view = self.app_proxy.select_single(
39  ubuntuuitoolkit.MainView)
40  self.dash = self.main_view.select_single(Dash)
41 
42 
43 class Dash(ubuntuuitoolkit.UbuntuUIToolkitCustomProxyObjectBase):
44  """A helper that understands the Dash."""
45 
46  def __init__(self, *args):
47  super().__init__(*args)
48  self.dash_content_list = self.wait_select_single(
49  'QQuickListView', objectName='dashContentList')
50 
51  def get_applications_grid(self):
52  get_grid = self.get_scope('clickscope').wait_select_single(
53  'CardGrid', objectName='local')
54  return get_grid
55 
56  def get_application_icon(self, text):
57  """Returns a 'Tile' icon that has the text 'text' from the application
58  grid.
59 
60  :param text: String containing the text of the icon to search for.
61 
62  """
63  app_grid = self.get_applications_grid()
64  resp_grid = app_grid.wait_select_single('ResponsiveGridView')
65  return resp_grid.select_single('Tile', text=text)
66 
67  def get_scope(self, scope_name='clickscope'):
68  return self.dash_content_list.wait_select_single(
69  'QQuickLoader', scopeId=scope_name)
70 
71  def get_scope_by_index(self, scope_index=0):
72  return self.dash_content_list.wait_select_single(
73  'QQuickLoader', objectName=("scopeLoader%i" % scope_index))
74 
75  @autopilot_logging.log_action(logger.info)
76  def open_scope(self, scope_id):
77  """Open a dash scope.
78 
79  :parameter scope_id: The id of the scope.
80  :return: The scope.
81 
82  """
83  scope_loader = self._get_scope_loader(scope_id)
84  if scope_loader.isCurrent:
85  logger.info('The scope is already open.')
86  return self._get_scope_from_loader(scope_loader)
87  else:
88  return self._open_scope_scrolling(scope_loader)
89 
90  def _get_scope_loader(self, scope_id):
91  try:
92  aux = self.dash_content_list.get_children_by_type('QQuickItem')[0]
93  for l in aux.get_children_by_type('QQuickLoader'):
94  if (l.scopeId == scope_id):
95  return l
97  'No scope found with id {0}'.format(scope_id))
98  except dbus.StateNotFoundError:
100  'No scope found with id {0}'.format(scope_id))
101 
102  def _get_scope_from_loader(self, loader):
103  return loader.wait_select_single('GenericScopeView')
104 
105  def _open_scope_scrolling(self, scope_loader):
106  scroll = self._get_scroll_direction(scope_loader)
107 
108  while not scope_loader.isCurrent:
109  scroll()
110  self.dash_content_list.moving.wait_for(False)
111 
112  scope_loader.isCurrent.wait_for(True)
113  scope = self._get_scope_from_loader(scope_loader)
114  return scope
115 
116  def _get_scroll_direction(self, scope_loader):
117  current_scope_loader = self.dash_content_list.select_single(
118  'QQuickLoader', isCurrent=True)
119  if scope_loader.globalRect.x < current_scope_loader.globalRect.x:
120  return self._scroll_to_left_scope
121  elif scope_loader.globalRect.x > current_scope_loader.globalRect.x:
122  return self._scroll_to_right_scope
123  else:
124  raise unity8.UnityException('The scope is already open')
125 
126  @autopilot_logging.log_action(logger.info)
127  def _scroll_to_left_scope(self):
128  original_index = self.dash_content_list.currentIndex
129  dash_content = self.select_single(objectName="dashContent")
130  x, y, width, height = dash_content.globalRect
131  # Make the drag range be a multiple of the drag "rate" value.
132  # Workarounds https://bugs.launchpad.net/mir/+bug/1399690
133  rate = 5
134  divisions = 5
135  jump = (width / divisions) // rate * rate
136  start_x = x + jump
137  stop_x = x + jump * (divisions - 1)
138  start_y = stop_y = y + 1
139  self.pointing_device.drag(start_x, start_y, stop_x, stop_y, rate)
140  self.dash_content_list.currentIndex.wait_for(original_index - 1)
141 
142  @autopilot_logging.log_action(logger.info)
143  def _scroll_to_right_scope(self):
144  original_index = self.dash_content_list.currentIndex
145  dash_content = self.select_single(objectName="dashContent")
146  x, y, width, height = dash_content.globalRect
147  # Make the drag range be a multiple of the drag "rate" value.
148  # Workarounds https://bugs.launchpad.net/mir/+bug/1399690
149  rate = 5
150  divisions = 5
151  jump = (width / divisions) // rate * rate
152  start_x = x + jump * (divisions - 1)
153  stop_x = x + jump
154  start_y = stop_y = y + 1
155  self.pointing_device.drag(start_x, start_y, stop_x, stop_y, rate)
156  self.dash_content_list.currentIndex.wait_for(original_index + 1)
157 
158  def enter_search_query(self, query, keyboard):
159  current_header = self._get_current_page_header()
160  search_button = \
161  current_header.select_single(objectName="search_action_button")
162  self.pointing_device.move(
163  search_button.globalRect.x + search_button.width / 2,
164  search_button.globalRect.y + search_button.height / 2)
165  self.pointing_device.click()
166  headerContainer = current_header.select_single(
167  objectName="headerContainer")
168  headerContainer.contentY.wait_for(0)
169  keyboard.type(query)
170  self.select_single(
171  objectName="processingIndicator").visible.wait_for(False)
172 
173  def get_search_text_field(self):
174  page_header = self._get_current_page_header()
175  return page_header.select_single(objectName='searchTextField')
176 
177  def _get_current_page_header(self):
178  dashContentList = self.select_single(objectName="dashContentList")
179  all_headers = dashContentList.select_many("QQuickLoader")
180  for i in all_headers:
181  if i.isCurrent:
182  return i.select_single(objectName="scopePageHeader")
183  return None
184 
185 
186 class ListViewWithPageHeader(ubuntuuitoolkit.QQuickFlickable):
187 
188  margin_to_swipe_from_bottom = ubuntuuitoolkit.units.gu(4)
189 
190 
191 class GenericScopeView(ubuntuuitoolkit.UbuntuUIToolkitCustomProxyObjectBase):
192  """Autopilot helper for generic scopes."""
193 
194  @autopilot_logging.log_action(logger.info)
195  def open_preview(self, category, app_name, press_duration=0.10):
196  """Open the preview of an application.
197 
198  :parameter category: The name of the category where the application is.
199  :parameter app_name: The name of the application.
200  :return: The opened preview.
201 
202  """
203  # FIXME some categories need a long press in order to see the preview.
204  # Some categories do not show previews, like recent apps.
205  # --elopio - 2014-1-14
206  self.click_scope_item(category, app_name, press_duration)
207  preview_list = self.wait_select_single(
208  'QQuickLoader', objectName='subPageLoader')
209  preview_list.subPageShown.wait_for(True)
210  preview_list.x.wait_for(0)
211  self.get_root_instance().select_single(
212  objectName='processingIndicator').visible.wait_for(False)
213  return preview_list.select_single(
214  Preview, objectName='preview{}'.format(
215  preview_list.initialIndex))
216 
217  @autopilot_logging.log_action(logger.debug)
218  def click_scope_item(self, category, title, press_duration=0.10):
219  """Click an item from the scope.
220 
221  :parameter category: The name of the category where the item is.
222  :parameter title: The title of the item.
223 
224  """
225  category_element = self._get_category_element(category)
226  icon = category_element.wait_select_single(
227  'UCAbstractButton', title=title)
228  list_view = self.select_single(
229  ListViewWithPageHeader, objectName='categoryListView')
230  list_view.swipe_child_into_view(icon)
231  self.pointing_device.click_object(icon, press_duration=press_duration)
232 
233  def _get_category_element(self, category):
234  try:
235  return self.wait_select_single(
236  'DashCategoryBase',
237  objectName='dashCategory{}'.format(category))
238  except dbus.StateNotFoundError:
239  raise unity8.UnityException(
240  'No category found with name {}'.format(category))
241 
242  def get_applications(self, category):
243  """Return the list of applications on a category.
244 
245  :parameter category: The name of the category.
246 
247  """
248  category_element = self._get_category_element(category)
249  see_all = category_element.select_single(objectName='seeAll')
250  application_cards = category_element.select_many('UCAbstractButton')
251 
252  application_cards = sorted(
253  (card for card in application_cards
254  if card.globalRect.y < see_all.globalRect.y),
255  key=lambda card: (card.globalRect.y, card.globalRect.x))
256 
257  result = []
258  for card in application_cards:
259  if card.objectName not in ('cardToolCard', 'seeAll'):
260  result.append(card.title)
261  return result
262 
263 
264 class Preview(ubuntuuitoolkit.UbuntuUIToolkitCustomProxyObjectBase):
265  """Autopilot custom proxy object for generic previews."""
def _get_scope_loader(self, scope_id)
Definition: dash.py:90
def get_applications_grid(self)
Definition: dash.py:51
def __init__(self, app_proxy)
Definition: dash.py:36
def _open_scope_scrolling(self, scope_loader)
Definition: dash.py:105
def get_application_icon(self, text)
Definition: dash.py:56
def _get_scope_from_loader(self, loader)
Definition: dash.py:102
def _scroll_to_right_scope(self)
Definition: dash.py:143
def get_scope
Definition: dash.py:67
def _get_category_element(self, category)
Definition: dash.py:233
def get_applications(self, category)
Definition: dash.py:242
def _scroll_to_left_scope(self)
Definition: dash.py:127
def open_scope(self, scope_id)
Definition: dash.py:76
def _get_scroll_direction(self, scope_loader)
Definition: dash.py:116
def _get_current_page_header(self)
Definition: dash.py:177