Merge lp:~elopio/ubuntu-ui-toolkit/datepicker-autopilot_helper into lp:ubuntu-ui-toolkit/staging

Proposed by Leo Arias
Status: Superseded
Proposed branch: lp:~elopio/ubuntu-ui-toolkit/datepicker-autopilot_helper
Merge into: lp:ubuntu-ui-toolkit/staging
Diff against target: 985 lines (+688/-41)
12 files modified
modules/Ubuntu/Components/Pickers/DatePicker.qml (+3/-5)
modules/Ubuntu/Components/Pickers/PickerRow.qml (+1/-1)
tests/autopilot/ubuntuuitoolkit/__init__.py (+4/-2)
tests/autopilot/ubuntuuitoolkit/_custom_proxy_objects/__init__.py (+4/-2)
tests/autopilot/ubuntuuitoolkit/_custom_proxy_objects/_common.py (+45/-1)
tests/autopilot/ubuntuuitoolkit/_custom_proxy_objects/_flickable.py (+50/-16)
tests/autopilot/ubuntuuitoolkit/_custom_proxy_objects/_qquicklistview.py (+28/-11)
tests/autopilot/ubuntuuitoolkit/_custom_proxy_objects/pickers.py (+206/-0)
tests/autopilot/ubuntuuitoolkit/emulators.py (+2/-2)
tests/autopilot/ubuntuuitoolkit/tests/custom_proxy_objects/test_date_picker.py (+147/-0)
tests/autopilot/ubuntuuitoolkit/tests/custom_proxy_objects/test_flickable.py (+197/-0)
tests/autopilot/ubuntuuitoolkit/tests/test_emulators.py (+1/-1)
To merge this branch: bzr merge lp:~elopio/ubuntu-ui-toolkit/datepicker-autopilot_helper
Reviewer Review Type Date Requested Status
PS Jenkins bot continuous-integration Needs Fixing
Allan LeSage (community) Approve
Ubuntu SDK team Pending
Review via email: mp+218588@code.launchpad.net

This proposal has been superseded by a proposal from 2014-05-09.

Commit message

Added autopilot helpers to pick a date from a date picker.

Description of the change

Next branch will add support for picking a time.

To post a comment you must log in.
1040. By Leo Arias

The containers don't change.

1041. By Leo Arias

Missing annotation.

Revision history for this message
Allan LeSage (allanlesage) wrote :

Ok I think understand this change, appears thoroughly tested, nice work :) . I'm assuming that the comment on l.535 doesn't apply to the present merge proposal.

review: Approve
Revision history for this message
PS Jenkins bot (ps-jenkins) wrote :
review: Needs Fixing (continuous-integration)
1042. By Leo Arias

Added the tests for the slow drag.

Revision history for this message
Leo Arias (elopio) wrote :

> Ok I think understand this change, appears thoroughly tested, nice work :) .
> I'm assuming that the comment on l.535 doesn't apply to the present merge
> proposal.

It does. I was just trying to test it the wrong way. Now I added a couple of tests for that.

1043. By Leo Arias

Fixed pep8.

1044. By Leo Arias

Merged with prerequisite.

1045. By Leo Arias

Added a warning.

1046. By Leo Arias

Updated the datepicker qttests.

1047. By Leo Arias

Merged with staging.

1048. By Leo Arias

Fixed the inheritance.

1049. By Leo Arias

Fixed pep8.

