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