Merge lp:~nskaggs/ubuntu-calculator-app/ap-fix-missing-keypress into lp:~ubuntu-calculator-dev/ubuntu-calculator-app/old_trunk
- ap-fix-missing-keypress
- Merge into old_trunk
Status: | Merged | ||||||||
---|---|---|---|---|---|---|---|---|---|
Approved by: | Nicholas Skaggs | ||||||||
Approved revision: | 341 | ||||||||
Merged at revision: | 323 | ||||||||
Proposed branch: | lp:~nskaggs/ubuntu-calculator-app/ap-fix-missing-keypress | ||||||||
Merge into: | lp:~ubuntu-calculator-dev/ubuntu-calculator-app/old_trunk | ||||||||
Diff against target: |
1349 lines (+639/-492) 6 files modified
Simple/SimplePage.qml (+1/-0) tests/autopilot/ubuntu_calculator_app/__init__.py (+332/-7) tests/autopilot/ubuntu_calculator_app/emulators.py (+0/-282) tests/autopilot/ubuntu_calculator_app/tests/__init__.py (+156/-72) tests/autopilot/ubuntu_calculator_app/tests/test_screen.py (+91/-0) tests/autopilot/ubuntu_calculator_app/tests/test_simple_page.py (+59/-131) |
||||||||
To merge this branch: | bzr merge lp:~nskaggs/ubuntu-calculator-app/ap-fix-missing-keypress | ||||||||
Related bugs: |
|
Reviewer | Review Type | Date Requested | Status |
---|---|---|---|
PS Jenkins bot | continuous-integration | Approve | |
Leo Arias (community) | Approve | ||
Ubuntu Phone Apps Jenkins Bot | continuous-integration | Approve | |
Review via email: mp+233534@code.launchpad.net |
Commit message
Increase the keypress duration to avoid missing pressing keys.
Description of the change
A simple tweak to increase the keypress duration to avoid missing pressing keys. We should still consider tweaking how we perform operations.
Ubuntu Phone Apps Jenkins Bot (ubuntu-phone-apps-jenkins-bot) wrote : | # |
Ubuntu Phone Apps Jenkins Bot (ubuntu-phone-apps-jenkins-bot) wrote : | # |
PASSED: Continuous integration, rev:324
http://
Executed test runs:
SUCCESS: http://
deb: http://
SUCCESS: http://
Click here to trigger a rebuild:
http://
Ubuntu Phone Apps Jenkins Bot (ubuntu-phone-apps-jenkins-bot) wrote : | # |
PASSED: Continuous integration, rev:325
http://
Executed test runs:
SUCCESS: http://
deb: http://
SUCCESS: http://
Click here to trigger a rebuild:
http://
Leo Arias (elopio) : | # |
Nicholas Skaggs (nskaggs) wrote : | # |
Thanks Leo, left my replies. I'm really trying to not go overboard on redoing this, but it's easy to start that direction.
Nicholas Skaggs (nskaggs) wrote : | # |
All the tests now pass locally and on the device, albeit with the extra attempt. Now to track down why the app isn't capturing the press.
Nicholas Skaggs (nskaggs) wrote : | # |
Note, althought some things are converted and some cleanups done, I attempted to keep this as simple as possible. Another MP can bring this inline with current best practices.
Ubuntu Phone Apps Jenkins Bot (ubuntu-phone-apps-jenkins-bot) wrote : | # |
FAILED: Continuous integration, rev:334
http://
Executed test runs:
UNSTABLE: http://
deb: http://
SUCCESS: http://
Click here to trigger a rebuild:
http://
Ubuntu Phone Apps Jenkins Bot (ubuntu-phone-apps-jenkins-bot) wrote : | # |
FAILED: Continuous integration, rev:335
http://
Executed test runs:
UNSTABLE: http://
deb: http://
SUCCESS: http://
Click here to trigger a rebuild:
http://
Ubuntu Phone Apps Jenkins Bot (ubuntu-phone-apps-jenkins-bot) wrote : | # |
FAILED: Continuous integration, rev:337
http://
Executed test runs:
UNSTABLE: http://
deb: http://
SUCCESS: http://
Click here to trigger a rebuild:
http://
Ubuntu Phone Apps Jenkins Bot (ubuntu-phone-apps-jenkins-bot) wrote : | # |
FAILED: Continuous integration, rev:339
http://
Executed test runs:
None: http://
SUCCESS: http://
Click here to trigger a rebuild:
http://
Ubuntu Phone Apps Jenkins Bot (ubuntu-phone-apps-jenkins-bot) wrote : | # |
PASSED: Continuous integration, rev:340
http://
Executed test runs:
SUCCESS: http://
deb: http://
SUCCESS: http://
Click here to trigger a rebuild:
http://
PS Jenkins bot (ps-jenkins) wrote : | # |
FAILED: Autolanding.
More details in the following jenkins job:
http://
Executed test runs:
FAILURE: http://
SUCCESS: http://
FAILURE: http://
SUCCESS: http://
SUCCESS: http://
PS Jenkins bot (ps-jenkins) wrote : | # |
FAILED: Autolanding.
More details in the following jenkins job:
http://
Executed test runs:
FAILURE: http://
SUCCESS: http://
FAILURE: http://
SUCCESS: http://
SUCCESS: http://
PS Jenkins bot (ps-jenkins) wrote : | # |
FAILED: Autolanding.
More details in the following jenkins job:
http://
Executed test runs:
FAILURE: http://
SUCCESS: http://
FAILURE: http://
SUCCESS: http://
SUCCESS: http://
Leo Arias (elopio) wrote : | # |
228 + self.pointing_
229 + buttonarea.
230 + self.pointing_
Could you please put a comment there explaining why calling self.pointing_
- 341. By Nicholas Skaggs
-
add comment explaining use of _click_button
Ubuntu Phone Apps Jenkins Bot (ubuntu-phone-apps-jenkins-bot) wrote : | # |
PASSED: Continuous integration, rev:341
http://
Executed test runs:
SUCCESS: http://
deb: http://
SUCCESS: http://
Click here to trigger a rebuild:
http://
Leo Arias (elopio) : | # |
PS Jenkins bot (ps-jenkins) : | # |
Preview Diff
1 | === modified file 'Simple/SimplePage.qml' |
2 | --- Simple/SimplePage.qml 2014-09-02 07:00:29 +0000 |
3 | +++ Simple/SimplePage.qml 2014-09-08 19:35:18 +0000 |
4 | @@ -45,6 +45,7 @@ |
5 | property variant keyboardButtons: null |
6 | |
7 | id: page |
8 | + objectName: "simplepage" |
9 | |
10 | function formulaPush(visual) { |
11 | //If first character in number is dot, then add zero before |
12 | |
13 | === modified file 'tests/autopilot/ubuntu_calculator_app/__init__.py' |
14 | --- tests/autopilot/ubuntu_calculator_app/__init__.py 2013-03-22 12:15:46 +0000 |
15 | +++ tests/autopilot/ubuntu_calculator_app/__init__.py 2014-09-08 19:35:18 +0000 |
16 | @@ -1,8 +1,333 @@ |
17 | # -*- Mode: Python; coding: utf-8; indent-tabs-mode: nil; tab-width: 4 -*- |
18 | -# Copyright 2013 Canonical |
19 | -# |
20 | -# This program is free software: you can redistribute it and/or modify it |
21 | -# under the terms of the GNU General Public License version 3, as published |
22 | -# by the Free Software Foundation. |
23 | - |
24 | -"""Autopilot tests and emulators for calculator - top level package.""" |
25 | +# |
26 | +# Copyright (C) 2013 Canonical Ltd. |
27 | +# |
28 | +# This program is free software; you can redistribute it and/or modify |
29 | +# it under the terms of the GNU Lesser General Public License as published by |
30 | +# the Free Software Foundation; version 3. |
31 | +# |
32 | +# This program is distributed in the hope that it will be useful, |
33 | +# but WITHOUT ANY WARRANTY; without even the implied warranty of |
34 | +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
35 | +# GNU Lesser General Public License for more details. |
36 | +# |
37 | +# You should have received a copy of the GNU Lesser General Public License |
38 | +# along with this program. If not, see <http://www.gnu.org/licenses/>. |
39 | + |
40 | +"""Calculator app autopilot emulators.""" |
41 | + |
42 | +import logging |
43 | + |
44 | +import autopilot.logging |
45 | +from autopilot.introspection import dbus |
46 | + |
47 | +import ubuntuuitoolkit |
48 | + |
49 | +logger = logging.getLogger(__name__) |
50 | + |
51 | + |
52 | +class CalculatorException(ubuntuuitoolkit.ToolkitException): |
53 | + |
54 | + """Exception raised when there are problems with the Calculator.""" |
55 | + |
56 | + |
57 | +class CalculatorApp(object): |
58 | + |
59 | + """Autopilot helper object for the terminal application.""" |
60 | + |
61 | + def __init__(self, app_proxy, test_type): |
62 | + self.app = app_proxy |
63 | + self.test_type = test_type |
64 | + self.main_view = self.app.select_single(MainView) |
65 | + |
66 | + @property |
67 | + def pointing_device(self): |
68 | + return self.app.pointing_device |
69 | + |
70 | + |
71 | +class MainView(ubuntuuitoolkit.MainView): |
72 | + """Calculator MainView Autopilot emulator.""" |
73 | + |
74 | + # TODO when we start the tests for the scientific calculator, many of this |
75 | + # methods will have to be moved to the SimplePage emulator, and the main |
76 | + # view can call them depending on the current page displayed. |
77 | + # --elopio - 2013-08-08 |
78 | + |
79 | + def __init__(self, *args): |
80 | + super(MainView, self).__init__(*args) |
81 | + self.visible.wait_for(True) |
82 | + |
83 | + def get_simple_page(self): |
84 | + return self.select_single('SimplePage', objectName='simplepage') |
85 | + |
86 | + def clear_with_button(self): |
87 | + """Clear the calculation using the clear button.""" |
88 | + self.get_calc_keyboard().click_clear() |
89 | + |
90 | + def get_calc_keyboard(self): |
91 | + """Return the CalcKeyboard autopilot emulator.""" |
92 | + return self.select_single(CalcKeyboard) |
93 | + |
94 | + def calculate_operation(self, operation): |
95 | + """Calculate an operation. |
96 | + |
97 | + :parameter operation: A string with the operation to calculate. |
98 | + |
99 | + """ |
100 | + self.enter_operation(operation) |
101 | + self.get_calc_keyboard().click_equals() |
102 | + |
103 | + def enter_operation(self, operation): |
104 | + """Enter an operation. |
105 | + |
106 | + :parameter operation: A string with the operation to enter. |
107 | + |
108 | + """ |
109 | + self.get_calc_keyboard().enter_operation(operation) |
110 | + |
111 | + def change_sign(self): |
112 | + """Change the sign of the last operand.""" |
113 | + self.get_calc_keyboard().click_sign() |
114 | + |
115 | + def get_result(self): |
116 | + """Return the result of a calculation.""" |
117 | + return self.get_current_screen().get_result() |
118 | + |
119 | + def get_current_screen(self): |
120 | + """Return the screen for the current calculation.""" |
121 | + count = self.get_number_of_screens() |
122 | + return self.select_many(Screen)[count - 1] |
123 | + |
124 | + def get_operand_by_index(self, index): |
125 | + """Return an operand of a calculation entered.""" |
126 | + return self.get_current_screen().get_operand_by_index(index) |
127 | + |
128 | + def clear_with_swipe(self): |
129 | + """Clear the calculation making a swipe on the screen.""" |
130 | + self.get_current_screen().swipe_up() |
131 | + self._wait_for_screen_to_finish_animation() |
132 | + |
133 | + def _wait_for_screen_to_finish_animation(self): |
134 | + qquicklistview = self.select_single('QQuickListView') |
135 | + qquicklistview.moving.wait_for(False) |
136 | + |
137 | + def is_cleared(self): |
138 | + """Return True if the screen has no calculation. False otherwise.""" |
139 | + return self.get_current_screen().is_cleared() |
140 | + |
141 | + def get_screen_by_index(self, index): |
142 | + """Return a screen.""" |
143 | + return self._get_screens()[index] |
144 | + |
145 | + def _get_screens(self): |
146 | + return self.select_many(Screen) |
147 | + |
148 | + def get_number_of_screens(self): |
149 | + """Return the number of screens.""" |
150 | + return len(self._get_screens()) |
151 | + |
152 | + def swipe_previous_calculation_into_view(self, screen_index): |
153 | + previous_screen = self.get_screen_by_index(screen_index) |
154 | + _, prev_screen_y, _, prev_screen_h = previous_screen.globalRect |
155 | + x, y, w, h = self.globalRect |
156 | + start_x = stop_x = x + w / 2 |
157 | + start_y = y + h / 2 |
158 | + stop_y = start_y + y - prev_screen_y |
159 | + self.pointing_device.drag(start_x, start_y, stop_x, stop_y) |
160 | + self._wait_for_screen_to_finish_animation() |
161 | + |
162 | + def delete_previous_calculation(self, screen_index, confirm): |
163 | + """Delete an old calculation. |
164 | + |
165 | + :parameter screen_index: If index of the screen with the calculation |
166 | + to delete. |
167 | + :parameter confirm: If True, the deletion will be confimed. If False, |
168 | + it will be canceled. |
169 | + |
170 | + """ |
171 | + self.swipe_previous_calculation_into_view(screen_index) |
172 | + screen = self.get_screen_by_index(screen_index) |
173 | + screen.swipe_to_delete() |
174 | + self._wait_for_screen_to_finish_animation() |
175 | + if confirm: |
176 | + screen.confirm_removal() |
177 | + else: |
178 | + raise NotImplementedError('Not yet implemented.') |
179 | + |
180 | + |
181 | +class CalcKeyboard(ubuntuuitoolkit.UbuntuUIToolkitCustomProxyObjectBase): |
182 | + """CalcKeyboard Autopilot emulator.""" |
183 | + |
184 | + _BUTTONS_DICT = { |
185 | + "0": "zeroButton", |
186 | + "1": "oneButton", |
187 | + "2": "twoButton", |
188 | + "3": "threeButton", |
189 | + "4": "fourButton", |
190 | + "5": "fiveButton", |
191 | + "6": "sixButton", |
192 | + "7": "sevenButton", |
193 | + "8": "eightButton", |
194 | + "9": "nineButton", |
195 | + "+": "plusButton", |
196 | + "-": "minusButton", |
197 | + "/": "divideButton", |
198 | + "*": "multiplyButton", |
199 | + '.': "pointButton", |
200 | + } |
201 | + |
202 | + def __init__(self, *args): |
203 | + super(CalcKeyboard, self).__init__(*args) |
204 | + |
205 | + @autopilot.logging.log_action(logger.info) |
206 | + def click_clear(self): |
207 | + """Click the clear button.""" |
208 | + clear_button = self._get_keyboard_button('clearButton') |
209 | + self._click_button(clear_button) |
210 | + |
211 | + def _get_keyboard_button(self, objectName): |
212 | + return self.select_single('KeyboardButton', objectName=objectName) |
213 | + |
214 | + def enter_operation(self, operation): |
215 | + """Enter an operation on the calculator. |
216 | + |
217 | + :parameter operation: A string with the operation to enter. |
218 | + |
219 | + """ |
220 | + for char in operation: |
221 | + button = self._get_keyboard_button(self._BUTTONS_DICT[char]) |
222 | + self._click_button(button) |
223 | + |
224 | + def _click_button(self, button): |
225 | + logger.debug("Pressing %s button", button.text) |
226 | + buttonarea = button.select_single('QQuickMouseArea') |
227 | + self.pointing_device.move_to_object(button) |
228 | + # we use press and release so we can check the qml property |
229 | + # and ensure the button is pressed long enough to be recieved |
230 | + # and processed correctly |
231 | + # using a larger press_duration for click_object would be inferior |
232 | + # as it would cause longer delays (we are forced to arbitrarily decide |
233 | + # how long to press each time) and potentially fail. |
234 | + # Also, https://bugs.launchpad.net/autopilot/+bug/1366949 |
235 | + # causes press_duration argument to be ignored currently |
236 | + # balloons 2014-09-08 |
237 | + self.pointing_device.press() |
238 | + buttonarea.pressed.wait_for(True) |
239 | + self.pointing_device.release() |
240 | + |
241 | + @autopilot.logging.log_action(logger.info) |
242 | + def click_equals(self): |
243 | + """Click the equals button.""" |
244 | + equals_button = self._get_keyboard_button('equalsButton') |
245 | + self._click_button(equals_button) |
246 | + |
247 | + @autopilot.logging.log_action(logger.info) |
248 | + def click_sign(self): |
249 | + """Click the sign button.""" |
250 | + sign_button = self._get_keyboard_button('signButton') |
251 | + self._click_button(sign_button) |
252 | + |
253 | + |
254 | +class Screen(ubuntuuitoolkit.listitems.Standard): |
255 | + """Screen Autopilot emulator.""" |
256 | + |
257 | + def __init__(self, *args): |
258 | + super(Screen, self).__init__(*args) |
259 | + |
260 | + def _wait_for_screen_to_finish_animation(self): |
261 | + flickables = self.select_many('QQuickFlickable') |
262 | + for animation in flickables: |
263 | + animation.moving.wait_for(False) |
264 | + |
265 | + def _click_screen_object(self, screen_object): |
266 | + self._wait_for_screen_to_finish_animation() |
267 | + self.pointing_device.click_object(screen_object) |
268 | + |
269 | + def get_result(self): |
270 | + """Return the result of a calculation.""" |
271 | + result_label = self.get_result_label() |
272 | + assert result_label is not None, 'No result displayed.' |
273 | + return result_label.numbers |
274 | + |
275 | + def get_result_label(self): |
276 | + try: |
277 | + return self.select_single( |
278 | + 'CalcLabel', objectName='result', isLast=True) |
279 | + except dbus.StateNotFoundError: |
280 | + results = self.select_many('CalcLabel', objectName='result') |
281 | + count = len(results) |
282 | + if count > 0: |
283 | + return results[count - 1] |
284 | + else: |
285 | + return None |
286 | + |
287 | + @autopilot.logging.log_action(logger.info) |
288 | + def click_result_label(self): |
289 | + result = self.get_result_label() |
290 | + self._click_screen_object(result) |
291 | + |
292 | + def get_edit_icon(self): |
293 | + results = self.select_many('QQuickItem', objectName='editIcon') |
294 | + count = len(results) |
295 | + if count > 0: |
296 | + return results[count - 1] |
297 | + else: |
298 | + return None |
299 | + |
300 | + @autopilot.logging.log_action(logger.info) |
301 | + def click_edit_icon(self): |
302 | + result = self.get_edit_icon() |
303 | + self._click_screen_object(result) |
304 | + |
305 | + def get_title_label(self): |
306 | + return self.select_single('TextField', objectName='title') |
307 | + |
308 | + @autopilot.logging.log_action(logger.info) |
309 | + def click_title_label(self): |
310 | + text_label = self.get_title_label() |
311 | + self._click_screen_object(text_label) |
312 | + |
313 | + def get_operand_by_index(self, index): |
314 | + """Return an operand of a calculation entered.""" |
315 | + operand_label = self.select_single( |
316 | + 'CalcLabel', objectName='operand{0}'.format(index)) |
317 | + assert operand_label is not None, 'No operand with index {0}.'.format( |
318 | + index) |
319 | + return operand_label.numbers |
320 | + |
321 | + def swipe_up(self): |
322 | + """Swipe up on the screen to tear off the calculation.""" |
323 | + x, y, w, h = self.globalRect |
324 | + tx = x + (w / 2) |
325 | + ty = y + (h / 2) |
326 | + |
327 | + self.pointing_device.drag(tx, ty + h, tx, ty - h) |
328 | + |
329 | + def is_cleared(self): |
330 | + """Return True if the screen has no calculation. False otherwise.""" |
331 | + return (self.get_result_label() is None and |
332 | + self._get_number_of_labels() == 1 and |
333 | + self.get_operand_by_index(0) == "") |
334 | + |
335 | + def _get_number_of_labels(self): |
336 | + return len(self.select_many('CalcLabel')) |
337 | + |
338 | + @autopilot.logging.log_action(logger.info) |
339 | + def swipe_to_delete(self): |
340 | + """Swipe the screen to delete the calculation.""" |
341 | + # We override it because the left side of the screen is used to |
342 | + # add labels, so the swipe has to start where the operation starts. |
343 | + # This is hard for a user to do, and weird for automation, which means |
344 | + # it's an usability issue. See http://pad.lv/1329536 |
345 | + self._drag_pointing_device_to_delete() |
346 | + self.waitingConfirmationForRemoval.wait_for(True) |
347 | + |
348 | + def _drag_pointing_device_to_delete(self): |
349 | + x, y, width, height = self.globalRect |
350 | + left_x = x + (width * 0.33) |
351 | + right_x = x + (width * 0.9) |
352 | + start_y = stop_y = y + (height // 2) |
353 | + |
354 | + start_x = left_x |
355 | + stop_x = right_x |
356 | + self.pointing_device.drag(start_x, start_y, stop_x, stop_y) |
357 | |
358 | === removed file 'tests/autopilot/ubuntu_calculator_app/emulators.py' |
359 | --- tests/autopilot/ubuntu_calculator_app/emulators.py 2014-06-12 22:25:46 +0000 |
360 | +++ tests/autopilot/ubuntu_calculator_app/emulators.py 1970-01-01 00:00:00 +0000 |
361 | @@ -1,282 +0,0 @@ |
362 | -# -*- Mode: Python; coding: utf-8; indent-tabs-mode: nil; tab-width: 4 -*- |
363 | -# |
364 | -# Copyright (C) 2013 Canonical Ltd. |
365 | -# |
366 | -# This program is free software; you can redistribute it and/or modify |
367 | -# it under the terms of the GNU Lesser General Public License as published by |
368 | -# the Free Software Foundation; version 3. |
369 | -# |
370 | -# This program is distributed in the hope that it will be useful, |
371 | -# but WITHOUT ANY WARRANTY; without even the implied warranty of |
372 | -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
373 | -# GNU Lesser General Public License for more details. |
374 | -# |
375 | -# You should have received a copy of the GNU Lesser General Public License |
376 | -# along with this program. If not, see <http://www.gnu.org/licenses/>. |
377 | - |
378 | -"""Calculator app autopilot emulators.""" |
379 | - |
380 | -import logging |
381 | - |
382 | -import autopilot.logging |
383 | -from autopilot.introspection import dbus |
384 | - |
385 | -from ubuntuuitoolkit import emulators as toolkit_emulators |
386 | - |
387 | - |
388 | -logger = logging.getLogger(__name__) |
389 | - |
390 | - |
391 | -class MainView(toolkit_emulators.MainView): |
392 | - """Calculator MainView Autopilot emulator.""" |
393 | - |
394 | - # TODO when we start the tests for the scientific calculator, many of this |
395 | - # methods will have to be moved to the SimplePage emulator, and the main |
396 | - # view can call them depending on the current page displayed. |
397 | - # --elopio - 2013-08-08 |
398 | - |
399 | - def __init__(self, *args): |
400 | - super(MainView, self).__init__(*args) |
401 | - self.pointing_device = toolkit_emulators.get_pointing_device() |
402 | - |
403 | - def clear_with_button(self): |
404 | - """Clear the calculation using the clear button.""" |
405 | - self.get_calc_keyboard().click_clear() |
406 | - |
407 | - def get_calc_keyboard(self): |
408 | - """Return the CalcKeyboard autopilot emulator.""" |
409 | - return self.select_single(CalcKeyboard) |
410 | - |
411 | - def calculate_operation(self, operation): |
412 | - """Calculate an operation. |
413 | - |
414 | - :parameter operation: A string with the operation to calculate. |
415 | - |
416 | - """ |
417 | - self.enter_operation(operation) |
418 | - self.get_calc_keyboard().click_equals() |
419 | - |
420 | - def enter_operation(self, operation): |
421 | - """Enter an operation. |
422 | - |
423 | - :parameter operation: A string with the operation to enter. |
424 | - |
425 | - """ |
426 | - self.get_calc_keyboard().enter_operation(operation) |
427 | - |
428 | - def change_sign(self): |
429 | - """Change the sign of the last operand.""" |
430 | - self.get_calc_keyboard().click_sign() |
431 | - |
432 | - def get_result(self): |
433 | - """Return the result of a calculation.""" |
434 | - return self.get_current_screen().get_result() |
435 | - |
436 | - def get_current_screen(self): |
437 | - """Return the screen for the current calculation.""" |
438 | - count = self.get_number_of_screens() |
439 | - return self.select_many(Screen)[count - 1] |
440 | - |
441 | - def get_operand_by_index(self, index): |
442 | - """Return an operand of a calculation entered.""" |
443 | - return self.get_current_screen().get_operand_by_index(index) |
444 | - |
445 | - def clear_with_swipe(self): |
446 | - """Clear the calculation making a swipe on the screen.""" |
447 | - self.get_current_screen().swipe_up() |
448 | - self._wait_for_screen_to_finish_animation() |
449 | - |
450 | - def _wait_for_screen_to_finish_animation(self): |
451 | - qquicklistview = self.select_single('QQuickListView') |
452 | - qquicklistview.moving.wait_for(False) |
453 | - |
454 | - def is_cleared(self): |
455 | - """Return True if the screen has no calculation. False otherwise.""" |
456 | - return self.get_current_screen().is_cleared() |
457 | - |
458 | - def get_screen_by_index(self, index): |
459 | - """Return a screen.""" |
460 | - return self._get_screens()[index] |
461 | - |
462 | - def _get_screens(self): |
463 | - return self.select_many(Screen) |
464 | - |
465 | - def get_number_of_screens(self): |
466 | - """Return the number of screens.""" |
467 | - return len(self._get_screens()) |
468 | - |
469 | - def swipe_previous_calculation_into_view(self, screen_index): |
470 | - previous_screen = self.get_screen_by_index(screen_index) |
471 | - _, prev_screen_y, _, prev_screen_h = previous_screen.globalRect |
472 | - x, y, w, h = self.globalRect |
473 | - start_x = stop_x = x + w / 2 |
474 | - start_y = y + h / 2 |
475 | - stop_y = start_y + y - prev_screen_y |
476 | - self.pointing_device.drag(start_x, start_y, stop_x, stop_y) |
477 | - self._wait_for_screen_to_finish_animation() |
478 | - |
479 | - def delete_previous_calculation(self, screen_index, confirm): |
480 | - """Delete an old calculation. |
481 | - |
482 | - :parameter screen_index: If index of the screen with the calculation |
483 | - to delete. |
484 | - :parameter confirm: If True, the deletion will be confimed. If False, |
485 | - it will be canceled. |
486 | - |
487 | - """ |
488 | - self.swipe_previous_calculation_into_view(screen_index) |
489 | - screen = self.get_screen_by_index(screen_index) |
490 | - screen.swipe_to_delete() |
491 | - self._wait_for_screen_to_finish_animation() |
492 | - if confirm: |
493 | - screen.confirm_removal() |
494 | - else: |
495 | - raise NotImplementedError('Not yet implemented.') |
496 | - |
497 | - |
498 | -class CalcKeyboard(toolkit_emulators.UbuntuUIToolkitEmulatorBase): |
499 | - """CalcKeyboard Autopilot emulator.""" |
500 | - |
501 | - _BUTTONS_DICT = { |
502 | - "0": "zeroButton", |
503 | - "1": "oneButton", |
504 | - "2": "twoButton", |
505 | - "3": "threeButton", |
506 | - "4": "fourButton", |
507 | - "5": "fiveButton", |
508 | - "6": "sixButton", |
509 | - "7": "sevenButton", |
510 | - "8": "eightButton", |
511 | - "9": "nineButton", |
512 | - "+": "plusButton", |
513 | - "-": "minusButton", |
514 | - "/": "divideButton", |
515 | - "*": "multiplyButton", |
516 | - '.': "pointButton", |
517 | - } |
518 | - |
519 | - def __init__(self, *args): |
520 | - super(CalcKeyboard, self).__init__(*args) |
521 | - self.pointing_device = toolkit_emulators.get_pointing_device() |
522 | - |
523 | - def click_clear(self): |
524 | - """Click the clear button.""" |
525 | - clear_button = self._get_keyboard_button('clearButton') |
526 | - self.pointing_device.click_object(clear_button) |
527 | - |
528 | - def _get_keyboard_button(self, objectName): |
529 | - return self.select_single('KeyboardButton', objectName=objectName) |
530 | - |
531 | - def enter_operation(self, operation): |
532 | - """Enter an operation on the calculator. |
533 | - |
534 | - :parameter operation: A string with the operation to enter. |
535 | - |
536 | - """ |
537 | - for char in operation: |
538 | - button = self._get_keyboard_button(self._BUTTONS_DICT[char]) |
539 | - self.pointing_device.click_object(button) |
540 | - |
541 | - def click_equals(self): |
542 | - """Click the equals button.""" |
543 | - equals_button = self._get_keyboard_button('equalsButton') |
544 | - self.pointing_device.click_object(equals_button) |
545 | - |
546 | - def click_sign(self): |
547 | - """Click the sign button.""" |
548 | - sign_button = self._get_keyboard_button('signButton') |
549 | - self.pointing_device.click_object(sign_button) |
550 | - |
551 | - |
552 | -class Screen(toolkit_emulators.Standard): |
553 | - """Screen Autopilot emulator.""" |
554 | - |
555 | - def __init__(self, *args): |
556 | - super(Screen, self).__init__(*args) |
557 | - self.pointing_device = toolkit_emulators.get_pointing_device() |
558 | - |
559 | - def get_result(self): |
560 | - """Return the result of a calculation.""" |
561 | - result_label = self.get_result_label() |
562 | - assert result_label is not None, 'No result displayed.' |
563 | - return result_label.numbers |
564 | - |
565 | - def get_result_label(self): |
566 | - try: |
567 | - return self.select_single( |
568 | - 'CalcLabel', objectName='result', isLast=True) |
569 | - except dbus.StateNotFoundError: |
570 | - results = self.select_many('CalcLabel', objectName='result') |
571 | - count = len(results) |
572 | - if count > 0: |
573 | - return results[count - 1] |
574 | - else: |
575 | - return None |
576 | - |
577 | - def click_result_label(self): |
578 | - result = self.get_result_label() |
579 | - self.pointing_device.click_object(result) |
580 | - |
581 | - def get_edit_icon(self): |
582 | - results = self.select_many('QQuickItem', objectName='editIcon') |
583 | - count = len(results) |
584 | - if count > 0: |
585 | - return results[count - 1] |
586 | - else: |
587 | - return None |
588 | - |
589 | - def click_edit_icon(self): |
590 | - result = self.get_edit_icon() |
591 | - self.pointing_device.click_object(result) |
592 | - |
593 | - def get_title_label(self): |
594 | - return self.select_single('TextField', objectName='title') |
595 | - |
596 | - def click_title_label(self): |
597 | - text_label = self.get_title_label() |
598 | - self.pointing_device.click_object(text_label) |
599 | - |
600 | - def get_operand_by_index(self, index): |
601 | - """Return an operand of a calculation entered.""" |
602 | - operand_label = self.select_single( |
603 | - 'CalcLabel', objectName='operand{0}'.format(index)) |
604 | - assert operand_label is not None, 'No operand with index {0}.'.format( |
605 | - index) |
606 | - return operand_label.numbers |
607 | - |
608 | - def swipe_up(self): |
609 | - """Swipe up on the screen to tear off the calculation.""" |
610 | - x, y, w, h = self.globalRect |
611 | - tx = x + (w / 2) |
612 | - ty = y + (h / 2) |
613 | - |
614 | - self.pointing_device.drag(tx, ty + h, tx, ty - h) |
615 | - |
616 | - def is_cleared(self): |
617 | - """Return True if the screen has no calculation. False otherwise.""" |
618 | - return (self.get_result_label() is None and |
619 | - self._get_number_of_labels() == 1 and |
620 | - self.get_operand_by_index(0) == "") |
621 | - |
622 | - def _get_number_of_labels(self): |
623 | - return len(self.select_many('CalcLabel')) |
624 | - |
625 | - @autopilot.logging.log_action(logger.info) |
626 | - def swipe_to_delete(self): |
627 | - """Swipe the screen to delete the calculation.""" |
628 | - # We override it because the left side of the screen is used to |
629 | - # add labels, so the swipe has to start where the operation starts. |
630 | - # This is hard for a user to do, and weird for automation, which means |
631 | - # it's an usability issue. See http://pad.lv/1329536 |
632 | - self._drag_pointing_device_to_delete() |
633 | - self.waitingConfirmationForRemoval.wait_for(True) |
634 | - |
635 | - def _drag_pointing_device_to_delete(self): |
636 | - x, y, width, height = self.globalRect |
637 | - left_x = x + (width * 0.33) |
638 | - right_x = x + (width * 0.9) |
639 | - start_y = stop_y = y + (height // 2) |
640 | - |
641 | - start_x = left_x |
642 | - stop_x = right_x |
643 | - self.pointing_device.drag(start_x, start_y, stop_x, stop_y) |
644 | |
645 | === modified file 'tests/autopilot/ubuntu_calculator_app/tests/__init__.py' |
646 | --- tests/autopilot/ubuntu_calculator_app/tests/__init__.py 2014-01-17 20:50:29 +0000 |
647 | +++ tests/autopilot/ubuntu_calculator_app/tests/__init__.py 2014-09-08 19:35:18 +0000 |
648 | @@ -1,102 +1,186 @@ |
649 | # -*- Mode: Python; coding: utf-8; indent-tabs-mode: nil; tab-width: 4 -*- |
650 | -# Copyright 2013 Canonical |
651 | -# |
652 | -# This program is free software: you can redistribute it and/or modify it |
653 | -# under the terms of the GNU General Public License version 3, as published |
654 | -# by the Free Software Foundation. |
655 | +# |
656 | +# Copyright (C) 2013, 2014 Canonical Ltd |
657 | +# |
658 | +# This program is free software: you can redistribute it and/or modify |
659 | +# it under the terms of the GNU General Public License version 3 as |
660 | +# published by the Free Software Foundation. |
661 | +# |
662 | +# This program is distributed in the hope that it will be useful, |
663 | +# but WITHOUT ANY WARRANTY; without even the implied warranty of |
664 | +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
665 | +# GNU General Public License for more details. |
666 | +# |
667 | +# You should have received a copy of the GNU General Public License |
668 | +# along with this program. If not, see <http://www.gnu.org/licenses/>. |
669 | |
670 | -"""Calculator autopilot tests.""" |
671 | +"""Calculator app autopilot tests.""" |
672 | |
673 | import os |
674 | import shutil |
675 | import logging |
676 | |
677 | +import fixtures |
678 | +import ubuntu_calculator_app |
679 | + |
680 | from autopilot.testcase import AutopilotTestCase |
681 | +from autopilot import logging as autopilot_logging |
682 | + |
683 | +import ubuntuuitoolkit |
684 | from ubuntuuitoolkit import ( |
685 | base, |
686 | - emulators as toolkit_emulators |
687 | + fixture_setup as toolkit_fixtures |
688 | ) |
689 | |
690 | -from ubuntu_calculator_app import emulators |
691 | - |
692 | logger = logging.getLogger(__name__) |
693 | |
694 | |
695 | -class CalculatorTestCase(AutopilotTestCase): |
696 | +class BaseTestCaseWithPatchedHome(AutopilotTestCase): |
697 | |
698 | - """A common testcase class that provides useful methods for calculator |
699 | - app. |
700 | + """A common test case class that provides several useful methods for |
701 | + ubuntu-calculator-app tests. |
702 | |
703 | """ |
704 | - local_location = "../../ubuntu-calculator-app.qml" |
705 | - installed_location = "/usr/share/ubuntu-calculator-app/" \ |
706 | - "ubuntu-calculator-app.qml" |
707 | - |
708 | - sqlite_dir = os.path.expanduser( |
709 | - "~/.local/share/com.ubuntu.calculator/Databases") |
710 | - backup_dir = sqlite_dir + ".backup" |
711 | + |
712 | + local_location = os.path.dirname(os.path.dirname(os.getcwd())) |
713 | + local_location_qml = os.path.join(local_location, |
714 | + 'ubuntu-calculator-app.qml') |
715 | + installed_location_qml = os.path.join('/usr/share/ubuntu-calculator-app/', |
716 | + 'ubuntu-calculator-app.qml') |
717 | + |
718 | + def get_launcher_and_type(self): |
719 | + if os.path.exists(self.local_location_qml): |
720 | + launcher = self.launch_test_local |
721 | + test_type = 'local' |
722 | + elif os.path.exists(self.installed_location_qml): |
723 | + launcher = self.launch_test_installed |
724 | + test_type = 'deb' |
725 | + else: |
726 | + launcher = self.launch_test_click |
727 | + test_type = 'click' |
728 | + return launcher, test_type |
729 | |
730 | def setUp(self): |
731 | - self.pointing_device = toolkit_emulators.get_pointing_device() |
732 | - super(CalculatorTestCase, self).setUp() |
733 | - self.temp_move_sqlite_db() |
734 | - self.addCleanup(self.restore_sqlite_db) |
735 | - |
736 | - if os.path.exists(self.local_location): |
737 | - self.launch_test_local() |
738 | - elif os.path.exists(self.installed_location): |
739 | - self.launch_test_installed() |
740 | - else: |
741 | - self.launch_test_click() |
742 | - |
743 | + super(BaseTestCaseWithPatchedHome, self).setUp() |
744 | + self.launcher, self.test_type = self.get_launcher_and_type() |
745 | + self.home_dir = self._patch_home() |
746 | + |
747 | + # Unset the current locale to ensure locale-specific data |
748 | + # (day and month names, first day of the week, …) doesn’t get |
749 | + # in the way of test expectations. |
750 | + self.useFixture(fixtures.EnvironmentVariable('LC_ALL', newvalue='C')) |
751 | + |
752 | + @autopilot_logging.log_action(logger.info) |
753 | def launch_test_local(self): |
754 | - self.app = self.launch_test_application( |
755 | + return self.launch_test_application( |
756 | base.get_qmlscene_launch_command(), |
757 | - self.local_location, |
758 | + self.local_location_qml, |
759 | app_type='qt', |
760 | - emulator_base=toolkit_emulators.UbuntuUIToolkitEmulatorBase) |
761 | + emulator_base=ubuntuuitoolkit.UbuntuUIToolkitCustomProxyObjectBase) |
762 | |
763 | + @autopilot_logging.log_action(logger.info) |
764 | def launch_test_installed(self): |
765 | - self.app = self.launch_test_application( |
766 | + return self.launch_test_application( |
767 | base.get_qmlscene_launch_command(), |
768 | - self.installed_location, |
769 | + self.installed_location_qml, |
770 | app_type='qt', |
771 | - emulator_base=toolkit_emulators.UbuntuUIToolkitEmulatorBase) |
772 | + emulator_base=ubuntuuitoolkit.UbuntuUIToolkitCustomProxyObjectBase) |
773 | |
774 | + @autopilot_logging.log_action(logger.info) |
775 | def launch_test_click(self): |
776 | - self.app = self.launch_click_package( |
777 | + return self.launch_click_package( |
778 | "com.ubuntu.calculator", |
779 | - emulator_base=toolkit_emulators.UbuntuUIToolkitEmulatorBase) |
780 | - |
781 | - def temp_move_sqlite_db(self): |
782 | - try: |
783 | - shutil.rmtree(self.backup_dir) |
784 | - except: |
785 | - pass |
786 | - else: |
787 | - logger.warning("Prexisting backup database found and removed") |
788 | - |
789 | - try: |
790 | - shutil.move(self.sqlite_dir, self.backup_dir) |
791 | - except: |
792 | - logger.warning("No current database found") |
793 | - else: |
794 | - logger.debug("Backed up database") |
795 | - |
796 | - def restore_sqlite_db(self): |
797 | - if os.path.exists(self.backup_dir): |
798 | - if os.path.exists(self.sqlite_dir): |
799 | - try: |
800 | - shutil.rmtree(self.sqlite_dir) |
801 | - except: |
802 | - logger.error("Failed to remove test database and restore" / |
803 | - "database") |
804 | - return |
805 | - try: |
806 | - shutil.move(self.backup_dir, self.sqlite_dir) |
807 | - except: |
808 | - logger.error("Failed to restore database") |
809 | - |
810 | - @property |
811 | - def main_view(self): |
812 | - return self.app.select_single(emulators.MainView) |
813 | + emulator_base=ubuntuuitoolkit.UbuntuUIToolkitCustomProxyObjectBase) |
814 | + |
815 | + def _copy_xauthority_file(self, directory): |
816 | + """ Copy .Xauthority file to directory, if it exists in /home |
817 | + """ |
818 | + # If running under xvfb, as jenkins does, |
819 | + # xsession will fail to start without xauthority file |
820 | + # Thus if the Xauthority file is in the home directory |
821 | + # make sure we copy it to our temp home directory |
822 | + |
823 | + xauth = os.path.expanduser(os.path.join(os.environ.get('HOME'), |
824 | + '.Xauthority')) |
825 | + if os.path.isfile(xauth): |
826 | + logger.debug("Copying .Xauthority to %s" % directory) |
827 | + shutil.copyfile( |
828 | + os.path.expanduser(os.path.join(os.environ.get('HOME'), |
829 | + '.Xauthority')), |
830 | + os.path.join(directory, '.Xauthority')) |
831 | + |
832 | + def _patch_home(self): |
833 | + """ mock /home for testing purposes to preserve user data |
834 | + """ |
835 | + # click requires apparmor profile, and writing to special dir |
836 | + # but the desktop can write to a traditional /tmp directory |
837 | + if self.test_type == 'click': |
838 | + env_dir = os.path.join(os.environ.get('HOME'), 'autopilot', |
839 | + 'fakeenv') |
840 | + |
841 | + if not os.path.exists(env_dir): |
842 | + os.makedirs(env_dir) |
843 | + |
844 | + temp_dir_fixture = fixtures.TempDir(env_dir) |
845 | + self.useFixture(temp_dir_fixture) |
846 | + |
847 | + # apparmor doesn't allow the app to create needed directories, |
848 | + # so we create them now |
849 | + temp_dir = temp_dir_fixture.path |
850 | + temp_dir_cache = os.path.join(temp_dir, '.cache') |
851 | + temp_dir_cache_font = os.path.join(temp_dir_cache, 'fontconfig') |
852 | + temp_dir_cache_media = os.path.join(temp_dir_cache, 'media-art') |
853 | + temp_dir_cache_write = os.path.join(temp_dir_cache, |
854 | + 'tncache-write-text.null') |
855 | + temp_dir_config = os.path.join(temp_dir, '.config') |
856 | + temp_dir_toolkit = os.path.join(temp_dir_config, |
857 | + 'ubuntu-ui-toolkit') |
858 | + temp_dir_font = os.path.join(temp_dir_cache, '.fontconfig') |
859 | + temp_dir_local = os.path.join(temp_dir, '.local', 'share') |
860 | + temp_dir_confined = os.path.join(temp_dir, 'confined') |
861 | + |
862 | + if not os.path.exists(temp_dir_cache): |
863 | + os.makedirs(temp_dir_cache) |
864 | + if not os.path.exists(temp_dir_cache_font): |
865 | + os.makedirs(temp_dir_cache_font) |
866 | + if not os.path.exists(temp_dir_cache_media): |
867 | + os.makedirs(temp_dir_cache_media) |
868 | + if not os.path.exists(temp_dir_cache_write): |
869 | + os.makedirs(temp_dir_cache_write) |
870 | + if not os.path.exists(temp_dir_config): |
871 | + os.makedirs(temp_dir_config) |
872 | + if not os.path.exists(temp_dir_toolkit): |
873 | + os.makedirs(temp_dir_toolkit) |
874 | + if not os.path.exists(temp_dir_font): |
875 | + os.makedirs(temp_dir_font) |
876 | + if not os.path.exists(temp_dir_local): |
877 | + os.makedirs(temp_dir_local) |
878 | + if not os.path.exists(temp_dir_confined): |
879 | + os.makedirs(temp_dir_confined) |
880 | + |
881 | + # before we set fixture, copy xauthority if needed |
882 | + self._copy_xauthority_file(temp_dir) |
883 | + self.useFixture(toolkit_fixtures.InitctlEnvironmentVariable( |
884 | + HOME=temp_dir)) |
885 | + else: |
886 | + temp_dir_fixture = fixtures.TempDir() |
887 | + self.useFixture(temp_dir_fixture) |
888 | + temp_dir = temp_dir_fixture.path |
889 | + |
890 | + # before we set fixture, copy xauthority if needed |
891 | + self._copy_xauthority_file(temp_dir) |
892 | + self.useFixture(fixtures.EnvironmentVariable('HOME', |
893 | + newvalue=temp_dir)) |
894 | + |
895 | + logger.debug("Patched home to fake home directory %s" % temp_dir) |
896 | + return temp_dir |
897 | + |
898 | + |
899 | +class CalculatorAppTestCase(BaseTestCaseWithPatchedHome): |
900 | + |
901 | + """Base test case that launches the ubuntu-calculator-app.""" |
902 | + |
903 | + def setUp(self): |
904 | + super(CalculatorAppTestCase, self).setUp() |
905 | + self.app = ubuntu_calculator_app.CalculatorApp(self.launcher(), |
906 | + self.test_type) |
907 | |
908 | === added file 'tests/autopilot/ubuntu_calculator_app/tests/test_screen.py' |
909 | --- tests/autopilot/ubuntu_calculator_app/tests/test_screen.py 1970-01-01 00:00:00 +0000 |
910 | +++ tests/autopilot/ubuntu_calculator_app/tests/test_screen.py 2014-09-08 19:35:18 +0000 |
911 | @@ -0,0 +1,91 @@ |
912 | +# -*- Mode: Python; coding: utf-8; indent-tabs-mode: nil; tab-width: 4 -*- |
913 | +# |
914 | +# Copyright (C) 2014 Canonical Ltd. |
915 | +# |
916 | +# This program is free software; you can redistribute it and/or modify |
917 | +# it under the terms of the GNU Lesser General Public License as published by |
918 | +# the Free Software Foundation; version 3. |
919 | +# |
920 | +# This program is distributed in the hope that it will be useful, |
921 | +# but WITHOUT ANY WARRANTY; without even the implied warranty of |
922 | +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
923 | +# GNU Lesser General Public License for more details. |
924 | +# |
925 | +# You should have received a copy of the GNU Lesser General Public License |
926 | +# along with this program. If not, see <http://www.gnu.org/licenses/>. |
927 | + |
928 | +"""Tests for the Calculator App Screen""" |
929 | + |
930 | +from __future__ import absolute_import |
931 | + |
932 | +from testtools.matchers import Equals |
933 | +from autopilot.matchers import Eventually |
934 | + |
935 | +from ubuntu_calculator_app.tests import CalculatorAppTestCase |
936 | + |
937 | + |
938 | +class TestScreen(CalculatorAppTestCase): |
939 | + |
940 | + def setUp(self): |
941 | + super(TestScreen, self).setUp() |
942 | + self.app.main_view.get_simple_page().visible.wait_for(True) |
943 | + |
944 | + def _assert_result(self, expected_result): |
945 | + self.assertThat( |
946 | + self.app.main_view.get_result, Eventually(Equals(expected_result))) |
947 | + |
948 | + def test_operands_before_number(self): |
949 | + """ Operands before the numbers.""" |
950 | + self.app.main_view.calculate_operation("*3") |
951 | + screen = self.app.main_view.get_current_screen() |
952 | + result_label = screen.get_result_label() |
953 | + |
954 | + self.assertThat(result_label, Equals(None)) |
955 | + |
956 | + def test_unfocus_label(self): |
957 | + """ Clear label focus by clicking outside its area. """ |
958 | + self.app.main_view.calculate_operation("12+3") |
959 | + self._assert_result("15") |
960 | + |
961 | + # Focus the item |
962 | + screen = self.app.main_view.get_current_screen() |
963 | + screen.click_title_label() |
964 | + title_label = screen.get_title_label() |
965 | + self.assertThat(title_label.activeFocus, Eventually(Equals(True))) |
966 | + |
967 | + # Click elsewhere to unfocus the label |
968 | + self.app.pointing_device.click_object(screen) |
969 | + self.assertThat(title_label.activeFocus, Eventually(Equals(False))) |
970 | + |
971 | + def test_edit_mode(self): |
972 | + """ Enter and exit edit mode clicking on pencil icon """ |
973 | + self.app.main_view.calculate_operation("105-5") |
974 | + self._assert_result("100") |
975 | + |
976 | + # Click the edit button |
977 | + screen = self.app.main_view.get_current_screen() |
978 | + screen.click_edit_icon() |
979 | + |
980 | + # Check if is focused |
981 | + title_label = screen.get_title_label() |
982 | + self.assertThat(title_label.activeFocus, Eventually(Equals(True))) |
983 | + |
984 | + # Click to unfocus the label |
985 | + screen.click_edit_icon() |
986 | + self.assertThat(title_label.activeFocus, Eventually(Equals(False))) |
987 | + |
988 | + def test_hide_calc_keyboard(self): |
989 | + """ Hide calc keyboard when focus on a label """ |
990 | + self.app.main_view.calculate_operation("18-1") |
991 | + self._assert_result("17") |
992 | + |
993 | + # Focus the item |
994 | + screen = self.app.main_view.get_current_screen() |
995 | + screen.click_title_label() |
996 | + |
997 | + calc_keyboard = self.app.main_view.get_calc_keyboard() |
998 | + self.assertThat(calc_keyboard.visible, Eventually(Equals(False))) |
999 | + |
1000 | + # Click elsewhere to unfocus the label |
1001 | + self.app.pointing_device.click_object(screen) |
1002 | + self.assertThat(calc_keyboard.visible, Eventually(Equals(True))) |
1003 | |
1004 | === modified file 'tests/autopilot/ubuntu_calculator_app/tests/test_simple_page.py' |
1005 | --- tests/autopilot/ubuntu_calculator_app/tests/test_simple_page.py 2014-06-12 21:56:11 +0000 |
1006 | +++ tests/autopilot/ubuntu_calculator_app/tests/test_simple_page.py 2014-09-08 19:35:18 +0000 |
1007 | @@ -14,93 +14,77 @@ |
1008 | # You should have received a copy of the GNU Lesser General Public License |
1009 | # along with this program. If not, see <http://www.gnu.org/licenses/>. |
1010 | |
1011 | -"""Tests for the Calculator App""" |
1012 | +"""Tests for the Calculator App Simple Page""" |
1013 | |
1014 | from __future__ import absolute_import |
1015 | |
1016 | from testtools.matchers import Equals |
1017 | from autopilot.matchers import Eventually |
1018 | -from autopilot.platform import model |
1019 | - |
1020 | -from ubuntu_calculator_app.tests import CalculatorTestCase |
1021 | -import subprocess |
1022 | - |
1023 | - |
1024 | -class TestSimplePage(CalculatorTestCase): |
1025 | + |
1026 | +from ubuntu_calculator_app.tests import CalculatorAppTestCase |
1027 | + |
1028 | + |
1029 | +class TestSimplePage(CalculatorAppTestCase): |
1030 | |
1031 | def setUp(self): |
1032 | super(TestSimplePage, self).setUp() |
1033 | - |
1034 | - @classmethod |
1035 | - def setUpClass(cls): |
1036 | - if model() != "Desktop": |
1037 | - try: |
1038 | - subprocess.check_call(["stop", "-q", "maliit-server"]) |
1039 | - except subprocess.CalledProcessError: |
1040 | - pass |
1041 | - |
1042 | - @classmethod |
1043 | - def tearDownClass(cls): |
1044 | - if model() != "Desktop": |
1045 | - try: |
1046 | - subprocess.check_call(["start", "-q", "maliit-server"]) |
1047 | - except subprocess.CalledProcessError: |
1048 | - pass |
1049 | + self.app.main_view.get_simple_page().visible.wait_for(True) |
1050 | + |
1051 | + def _assert_result(self, expected_result): |
1052 | + self.assertThat( |
1053 | + self.app.main_view.get_result, Eventually(Equals(expected_result))) |
1054 | |
1055 | def test_operation_after_clear(self): |
1056 | """ Test that after clearing one calculation, the next is still correct |
1057 | (bug #1164973). |
1058 | |
1059 | """ |
1060 | - self.main_view.calculate_operation("1+1") |
1061 | - self._assert_result("2") |
1062 | - self.main_view.clear_with_button() |
1063 | - self.main_view.calculate_operation("1+1") |
1064 | - self._assert_result("2") |
1065 | - |
1066 | - def _assert_result(self, expected_result): |
1067 | - self.assertThat( |
1068 | - self.main_view.get_result, Eventually(Equals(expected_result))) |
1069 | + self.app.main_view.calculate_operation("1+1") |
1070 | + self._assert_result("2") |
1071 | + self.app.main_view.clear_with_button() |
1072 | + self.app.main_view.calculate_operation("1+1") |
1073 | + self._assert_result("2") |
1074 | |
1075 | def test_enter_operand(self): |
1076 | - self.main_view.enter_operation("123") |
1077 | - self.assertEqual(self.main_view.get_operand_by_index(0), "123") |
1078 | + self.app.main_view.enter_operation("123") |
1079 | + self.assertEqual(self.app.main_view.get_operand_by_index(0), "123") |
1080 | |
1081 | def test_equals_dont_change_numbers(self): |
1082 | """ Test that typing a number and pressing "=" won't change the number |
1083 | (bug #1165894). |
1084 | |
1085 | """ |
1086 | - self.main_view.enter_operation("123") |
1087 | - self.main_view.get_calc_keyboard().click_equals() |
1088 | + self.app.main_view.enter_operation("123") |
1089 | + self.app.main_view.get_calc_keyboard().click_equals() |
1090 | |
1091 | # check that the label is still displaying correctly |
1092 | - self.assertEqual(self.main_view.get_operand_by_index(0), "123") |
1093 | + self.assertEqual(self.app.main_view.get_operand_by_index(0), "123") |
1094 | |
1095 | def test_multiply_priority(self): |
1096 | """Multiply is done before addition or substraction.""" |
1097 | - self.main_view.calculate_operation("2+2*2") |
1098 | + self.app.main_view.calculate_operation("2+2*2") |
1099 | self._assert_result("6") |
1100 | |
1101 | def test_divide_priority(self): |
1102 | """Divide is done before addition or substraction.""" |
1103 | - self.main_view.calculate_operation("5+6/2") |
1104 | + self.app.main_view.calculate_operation("5+6/2") |
1105 | self._assert_result("8") |
1106 | |
1107 | def test_divide_with_infinity_length_result_number(self): |
1108 | """Check how is formatted infinity long result number.""" |
1109 | - self.main_view.calculate_operation("1/3") |
1110 | + self.app.main_view.calculate_operation("1/3") |
1111 | self._assert_result("0.33333333") |
1112 | |
1113 | def test_swipe_up_clears(self): |
1114 | """Make sure swiping up clears the formula label.""" |
1115 | - self.main_view.enter_operation("9") |
1116 | - self.main_view.clear_with_swipe() |
1117 | - self.assertThat(self.main_view.is_cleared, Eventually(Equals(True))) |
1118 | + self.app.main_view.enter_operation("9") |
1119 | + self.app.main_view.clear_with_swipe() |
1120 | + self.assertThat(self.app.main_view.is_cleared, |
1121 | + Eventually(Equals(True))) |
1122 | |
1123 | def test_sign_button(self): |
1124 | """Ensure the sign button is working.""" |
1125 | - calc_keyboard = self.main_view.get_calc_keyboard() |
1126 | + calc_keyboard = self.app.main_view.get_calc_keyboard() |
1127 | calc_keyboard.enter_operation("5+") |
1128 | calc_keyboard.click_sign() |
1129 | calc_keyboard.enter_operation("1") |
1130 | @@ -110,24 +94,24 @@ |
1131 | |
1132 | def test_addition(self): |
1133 | """Addition test.""" |
1134 | - self.main_view.calculate_operation("4+2.55") |
1135 | + self.app.main_view.calculate_operation("4+2.55") |
1136 | self._assert_result("6.55") |
1137 | |
1138 | def test_substraction(self): |
1139 | - self.main_view.calculate_operation("5-4") |
1140 | + self.app.main_view.calculate_operation("5-4") |
1141 | self._assert_result("1") |
1142 | |
1143 | def test_positive_numbers_multiplication(self): |
1144 | - self.main_view.calculate_operation("2.1*3") |
1145 | + self.app.main_view.calculate_operation("2.1*3") |
1146 | self._assert_result("6.3") |
1147 | |
1148 | def test_multiplication_by_zero(self): |
1149 | - self.main_view.calculate_operation("4*0") |
1150 | + self.app.main_view.calculate_operation("4*0") |
1151 | self._assert_result("0") |
1152 | |
1153 | def test_one_negative_number_multiplication(self): |
1154 | # tested operation: -3*4 |
1155 | - calc_keyboard = self.main_view.get_calc_keyboard() |
1156 | + calc_keyboard = self.app.main_view.get_calc_keyboard() |
1157 | calc_keyboard.click_sign() # to get "-" |
1158 | calc_keyboard.enter_operation("3*4") |
1159 | calc_keyboard.click_equals() |
1160 | @@ -135,7 +119,7 @@ |
1161 | |
1162 | def test_two_negative_numbers_multiplication(self): |
1163 | # tested operation: -2*-3 |
1164 | - calc_keyboard = self.main_view.get_calc_keyboard() |
1165 | + calc_keyboard = self.app.main_view.get_calc_keyboard() |
1166 | calc_keyboard.click_sign() # to get "-" |
1167 | calc_keyboard.enter_operation("2*") |
1168 | calc_keyboard.click_sign() # to get "-" |
1169 | @@ -145,12 +129,12 @@ |
1170 | self._assert_result("6") |
1171 | |
1172 | def test_three_positive_numbers_multiplication(self): |
1173 | - self.main_view.calculate_operation("2*3*10") |
1174 | + self.app.main_view.calculate_operation("2*3*10") |
1175 | self._assert_result("60") |
1176 | |
1177 | def test_two_negatives_in_three_numbers_multiplication(self): |
1178 | # tested operation: -2*-3*5 |
1179 | - calc_keyboard = self.main_view.get_calc_keyboard() |
1180 | + calc_keyboard = self.app.main_view.get_calc_keyboard() |
1181 | calc_keyboard.click_sign() # to get "-" |
1182 | calc_keyboard.enter_operation("2*") |
1183 | calc_keyboard.click_sign() # to get "-" |
1184 | @@ -161,7 +145,7 @@ |
1185 | |
1186 | def test_three_negative_numbers_multiplication(self): |
1187 | # tested operation: -4*-5*-7 |
1188 | - calc_keyboard = self.main_view.get_calc_keyboard() |
1189 | + calc_keyboard = self.app.main_view.get_calc_keyboard() |
1190 | calc_keyboard.click_sign() # to get "-" |
1191 | calc_keyboard.enter_operation("4*") |
1192 | calc_keyboard.click_sign() # to get "-" |
1193 | @@ -173,12 +157,12 @@ |
1194 | self._assert_result("-140") |
1195 | |
1196 | def test_divide_positive(self): |
1197 | - self.main_view.calculate_operation("4/2") |
1198 | + self.app.main_view.calculate_operation("4/2") |
1199 | self._assert_result("2") |
1200 | |
1201 | def test_divide_negative(self): |
1202 | # tested operation: 4/-2 |
1203 | - calc_keyboard = self.main_view.get_calc_keyboard() |
1204 | + calc_keyboard = self.app.main_view.get_calc_keyboard() |
1205 | calc_keyboard.enter_operation("4/") |
1206 | calc_keyboard.click_sign() # to get "-" |
1207 | calc_keyboard.enter_operation("2") |
1208 | @@ -188,7 +172,7 @@ |
1209 | |
1210 | def test_divide_two_negatives(self): |
1211 | # tested operation: -4/-2 |
1212 | - calc_keyboard = self.main_view.get_calc_keyboard() |
1213 | + calc_keyboard = self.app.main_view.get_calc_keyboard() |
1214 | calc_keyboard.click_sign() # to get "-" |
1215 | calc_keyboard.enter_operation("4/") |
1216 | calc_keyboard.click_sign() # to get "-" |
1217 | @@ -198,114 +182,58 @@ |
1218 | self._assert_result("2") |
1219 | |
1220 | def test_divide_with_zero(self): |
1221 | - self.main_view.calculate_operation("0/4") |
1222 | + self.app.main_view.calculate_operation("0/4") |
1223 | self._assert_result("0") |
1224 | |
1225 | def test_divide_by_zero(self): |
1226 | - self.main_view.calculate_operation("4/0") |
1227 | + self.app.main_view.calculate_operation("4/0") |
1228 | self._assert_result(u'+\u221e') |
1229 | |
1230 | def test_divide_zero_by_zero(self): |
1231 | - self.main_view.calculate_operation("0/0") |
1232 | + self.app.main_view.calculate_operation("0/0") |
1233 | self._assert_result("NaN") |
1234 | |
1235 | def test_adding_small_number(self): |
1236 | - self.main_view.calculate_operation("0.000000001+1") |
1237 | + self.app.main_view.calculate_operation("0.000000001+1") |
1238 | self._assert_result("1.000000001") |
1239 | |
1240 | def test_divide_small_number(self): |
1241 | """Check result number formating.""" |
1242 | - self.main_view.calculate_operation(".000000001/10") |
1243 | + self.app.main_view.calculate_operation(".000000001/10") |
1244 | self._assert_result("1e-10") |
1245 | |
1246 | def test_large_numbers_multiplication(self): |
1247 | - self.main_view.calculate_operation("99999999999*99999999999") |
1248 | + self.app.main_view.calculate_operation("99999999999*99999999999") |
1249 | self._assert_result("1.0000000e+22") |
1250 | |
1251 | def test_swipe_up_with_result(self): |
1252 | """Test swipe up adds calculation to history""" |
1253 | - self.main_view.calculate_operation("9+8") |
1254 | - self.main_view.clear_with_swipe() |
1255 | + self.app.main_view.calculate_operation("9+8") |
1256 | + self.app.main_view.clear_with_swipe() |
1257 | |
1258 | self.assertThat( |
1259 | - self.main_view.get_number_of_screens, Eventually(Equals(2))) |
1260 | + self.app.main_view.get_number_of_screens, Eventually(Equals(2))) |
1261 | self.assertTrue( |
1262 | - self.main_view.get_screen_by_index(1).is_cleared()) |
1263 | + self.app.main_view.get_screen_by_index(1).is_cleared()) |
1264 | self.assertEquals( |
1265 | - self.main_view.get_screen_by_index(0).get_result(), "17") |
1266 | + self.app.main_view.get_screen_by_index(0).get_result(), "17") |
1267 | |
1268 | def test_swipe_to_delete_calculation(self): |
1269 | """Delete a calculation swiping on it to the side.""" |
1270 | - self.main_view.calculate_operation("9+5") |
1271 | - self.main_view.clear_with_swipe() |
1272 | + self.app.main_view.calculate_operation("9+5") |
1273 | + self.app.main_view.clear_with_swipe() |
1274 | |
1275 | - self.main_view.swipe_previous_calculation_into_view(0) |
1276 | - self.main_view.delete_previous_calculation(0, confirm=True) |
1277 | + self.app.main_view.swipe_previous_calculation_into_view(0) |
1278 | + self.app.main_view.delete_previous_calculation(0, confirm=True) |
1279 | |
1280 | self.assertThat( |
1281 | - self.main_view.get_number_of_screens, Eventually(Equals(1))) |
1282 | - |
1283 | - def test_operands_before_number(self): |
1284 | - """ Operands before the numbers.""" |
1285 | - self.main_view.calculate_operation("*3") |
1286 | - screen = self.main_view.get_current_screen() |
1287 | - result_label = screen.get_result_label() |
1288 | - |
1289 | - self.assertThat(result_label, Equals(None)) |
1290 | + self.app.main_view.get_number_of_screens, Eventually(Equals(1))) |
1291 | |
1292 | def test_continue_calculation(self): |
1293 | - self.main_view.calculate_operation("59+1") |
1294 | + self.app.main_view.calculate_operation("59+1") |
1295 | |
1296 | - calc_keyboard = self.main_view.get_calc_keyboard() |
1297 | + calc_keyboard = self.app.main_view.get_calc_keyboard() |
1298 | calc_keyboard.enter_operation("+5") |
1299 | calc_keyboard.click_equals() |
1300 | |
1301 | self._assert_result("65") |
1302 | - |
1303 | - def test_unfocus_label(self): |
1304 | - """ Clear label focus by clicking outside its area. """ |
1305 | - self.main_view.calculate_operation("12+3") |
1306 | - self._assert_result("15") |
1307 | - |
1308 | - # Focus the item |
1309 | - screen = self.main_view.get_current_screen() |
1310 | - screen.click_title_label() |
1311 | - title_label = screen.get_title_label() |
1312 | - self.assertThat(title_label.activeFocus, Eventually(Equals(True))) |
1313 | - |
1314 | - # Click elsewhere to unfocus the label |
1315 | - screen.click_result_label() |
1316 | - self.assertThat(title_label.activeFocus, Eventually(Equals(False))) |
1317 | - |
1318 | - def test_hide_calc_keyboard(self): |
1319 | - """ Hide calc keyboard when focus on a label """ |
1320 | - self.main_view.calculate_operation("18-1") |
1321 | - self._assert_result("17") |
1322 | - |
1323 | - # Focus the item |
1324 | - screen = self.main_view.get_current_screen() |
1325 | - screen.click_title_label() |
1326 | - |
1327 | - calc_keyboard = self.main_view.get_calc_keyboard() |
1328 | - self.assertThat(calc_keyboard.visible, Eventually(Equals(False))) |
1329 | - |
1330 | - # Click elsewhere to unfocus the label |
1331 | - screen.click_result_label() |
1332 | - self.assertThat(calc_keyboard.visible, Eventually(Equals(True))) |
1333 | - |
1334 | - def test_edit_mode(self): |
1335 | - """ Enter and exit edit mode clicking on pencil icon """ |
1336 | - self.main_view.calculate_operation("105-5") |
1337 | - self._assert_result("100") |
1338 | - |
1339 | - # Click the edit button |
1340 | - screen = self.main_view.get_current_screen() |
1341 | - screen.click_edit_icon() |
1342 | - |
1343 | - # Check if is focused |
1344 | - title_label = screen.get_title_label() |
1345 | - self.assertThat(title_label.activeFocus, Eventually(Equals(True))) |
1346 | - |
1347 | - # Click to unfocus the label |
1348 | - screen.click_edit_icon() |
1349 | - self.assertThat(title_label.activeFocus, Eventually(Equals(False))) |
FAILED: Continuous integration, rev:322 91.189. 93.70:8080/ job/ubuntu- calculator- app-ci/ 242/ 91.189. 93.70:8080/ job/generic- mediumtests- utopic/ 1909 91.189. 93.70:8080/ job/generic- mediumtests- utopic/ 1909/artifact/ work/output/ *zip*/output. zip 91.189. 93.70:8080/ job/ubuntu- calculator- app-utopic- amd64-ci/ 37
http://
Executed test runs:
UNSTABLE: http://
deb: http://
SUCCESS: http://
Click here to trigger a rebuild: 91.189. 93.70:8080/ job/ubuntu- calculator- app-ci/ 242/rebuild
http://