Unmerged revisions

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1=== modified file 'modules/Ubuntu/Components/Pickers/DatePicker.qml'
2--- modules/Ubuntu/Components/Pickers/DatePicker.qml 2014-04-23 08:50:20 +0000
3+++ modules/Ubuntu/Components/Pickers/DatePicker.qml 2014-05-09 04:12:47 +0000
4@@ -42,7 +42,7 @@
5 Column {
6 Label {
7 text: "Selected date: W" + datePicker.week + " - " +
8- Qt.formatDate(datePicker.date, "dddd, dd-mmmm-yyyy")
9+ Qt.formatDate(datePicker.date, "dddd, dd-MMMM-yyyy")
10 }
11 DatePicker {
12 id: datePicker
13@@ -61,7 +61,7 @@
14
15 Column {
16 Label {
17- text: "Selected month: " + Qt.formatDate(datePicker.date, "mmmm-yyyy")
18+ text: "Selected month: " + Qt.formatDate(datePicker.date, "MMMM-yyyy")
19 }
20 DatePicker {
21 id: datePicker
22@@ -117,7 +117,7 @@
23
24 Column {
25 Label {
26- text: "Selected date: " + Qt.formatDate(datePicker.date, "dddd, dd-mmmm-yyyy")
27+ text: "Selected date: " + Qt.formatDate(datePicker.date, "dddd, dd-MMMM-yyyy")
28 }
29 DatePicker {
30 id: datePicker
31@@ -667,5 +667,3 @@
32 }
33 }
34 }
35-
36-
37
38=== modified file 'modules/Ubuntu/Components/Pickers/PickerRow.qml'
39--- modules/Ubuntu/Components/Pickers/PickerRow.qml 2014-04-23 08:50:20 +0000
40+++ modules/Ubuntu/Components/Pickers/PickerRow.qml 2014-05-09 04:12:47 +0000
41@@ -96,7 +96,7 @@
42 }
43 delegate: PickerDelegate {
44 Label {
45- objectName: "PickerRow_PickerLabel"
46+ objectName: "PickerRow_PickerLabel" + (pickerModel ? modelData : "")
47 text: pickerModel ? pickerModel.text(modelData) : ""
48 anchors.fill: parent
49 verticalAlignment: Text.AlignVCenter
50
51=== modified file 'tests/autopilot/ubuntuuitoolkit/__init__.py'
52--- tests/autopilot/ubuntuuitoolkit/__init__.py 2014-04-25 18:39:51 +0000
53+++ tests/autopilot/ubuntuuitoolkit/__init__.py 2014-05-09 04:12:47 +0000
54@@ -24,14 +24,15 @@
55 'environment',
56 'emulators',
57 'fixture_setup',
58- 'Flickable',
59 'get_keyboard',
60 'get_pointing_device',
61 'Header',
62 'listitems',
63 'MainView',
64 'OptionSelector',
65+ 'pickers',
66 'popups',
67+ 'QQuickFlickable',
68 'QQuickListView',
69 'TabBar',
70 'Tabs',
71@@ -53,14 +54,15 @@
72 from ubuntuuitoolkit._custom_proxy_objects import (
73 check_autopilot_version,
74 CheckBox,
75- Flickable,
76 get_keyboard,
77 get_pointing_device,
78 Header,
79 listitems,
80 MainView,
81 OptionSelector,
82+ pickers,
83 popups,
84+ QQuickFlickable,
85 QQuickListView,
86 TabBar,
87 Tabs,
88
89=== modified file 'tests/autopilot/ubuntuuitoolkit/_custom_proxy_objects/__init__.py'
90--- tests/autopilot/ubuntuuitoolkit/_custom_proxy_objects/__init__.py 2014-04-28 15:39:24 +0000
91+++ tests/autopilot/ubuntuuitoolkit/_custom_proxy_objects/__init__.py 2014-05-09 04:12:47 +0000
92@@ -20,14 +20,15 @@
93 __all__ = [
94 'check_autopilot_version',
95 'CheckBox',
96- 'Flickable',
97 'get_keyboard',
98 'get_pointing_device',
99 'Header',
100 'listitems',
101 'MainView',
102 'OptionSelector',
103+ 'pickers',
104 'popups',
105+ 'QQuickFlickable',
106 'QQuickListView',
107 'TabBar',
108 'Tabs',
109@@ -45,13 +46,14 @@
110 ToolkitException,
111 UbuntuUIToolkitCustomProxyObjectBase,
112 )
113-from ubuntuuitoolkit._custom_proxy_objects._flickable import Flickable
114+from ubuntuuitoolkit._custom_proxy_objects._flickable import QQuickFlickable
115 from ubuntuuitoolkit._custom_proxy_objects._header import Header
116 from ubuntuuitoolkit._custom_proxy_objects import listitems
117 from ubuntuuitoolkit._custom_proxy_objects._mainview import MainView
118 from ubuntuuitoolkit._custom_proxy_objects._optionselector import (
119 OptionSelector
120 )
121+from ubuntuuitoolkit._custom_proxy_objects import pickers
122 from ubuntuuitoolkit._custom_proxy_objects import popups
123 from ubuntuuitoolkit._custom_proxy_objects._qquicklistview import (
124 QQuickListView
125
126=== modified file 'tests/autopilot/ubuntuuitoolkit/_custom_proxy_objects/_common.py'
127--- tests/autopilot/ubuntuuitoolkit/_custom_proxy_objects/_common.py 2014-04-16 21:13:39 +0000
128+++ tests/autopilot/ubuntuuitoolkit/_custom_proxy_objects/_common.py 2014-05-09 04:12:47 +0000
129@@ -16,13 +16,21 @@
130
131 """Common helpers for Ubuntu UI Toolkit Autopilot custom proxy objects."""
132
133+import logging
134 from distutils import version
135
136 import autopilot
137-from autopilot import platform, input
138+from autopilot import (
139+ input,
140+ logging as autopilot_logging,
141+ platform
142+)
143 from autopilot.introspection import dbus
144
145
146+logger = logging.getLogger(__name__)
147+
148+
149 class ToolkitException(Exception):
150 """Exception raised when there is an error with the emulator."""
151
152@@ -67,3 +75,39 @@
153 check_autopilot_version()
154 super(UbuntuUIToolkitCustomProxyObjectBase, self).__init__(*args)
155 self.pointing_device = get_pointing_device()
156+
157+ def is_flickable(self):
158+ """Check if the object is flickable.
159+
160+ If the object has a flicking attribute, we consider it as a flickable.
161+
162+ :return: True if the object is flickable. False otherwise.
163+
164+ """
165+ try:
166+ self.flicking
167+ return True
168+ except AttributeError:
169+ return False
170+
171+ @autopilot_logging.log_action(logger.info)
172+ def swipe_into_view(self):
173+ """Make the object visible.
174+
175+ Currently it works only when the object needs to be swiped vertically.
176+ TODO implement horizontal swiping. --elopio - 2014-03-21
177+
178+ """
179+ flickable_parent = self._get_flickable_parent()
180+ flickable_parent.swipe_child_into_view(self)
181+
182+ def _get_flickable_parent(self):
183+ parent = self.get_parent()
184+ root = self.get_root_instance()
185+ while parent.id != root.id:
186+ if parent.is_flickable():
187+ return parent
188+ parent = parent.get_parent()
189+ raise ToolkitException(
190+ "The element is not contained in a Flickable so it can't be "
191+ "swiped into view.")
192
193=== modified file 'tests/autopilot/ubuntuuitoolkit/_custom_proxy_objects/_flickable.py'
194--- tests/autopilot/ubuntuuitoolkit/_custom_proxy_objects/_flickable.py 2014-04-16 21:13:39 +0000
195+++ tests/autopilot/ubuntuuitoolkit/_custom_proxy_objects/_flickable.py 2014-05-09 04:12:47 +0000
196@@ -38,7 +38,7 @@
197 return min(containers_bottom)
198
199
200-class Flickable(_common.UbuntuUIToolkitCustomProxyObjectBase):
201+class Scrollable(_common.UbuntuUIToolkitCustomProxyObjectBase):
202
203 @autopilot_logging.log_action(logger.info)
204 def swipe_child_into_view(self, child):
205@@ -68,15 +68,19 @@
206 def _get_top_container(self):
207 """Return the top-most container with a globalRect."""
208 root = self.get_root_instance()
209- containers = [root]
210- while len(containers) == 1:
211- try:
212- containers[0].globalRect
213- return containers[0]
214- except AttributeError:
215- containers = containers[0].get_children()
216-
217- raise _common.ToolkitException("Couldn't find the top-most container.")
218+ parent = self.get_parent()
219+ top_container = None
220+ while parent.id != root.id:
221+ if hasattr(parent, 'globalRect'):
222+ top_container = parent
223+
224+ parent = parent.get_parent()
225+
226+ if top_container is None:
227+ raise _common.ToolkitException(
228+ "Couldn't find the top-most container.")
229+ else:
230+ return top_container
231
232 def _is_child_visible(self, child, containers):
233 """Check if the center of the child is visible.
234@@ -90,8 +94,35 @@
235 return (object_center >= visible_top and
236 object_center <= visible_bottom)
237
238+ def _slow_drag(self, start_x, stop_x, start_y, stop_y):
239+ # If we drag too fast, we end up scrolling more than what we
240+ # should, sometimes missing the element we are looking for.
241+ # I found that when the flickDeceleration is 1500, the rate should be
242+ # 5 and that when it's 100, the rate should be 1. With those two points
243+ # we can get that the following equation.
244+ rate = (self.flickDeceleration + 250) / 350
245+ self.pointing_device.drag(start_x, start_y, stop_x, stop_y, rate=rate)
246+
247+
248+class QQuickFlickable(Scrollable):
249+
250+ @autopilot_logging.log_action(logger.info)
251+ def swipe_child_into_view(self, child):
252+ """Make the child visible.
253+
254+ Currently it works only when the object needs to be swiped vertically.
255+ TODO implement horizontal swiping. --elopio - 2014-03-21
256+
257+ """
258+ containers = self._get_containers()
259+ if not self._is_child_visible(child, containers):
260+ self._swipe_non_visible_child_into_view(child, containers)
261+ else:
262+ logger.debug('The element is already visible.')
263+
264 @autopilot_logging.log_action(logger.info)
265 def _swipe_non_visible_child_into_view(self, child, containers):
266+ original_content_y = self.contentY
267 while not self._is_child_visible(child, containers):
268 # Check the direction of the swipe based on the position of the
269 # child relative to the immediate flickable container.
270@@ -100,6 +131,10 @@
271 else:
272 self._swipe_to_show_more_below(containers)
273
274+ if self.contentY == original_content_y:
275+ raise _common.ToolkitException(
276+ "Couldn't swipe in the flickable.")
277+
278 @autopilot_logging.log_action(logger.info)
279 def _swipe_to_show_more_above(self, containers):
280 if self.atYBeginning:
281@@ -124,8 +159,12 @@
282 # bottom.
283 top = _get_visible_container_top(containers) + 5
284 bottom = _get_visible_container_bottom(containers) - 5
285+
286 if direction == 'below':
287- start_y = bottom
288+ # Take into account that swiping from below can open the toolbar or
289+ # trigger the bottom edge gesture.
290+ # XXX Do this only if we are close to the bottom edge.
291+ start_y = bottom - 20
292 stop_y = top
293 elif direction == 'above':
294 start_y = top
295@@ -137,11 +176,6 @@
296 self.dragging.wait_for(False)
297 self.moving.wait_for(False)
298
299- def _slow_drag(self, start_x, stop_x, start_y, stop_y):
300- # If we drag too fast, we end up scrolling more than what we
301- # should, sometimes missing the element we are looking for.
302- self.pointing_device.drag(start_x, start_y, stop_x, stop_y, rate=5)
303-
304 @autopilot_logging.log_action(logger.info)
305 def _scroll_to_top(self):
306 if not self.atYBeginning:
307
308=== modified file 'tests/autopilot/ubuntuuitoolkit/_custom_proxy_objects/_qquicklistview.py'
309--- tests/autopilot/ubuntuuitoolkit/_custom_proxy_objects/_qquicklistview.py 2014-04-16 21:13:39 +0000
310+++ tests/autopilot/ubuntuuitoolkit/_custom_proxy_objects/_qquicklistview.py 2014-05-09 04:12:47 +0000
311@@ -19,22 +19,24 @@
312 from autopilot import logging as autopilot_logging
313 from autopilot.introspection import dbus
314
315-from ubuntuuitoolkit._custom_proxy_objects import _flickable
316-from ubuntuuitoolkit._custom_proxy_objects import _common
317-
318+from ubuntuuitoolkit._custom_proxy_objects import _common, _flickable
319
320 logger = logging.getLogger(__name__)
321
322
323-class QQuickListView(_flickable.Flickable):
324+class QQuickListView(_flickable.QQuickFlickable):
325
326 @autopilot_logging.log_action(logger.info)
327- def click_element(self, object_name):
328+ def click_element(self, object_name, direction=None):
329 """Click an element from the list.
330
331 It swipes the element into view if it's center is not visible.
332
333 :parameter objectName: The objectName property of the element to click.
334+ :parameter direction: The direction where the element is, it can be
335+ either 'above' or 'below'. Default value is None, which means we
336+ don't know where the object is and we will need to search the full
337+ list.
338
339 """
340 try:
341@@ -42,16 +44,31 @@
342 except dbus.StateNotFoundError:
343 # The element might be on a part of the list that hasn't been
344 # created yet. We have to search for it scrolling the entire list.
345- element = self._find_element(object_name)
346+ element = self._find_element(object_name, direction)
347 self.swipe_child_into_view(element)
348 self.pointing_device.click_object(element)
349
350 @autopilot_logging.log_action(logger.info)
351- def _find_element(self, object_name):
352- self._scroll_to_top()
353- while not self.atYEnd:
354- containers = self._get_containers()
355- self._swipe_to_show_more_below(containers)
356+ def _find_element(self, object_name, direction=None):
357+ if direction is None:
358+ # We don't know where the object is so we start looking for it from
359+ # the top.
360+ self._scroll_to_top()
361+ direction = 'below'
362+
363+ if direction == 'below':
364+ fail_condition = lambda: self.atYEnd
365+ swipe_method = self._swipe_to_show_more_below
366+ elif direction == 'above':
367+ fail_condition = lambda: self.atYBeginning
368+ swipe_method = self._swipe_to_show_more_above
369+ else:
370+ raise _common.ToolkitException(
371+ 'Invalid direction: {}'.format(direction))
372+
373+ containers = self._get_containers()
374+ while not fail_condition():
375+ swipe_method(containers)
376 try:
377 return self.select_single(objectName=object_name)
378 except dbus.StateNotFoundError:
379
380=== added file 'tests/autopilot/ubuntuuitoolkit/_custom_proxy_objects/pickers.py'
381--- tests/autopilot/ubuntuuitoolkit/_custom_proxy_objects/pickers.py 1970-01-01 00:00:00 +0000
382+++ tests/autopilot/ubuntuuitoolkit/_custom_proxy_objects/pickers.py 2014-05-09 04:12:47 +0000
383@@ -0,0 +1,206 @@
384+# -*- Mode: Python; coding: utf-8; indent-tabs-mode: nil; tab-width: 4 -*-
385+#
386+# Copyright (C) 2014 Canonical Ltd.
387+#
388+# This program is free software; you can redistribute it and/or modify
389+# it under the terms of the GNU Lesser General Public License as published by
390+# the Free Software Foundation; version 3.
391+#
392+# This program is distributed in the hope that it will be useful,
393+# but WITHOUT ANY WARRANTY; without even the implied warranty of
394+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
395+# GNU Lesser General Public License for more details.
396+#
397+# You should have received a copy of the GNU Lesser General Public License
398+# along with this program. If not, see <http://www.gnu.org/licenses/>.
399+
400+import datetime
401+import logging
402+
403+from autopilot import logging as autopilot_logging
404+from autopilot.introspection import dbus
405+
406+from ubuntuuitoolkit._custom_proxy_objects import (
407+ _common,
408+ _flickable,
409+ _qquicklistview
410+)
411+
412+
413+logger = logging.getLogger(__name__)
414+
415+
416+class DatePicker(_common.UbuntuUIToolkitCustomProxyObjectBase):
417+ """Autopilot helper for the DatePicker component."""
418+
419+ @autopilot_logging.log_action(logger.info)
420+ def pick_date(self, date):
421+ """Pick a date from the date picker.
422+
423+ :parameter date: The date to pick.
424+ :type date: An object with year, month and day attributes, like
425+ python's datetime.date.
426+ :raises ubuntuuitoolkit.ToolkitException if the mode of the picker
427+ doesn't let select a date.
428+
429+ """
430+ if not self._is_date_picker():
431+ raise _common.ToolkitException(
432+ "Can't pick date. The picker mode is: {!r}.".format(self.mode))
433+ if 'Years' in self.mode:
434+ self._pick_year(date.year)
435+ self.year.wait_for(date.year)
436+ if 'Month' in self.mode:
437+ # Python's date object starts at one. The model in the date picker
438+ # at 0.
439+ self._pick_month(date.month - 1)
440+ self.month.wait_for(date.month - 1)
441+ if 'Day' in self.mode:
442+ self._pick_day(date.day)
443+ self.day.wait_for(date.day)
444+
445+ def _is_date_picker(self):
446+ mode = self.mode
447+ if 'Years' in mode or 'Months' in mode or 'Days' in mode:
448+ return True
449+ else:
450+ return False
451+
452+ @autopilot_logging.log_action(logger.info)
453+ def _pick_year(self, year):
454+ picker = self.select_single(
455+ 'Picker', objectName='PickerRow_YearPicker')
456+ list_view = picker.select_single(
457+ _qquicklistview.QQuickListView, objectName='Picker_Linear')
458+ self._pick_date_value(self.year, year, list_view)
459+
460+ @autopilot_logging.log_action(logger.info)
461+ def _pick_month(self, month):
462+ picker = self.select_single(
463+ 'Picker', objectName='PickerRow_MonthPicker')
464+ path_view = picker.select_single(
465+ QQuickPathView, objectName='Picker_WrapAround')
466+ self._pick_date_value(self.month, month, path_view)
467+
468+ @autopilot_logging.log_action(logger.info)
469+ def _pick_day(self, day):
470+ picker = self.select_single(
471+ 'Picker', objectName='PickerRow_DayPicker')
472+ path_view = picker.select_single(
473+ QQuickPathView, objectName='Picker_WrapAround')
474+ # Python's date object starts at one. The model in the date picker
475+ # at 0.
476+ self._pick_date_value(self.get_date().day - 1, day - 1, path_view)
477+
478+ def _pick_date_value(self, current_value, new_value, scrollable):
479+ if new_value > current_value:
480+ direction = 'below'
481+ elif new_value < current_value:
482+ direction = 'above'
483+ else:
484+ logger.debug('The value is already selected.')
485+ return
486+ scrollable.click_element(
487+ object_name='PickerRow_PickerLabel{}'.format(new_value),
488+ direction=direction)
489+
490+ def get_date(self):
491+ """Return the currently selected date.
492+
493+ :return: a python datetime.date object with the selected date.
494+
495+ """
496+ # Python's date object starts at one. The model in the date picker
497+ # at 0.
498+ return datetime.date(
499+ self.year, self.month + 1, self.day)
500+
501+
502+class QQuickPathView(_flickable.Scrollable):
503+
504+ # TODO make it more general and move it to its own module.
505+ # --elopio - 2014-05-06
506+
507+ @autopilot_logging.log_action(logger.info)
508+ def click_element(self, object_name, direction='below'):
509+ try:
510+ element = self.select_single(objectName=object_name)
511+ except dbus.StateNotFoundError:
512+ # The element might be on a part of the list that hasn't been
513+ # created yet. We have to search for it scrolling.
514+ element = self._find_element(object_name, direction)
515+ self.swipe_child_into_view(element)
516+ self.pointing_device.click_object(element)
517+
518+ @autopilot_logging.log_action(logger.info)
519+ def _find_element(self, object_name, direction):
520+ containers = self._get_containers()
521+ for index in range(self.count):
522+ if direction == 'below':
523+ swipe_method = self._swipe_to_show_one_more_below
524+ elif direction == 'above':
525+ swipe_method = self._swipe_to_show_one_more_above
526+ else:
527+ raise _common.ToolkitException(
528+ 'Invalid direction: {}'.format(direction))
529+
530+ swipe_method(containers)
531+
532+ try:
533+ return self.select_single(objectName=object_name)
534+ except dbus.StateNotFoundError:
535+ pass
536+ raise _common.ToolkitException(
537+ 'List element with objectName "{}" not found.'.format(object_name))
538+
539+ @autopilot_logging.log_action(logger.info)
540+ def _swipe_to_show_one_more_above(self, containers):
541+ self._swipe_to_show_one_more('above', containers)
542+
543+ @autopilot_logging.log_action(logger.info)
544+ def _swipe_to_show_one_more_below(self, containers):
545+ self._swipe_to_show_one_more('below', containers)
546+
547+ def _swipe_to_show_one_more(self, direction, containers):
548+ start_x = stop_x = self.globalRect.x + (self.globalRect.width // 2)
549+ center_y = self.globalRect.y + (self.globalRect.height // 2)
550+ # XXX This assumes all the children are of the same height
551+ child = self.get_children_by_type('PickerDelegate')[0]
552+ top = center_y - (child.globalRect.height // 2)
553+ bottom = center_y + (child.globalRect.height // 2)
554+ if direction == 'below':
555+ start_y = bottom
556+ stop_y = top
557+ elif direction == 'above':
558+ start_y = top
559+ stop_y = bottom
560+ else:
561+ raise _common.ToolkitException(
562+ 'Invalid direction {}.'.format(direction))
563+ self._slow_drag(start_x, stop_x, start_y, stop_y)
564+ self.dragging.wait_for(False)
565+ self.moving.wait_for(False)
566+
567+ @autopilot_logging.log_action(logger.info)
568+ def swipe_child_into_view(self, child):
569+ """Make the child visible.
570+
571+ Currently it works only when the object needs to be swiped vertically.
572+ TODO implement horizontal swiping. --elopio - 2014-03-21
573+
574+ """
575+ containers = self._get_containers()
576+ if not self._is_child_visible(child, containers):
577+ self._swipe_non_visible_child_into_view(child, containers)
578+ else:
579+ logger.debug('The element is already visible.')
580+
581+ @autopilot_logging.log_action(logger.info)
582+ def _swipe_non_visible_child_into_view(self, child, containers):
583+ while not self._is_child_visible(child, containers):
584+ # Check the direction of the swipe based on the position of the
585+ # child relative to the immediate flickable container.
586+ if child.globalRect.y < self.globalRect.y:
587+ self._swipe_to_show_one_more_above(containers)
588+ else:
589+ self._swipe_to_show_one_more_below(containers)
590
591=== modified file 'tests/autopilot/ubuntuuitoolkit/emulators.py'
592--- tests/autopilot/ubuntuuitoolkit/emulators.py 2014-04-25 18:39:51 +0000
593+++ tests/autopilot/ubuntuuitoolkit/emulators.py 2014-05-09 04:12:47 +0000
594@@ -36,12 +36,12 @@
595 'CheckBox',
596 'ComposerSheet',
597 'Empty',
598- 'Flickable',
599 'Header',
600 'ItemSelector',
601 'MainView',
602 'MultiValue',
603 'OptionSelector',
604+ 'QQuickFlickable',
605 'QQuickListView',
606 'SingleControl',
607 'SingleValue',
608@@ -61,10 +61,10 @@
609 get_keyboard,
610 get_pointing_device,
611 CheckBox,
612- Flickable,
613 Header,
614 MainView,
615 OptionSelector,
616+ QQuickFlickable,
617 QQuickListView,
618 TabBar,
619 Tabs,
620
621=== added file 'tests/autopilot/ubuntuuitoolkit/tests/custom_proxy_objects/test_date_picker.py'
622--- tests/autopilot/ubuntuuitoolkit/tests/custom_proxy_objects/test_date_picker.py 1970-01-01 00:00:00 +0000
623+++ tests/autopilot/ubuntuuitoolkit/tests/custom_proxy_objects/test_date_picker.py 2014-05-09 04:12:47 +0000
624@@ -0,0 +1,147 @@
625+# -*- Mode: Python; coding: utf-8; indent-tabs-mode: nil; tab-width: 4 -*-
626+#
627+# Copyright (C) 2014 Canonical Ltd.
628+#
629+# This program is free software; you can redistribute it and/or modify
630+# it under the terms of the GNU Lesser General Public License as published by
631+# the Free Software Foundation; version 3.
632+#
633+# This program is distributed in the hope that it will be useful,
634+# but WITHOUT ANY WARRANTY; without even the implied warranty of
635+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
636+# GNU Lesser General Public License for more details.
637+#
638+# You should have received a copy of the GNU Lesser General Public License
639+# along with this program. If not, see <http://www.gnu.org/licenses/>.
640+
641+import datetime
642+
643+import ubuntuuitoolkit
644+from ubuntuuitoolkit import pickers, tests
645+
646+
647+class DatePickerBaseTestCase(tests.QMLStringAppTestCase):
648+
649+ test_qml = ("""
650+import QtQuick 2.0
651+import Ubuntu.Components 1.1
652+import Ubuntu.Components.Pickers 1.0
653+
654+MainView {
655+ width: units.gu(48)
656+ height: units.gu(60)
657+
658+ Column {
659+ DatePicker {
660+ id: datePicker
661+ objectName: 'datePicker'
662+ mode: 'Years|Months|Days'
663+ date: {
664+ var d = new Date()
665+ // Make sure that the picker will have higher and lower values
666+ // to select.
667+ d.setFullYear(d.getFullYear() + 25)
668+ d.setMonth('5')
669+ d.setDate('15')
670+ return d
671+ }
672+ }
673+ DatePicker {
674+ id: timePicker
675+ objectName: 'timePicker'
676+ mode: 'Hours|Minutes|Seconds'
677+ }
678+ }
679+}
680+""")
681+
682+ def setUp(self):
683+ super(DatePickerBaseTestCase, self).setUp()
684+ self.date_picker = self.main_view.select_single(
685+ pickers.DatePicker, objectName='datePicker')
686+
687+
688+class DatePickerTestCase(DatePickerBaseTestCase):
689+
690+ def test_select_date_picker_must_return_custom_proxy_object(self):
691+ self.assertIsInstance(
692+ self.date_picker, pickers.DatePicker)
693+
694+ def test_pick_date_on_time_picker_must_raise_exception(self):
695+ time_picker = self.main_view.select_single(
696+ pickers.DatePicker, objectName='timePicker')
697+ error = self.assertRaises(
698+ ubuntuuitoolkit.ToolkitException, time_picker.pick_date, 'dummy')
699+ self.assertEqual(
700+ str(error),
701+ "Can't pick date. The picker mode is: {!r}.".format(
702+ time_picker.mode))
703+
704+ def test_swipe_to_show_one_more_below_must_select_next_index(self):
705+ """Test that we don't end up swiping more than needed.
706+
707+ This would cause us to miss the element we are looking for, and to have
708+ to swipe many times in order to finally click it.
709+
710+ """
711+ picker = self.main_view.select_single(
712+ 'Picker', objectName='PickerRow_DayPicker')
713+ path_view = picker.select_single(
714+ pickers.QQuickPathView, objectName='Picker_WrapAround')
715+ current_index = path_view.currentIndex
716+
717+ path_view._swipe_to_show_one_more_below(path_view._get_containers())
718+
719+ self.assertEqual(path_view.currentIndex, current_index + 1)
720+
721+ def test_swipe_to_show_one_more_above_must_select_previous_index(self):
722+ """Test that we don't end up swiping more than needed.
723+
724+ This would cause us to miss the element we are looking for, and to have
725+ to swipe many times in order to finally click it.
726+
727+ """
728+ picker = self.main_view.select_single(
729+ 'Picker', objectName='PickerRow_DayPicker')
730+ path_view = picker.select_single(
731+ pickers.QQuickPathView, objectName='Picker_WrapAround')
732+ current_index = path_view.currentIndex
733+
734+ path_view._swipe_to_show_one_more_above(path_view._get_containers())
735+
736+ self.assertEqual(path_view.currentIndex, current_index - 1)
737+
738+
739+class PickDateFromDatePickerTestCase(DatePickerBaseTestCase):
740+
741+ SELECTED_YEAR = datetime.date.today().year + 25
742+ SELECTED_MONTH = 6 # June
743+ SELECTED_DAY = 15
744+
745+ scenarios = [
746+ ('higher year', {
747+ 'date_to_pick': datetime.date(
748+ SELECTED_YEAR + 10, SELECTED_MONTH, SELECTED_DAY)}),
749+ ('lower year', {
750+ 'date_to_pick': datetime.date(
751+ SELECTED_YEAR - 10, SELECTED_MONTH, SELECTED_DAY)}),
752+ ('higher month', {
753+ 'date_to_pick': datetime.date(
754+ SELECTED_YEAR, SELECTED_MONTH + 4, SELECTED_DAY)}),
755+ ('lower month', {
756+ 'date_to_pick': datetime.date(
757+ SELECTED_YEAR, SELECTED_MONTH - 4, SELECTED_DAY)}),
758+ ('higher day', {
759+ 'date_to_pick': datetime.date(
760+ SELECTED_YEAR, SELECTED_MONTH, SELECTED_DAY + 10)}),
761+ ('lower day', {
762+ 'date_to_pick': datetime.date(
763+ SELECTED_YEAR, SELECTED_MONTH, SELECTED_DAY - 10)}),
764+ ('change all value', {
765+ 'date_to_pick': datetime.date(
766+ SELECTED_YEAR - 10, SELECTED_MONTH + 4, SELECTED_DAY - 10)}),
767+ ]
768+
769+ def test_pick_date(self):
770+ self.date_picker.pick_date(self.date_to_pick)
771+ self.assertEqual(self.date_picker.get_date(), self.date_to_pick)
772
773=== added file 'tests/autopilot/ubuntuuitoolkit/tests/custom_proxy_objects/test_flickable.py'
774--- tests/autopilot/ubuntuuitoolkit/tests/custom_proxy_objects/test_flickable.py 1970-01-01 00:00:00 +0000
775+++ tests/autopilot/ubuntuuitoolkit/tests/custom_proxy_objects/test_flickable.py 2014-05-09 04:12:47 +0000
776@@ -0,0 +1,197 @@
777+# -*- Mode: Python; coding: utf-8; indent-tabs-mode: nil; tab-width: 4 -*-
778+#
779+# Copyright (C) 2014 Canonical Ltd.
780+#
781+# This program is free software; you can redistribute it and/or modify
782+# it under the terms of the GNU Lesser General Public License as published by
783+# the Free Software Foundation; version 3.
784+#
785+# This program is distributed in the hope that it will be useful,
786+# but WITHOUT ANY WARRANTY; without even the implied warranty of
787+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
788+# GNU Lesser General Public License for more details.
789+#
790+# You should have received a copy of the GNU Lesser General Public License
791+# along with this program. If not, see <http://www.gnu.org/licenses/>.
792+
793+
794+import testtools
795+
796+import ubuntuuitoolkit
797+from ubuntuuitoolkit import tests
798+from ubuntuuitoolkit._custom_proxy_objects import _common
799+
800+
801+class FlickableTestCase(testtools.TestCase):
802+
803+ def test_get_unity_top_container(self):
804+ """Test that we can get the top cointainer in Unity."""
805+ # This tests bug http://pad.lv/1314390
806+ # On Unity, the top container is not the first child as it is in all
807+ # the apps that have a MainView. This makes the first implementation of
808+ # _get_top_container fail. Instead of going from the top looking for
809+ # a container, we should start from the flickable until we find the
810+ # top-most container.
811+ RootClass = type('obj', (object,), {'id': 'root'})
812+ mock_root_instance = RootClass()
813+ # We consider a container is an object with a globalRect.
814+ MockNonContainerClass = type('obj', (object,), {})
815+ mock_non_container = MockNonContainerClass()
816+ MockContainerClass = type(
817+ 'obj', (object,), {'id': 'container', 'globalRect': 'dummy'})
818+ mock_container = MockContainerClass()
819+ mock_container.get_parent = lambda: mock_root_instance
820+
821+ # The root instance has two children. This exposes the bug.
822+ mock_root_instance.get_children = lambda: [
823+ mock_non_container, mock_container]
824+
825+ dummy_state = {'id': '10'}
826+ flickable = ubuntuuitoolkit.QQuickFlickable(
827+ dummy_state, 'dummy', 'dummy')
828+
829+ flickable.get_root_instance = lambda: mock_root_instance
830+ # The top container of the flickable is its immediate parent.
831+ flickable.get_parent = lambda: mock_container
832+
833+ top_container = flickable._get_top_container()
834+ self.assertEqual(top_container, mock_container)
835+
836+ def test_is_flickable_with_flicking_property_must_return_true(self):
837+ """is_flickable returns True if flickable property exists."""
838+ dummy_id = (0, 0)
839+ dummy_flicking = (0, 'dummy')
840+ state_with_flicking = {'id': dummy_id, 'flicking': dummy_flicking}
841+ element = _common.UbuntuUIToolkitCustomProxyObjectBase(
842+ state_with_flicking, 'dummy', 'dummy')
843+ with element.no_automatic_refreshing():
844+ self.assertTrue(element.is_flickable())
845+
846+ def test_is_flickable_without_flicking_property_must_return_false(self):
847+ """is_flickable returns False if flickable property doesn't exist."""
848+ dummy_id = (0, 0)
849+ state_without_flicking = {'id': dummy_id}
850+ element = _common.UbuntuUIToolkitCustomProxyObjectBase(
851+ state_without_flicking, 'dummy', 'dummy')
852+ with element.no_automatic_refreshing():
853+ self.assertFalse(element.is_flickable())
854+
855+
856+class IsFlickableTestCase(tests.QMLStringAppTestCase):
857+ """Functional test to check that is_flickable returns the right value.
858+
859+ We already have tests for is_flickable with mocks, so here we just check
860+ with some real elements.
861+
862+ """
863+
864+ test_qml = ("""
865+import QtQuick 2.0
866+import Ubuntu.Components 0.1
867+import Ubuntu.Components.ListItems 0.1 as ListItem
868+
869+MainView {
870+ objectName: 'mainView'
871+ width: units.gu(48)
872+ height: units.gu(60)
873+
874+ Flickable {
875+ objectName: 'flickable'
876+ }
877+ ListView {
878+ objectName: 'listView'
879+ }
880+ Label {
881+ objectName: 'label'
882+ }
883+}
884+""")
885+
886+ scenarios = [
887+ ('main view', dict(object_name='mainView', is_flickable=False)),
888+ ('flickable', dict(object_name='flickable', is_flickable=True)),
889+ ('list view', dict(object_name='listView', is_flickable=True)),
890+ ('label', dict(object_name='label', is_flickable=False))
891+ ]
892+
893+ def test_is_flickable(self):
894+ """Test that is_flickable identifies the elements correctly."""
895+ element = self.app.select_single(objectName=self.object_name)
896+ self.assertEqual(element.is_flickable(), self.is_flickable)
897+
898+
899+class SwipeIntoViewTestCase(tests.QMLStringAppTestCase):
900+
901+ test_qml = ("""
902+import QtQuick 2.0
903+import Ubuntu.Components 0.1
904+
905+MainView {
906+ width: units.gu(48)
907+ height: units.gu(60)
908+
909+ Label {
910+ id: clickedLabel
911+ objectName: "clickedLabel"
912+ text: "No element clicked."
913+ }
914+
915+ Flickable {
916+ anchors {
917+ fill: parent
918+ topMargin: clickedLabel.height
919+ // It can't be at the bottom, or the toolbar will be opened
920+ // when we try to click it.
921+ bottomMargin: units.gu(10)
922+ }
923+ objectName: 'flickable'
924+ height: units.gu(60)
925+ contentHeight: bottomButton.y + bottomButton.height
926+
927+ Button {
928+ id: topButton
929+ objectName: 'topButton'
930+ text: 'Top button'
931+ onClicked: clickedLabel.text = objectName
932+ }
933+ Rectangle {
934+ id: emptyRectangle
935+ height: units.gu(80)
936+ anchors.top: topButton.bottom
937+ }
938+ Button {
939+ id: bottomButton
940+ objectName: 'bottomButton'
941+ text: 'Bottom button'
942+ onClicked: clickedLabel.text = objectName
943+ anchors.top: emptyRectangle.bottom
944+ }
945+ }
946+}
947+""")
948+
949+ def setUp(self):
950+ super(SwipeIntoViewTestCase, self).setUp()
951+ self.label = self.main_view.select_single(
952+ 'Label', objectName='clickedLabel')
953+ self.assertEqual(self.label.text, 'No element clicked.')
954+
955+ def test_swipe_to_bottom(self):
956+ self.main_view.close_toolbar()
957+
958+ button = self.main_view.select_single(objectName='bottomButton')
959+ button.swipe_into_view()
960+
961+ self.pointing_device.click_object(button)
962+ self.assertEqual(self.label.text, 'bottomButton')
963+
964+ def test_swipe_to_top(self):
965+ self.main_view.close_toolbar()
966+ bottomButton = self.main_view.select_single(objectName='bottomButton')
967+ bottomButton.swipe_into_view()
968+
969+ topButton = self.main_view.select_single(objectName='topButton')
970+ topButton.swipe_into_view()
971+
972+ self.pointing_device.click_object(topButton)
973+ self.assertEqual(self.label.text, 'topButton')
974
975=== modified file 'tests/autopilot/ubuntuuitoolkit/tests/test_emulators.py'
976--- tests/autopilot/ubuntuuitoolkit/tests/test_emulators.py 2014-04-28 15:39:24 +0000
977+++ tests/autopilot/ubuntuuitoolkit/tests/test_emulators.py 2014-05-09 04:12:47 +0000
978@@ -32,7 +32,7 @@
979
980 symbols_retaining_name = [
981 'check_autopilot_version', 'get_keyboard', 'get_pointing_device',
982- 'CheckBox', 'Flickable', 'Header', 'MainView', 'OptionSelector',
983+ 'CheckBox', 'Header', 'MainView', 'OptionSelector', 'QQuickFlickable',
984 'QQuickListView', 'TabBar', 'Tabs', 'TextField', 'Toolbar',
985 ]
986

Subscribers

People subscribed via source and target branches