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 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 import ubuntuuitoolkit
22 
23 from unity8.shell import emulators
24 
25 from autopilot import logging as autopilot_logging
26 from autopilot.introspection import dbus
27 from ubuntuuitoolkit import emulators as toolkit_emulators
28 
29 
30 logger = logging.getLogger(__name__)
31 
32 
33 class DashApp(object):
34 
35  """Autopilot helper for the Dash app."""
36 
37  def __init__(self, app_proxy):
38  self.app_proxy = app_proxy
39  self.main_view = self.app_proxy.select_single(
40  toolkit_emulators.MainView)
41  self.dash = self.main_view.select_single(Dash)
42 
43 
45  """An emulator that understands the Dash."""
46 
47  def __init__(self, *args):
48  super(Dash, self).__init__(*args)
49  self.dash_content_list = self.wait_select_single(
50  'QQuickListView', objectName='dashContentList')
51 
52  def get_applications_grid(self):
53  get_grid = self.get_scope('clickscope').wait_select_single(
54  'CardGrid', objectName='local')
55  return get_grid
56 
57  def get_application_icon(self, text):
58  """Returns a 'Tile' icon that has the text 'text' from the application
59  grid.
60 
61  :param text: String containing the text of the icon to search for.
62 
63  """
64  app_grid = self.get_applications_grid()
65  resp_grid = app_grid.wait_select_single('ResponsiveGridView')
66  return resp_grid.select_single('Tile', text=text)
67 
68  def get_scope(self, scope_name='clickscope'):
69  return self.dash_content_list.wait_select_single(
70  'QQuickLoader', scopeId=scope_name)
71 
72  def get_scope_by_index(self, scope_index=0):
73  return self.dash_content_list.wait_select_single(
74  'QQuickLoader', objectName=("scopeLoader%i" % scope_index))
75 
76  @autopilot_logging.log_action(logger.info)
77  def open_scope(self, scope_id):
78  """Open a dash scope.
79 
80  :parameter scope_id: The id of the scope.
81  :return: The scope.
82 
83  """
84  scope_loader = self._get_scope_loader(scope_id)
85  if scope_loader.isCurrent:
86  logger.info('The scope is already open.')
87  return self._get_scope_from_loader(scope_loader)
88  else:
89  return self._open_scope_scrolling(scope_loader)
90 
91  def _get_scope_loader(self, scope_id):
92  try:
93  aux = self.dash_content_list.get_children_by_type('QQuickItem')[0]
94  for l in aux.get_children_by_type('QQuickLoader'):
95  if (l.scopeId == scope_id):
96  return l;
98  'No scope found with id {0}'.format(scope_id))
99  except dbus.StateNotFoundError:
101  'No scope found with id {0}'.format(scope_id))
102 
103  def _get_scope_from_loader(self, loader):
104  return loader.wait_select_single('GenericScopeView');
105 
106  def _open_scope_scrolling(self, scope_loader):
107  scroll = self._get_scroll_direction(scope_loader)
108 
109  while not scope_loader.isCurrent:
110  scroll()
111  self.dash_content_list.moving.wait_for(False)
112 
113  scope_loader.isCurrent.wait_for(True)
114  scope = self._get_scope_from_loader(scope_loader)
115  return scope
116 
117  def _get_scroll_direction(self, scope_loader):
118  current_scope_loader = self.dash_content_list.select_single(
119  'QQuickLoader', isCurrent=True)
120  if scope_loader.globalRect.x < current_scope_loader.globalRect.x:
121  return self._scroll_to_left_scope
122  elif scope_loader.globalRect.x > current_scope_loader.globalRect.x:
123  return self._scroll_to_right_scope
124  else:
125  raise emulators.UnityEmulatorException('The scope is already open')
126 
127  @autopilot_logging.log_action(logger.info)
128  def _scroll_to_left_scope(self):
129  original_index = self.dash_content_list.currentIndex
130  dash_content = self.select_single(objectName="dashContent")
131  x, y, width, height = dash_content.globalRect
132  # Make the drag range be a multiple of the drag "rate" value.
133  # Workarounds https://bugs.launchpad.net/mir/+bug/1399690
134  rate = 10
135  divisions = 5
136  jump = ( width / divisions ) // rate * rate
137  start_x = x + jump
138  stop_x = x + jump * (divisions - 1)
139  start_y = stop_y = y + 1
140  self.pointing_device.drag(start_x, start_y, stop_x, stop_y, rate)
141  self.dash_content_list.currentIndex.wait_for(original_index - 1)
142 
143  @autopilot_logging.log_action(logger.info)
144  def _scroll_to_right_scope(self):
145  original_index = self.dash_content_list.currentIndex
146  dash_content = self.select_single(objectName="dashContent")
147  x, y, width, height = dash_content.globalRect
148  # Make the drag range be a multiple of the drag "rate" value.
149  # Workarounds https://bugs.launchpad.net/mir/+bug/1399690
150  rate = 10
151  divisions = 5
152  jump = ( width / divisions ) // rate * rate
153  start_x = x + jump * (divisions - 1)
154  stop_x = x + jump
155  start_y = stop_y = y + 1
156  self.pointing_device.drag(start_x, start_y, stop_x, stop_y, rate)
157  self.dash_content_list.currentIndex.wait_for(original_index + 1)
158 
159  def enter_search_query(self, query):
160  current_header = self._get_current_page_header()
161  search_button = current_header.select_single(objectName="search_header_button")
162  self.pointing_device.move(search_button.globalRect.x + search_button.width / 2,
163  search_button.globalRect.y + search_button.height / 2)
164  self.pointing_device.click()
165  headerContainer = current_header.select_single(
166  objectName="headerContainer")
167  headerContainer.contentY.wait_for(0)
168  search_text_field = self._get_search_text_field()
169  search_text_field.write(query)
170  self.select_single(objectName="processingIndicator").visible.wait_for(False)
171 
172  def _get_search_text_field(self):
173  page_header = self._get_current_page_header()
174  return page_header.select_single(objectName='searchTextField')
175 
176  def _get_current_page_header(self):
177  dashContentList = self.select_single(objectName="dashContentList")
178  all_headers = dashContentList.select_many("QQuickLoader")
179  for i in all_headers:
180  if i.isCurrent:
181  return i.select_single(objectName="scopePageHeader")
182  return None
183 
184 
185 class ListViewWithPageHeader(ubuntuuitoolkit.QQuickFlickable):
186  pass
187 
188 
189 class GenericScopeView(emulators.UnityEmulatorBase):
190  """Autopilot emulator for generic scopes."""
191 
192  @autopilot_logging.log_action(logger.info)
193  def open_preview(self, category, app_name):
194  """Open the preview of an application.
195 
196  :parameter category: The name of the category where the application is.
197  :parameter app_name: The name of the application.
198  :return: The opened preview.
199 
200  """
201  # FIXME some categories need a long press in order to see the preview.
202  # Some categories do not show previews, like recent apps.
203  # --elopio - 2014-1-14
204  self.click_scope_item(category, app_name)
205  preview_list = self.wait_select_single(
206  'QQuickLoader', objectName='subPageLoader')
207  preview_list.subPageShown.wait_for(True)
208  preview_list.x.wait_for(0)
209  return preview_list.select_single(
210  Preview, objectName='preview{}'.format(preview_list.currentIndex))
211 
212  @autopilot_logging.log_action(logger.debug)
213  def click_scope_item(self, category, title):
214  """Click an item from the scope.
215 
216  :parameter category: The name of the category where the item is.
217  :parameter title: The title of the item.
218 
219  """
220  category_element = self._get_category_element(category)
221  icon = category_element.wait_select_single('AbstractButton', title=title)
222  self.pointing_device.click_object(icon)
223 
224  def _get_category_element(self, category):
225  try:
226  return self.wait_select_single(
227  'DashCategoryBase', objectName='dashCategory{}'.format(category))
228  except dbus.StateNotFoundError:
230  'No category found with name {}'.format(category))
231 
232  def get_applications(self, category):
233  """Return the list of applications on a category.
234 
235  :parameter category: The name of the category.
236 
237  """
238  category_element = self._get_category_element(category)
239  see_all = category_element.select_single(objectName='seeAll')
240  application_cards = category_element.select_many('AbstractButton')
241 
242  application_cards = sorted(
243  (card for card in application_cards
244  if card.globalRect.y < see_all.globalRect.y),
245  key=lambda card: (card.globalRect.y, card.globalRect.x))
246 
247  result = []
248  for card in application_cards:
249  if card.objectName not in ('cardToolCard', 'seeAll'):
250  result.append(card.title)
251  return result
252 
253 
255  """Autopilot custom proxy object for generic previews."""
def get_application_icon(self, text)
Definition: dash.py:57
def click_scope_item(self, category, title)
Definition: dash.py:213
def _get_scope_loader(self, scope_id)
Definition: dash.py:91
def get_applications(self, category)
Definition: dash.py:232
def open_preview(self, category, app_name)
Definition: dash.py:193
def _get_scope_from_loader(self, loader)
Definition: dash.py:103
def _get_search_text_field(self)
Definition: dash.py:172
def _get_category_element(self, category)
Definition: dash.py:224
def get_applications_grid(self)
Definition: dash.py:52
def __init__(self, app_proxy)
Definition: dash.py:37
def _open_scope_scrolling(self, scope_loader)
Definition: dash.py:106
def _scroll_to_right_scope(self)
Definition: dash.py:144
def _get_current_page_header(self)
Definition: dash.py:176
def open_scope(self, scope_id)
Definition: dash.py:77
def _get_scroll_direction(self, scope_loader)
Definition: dash.py:117