Merge lp:~canonical-platform-qa/sudoku-app/clean_tests1 into lp:sudoku-app

Proposed by Leo Arias
Status: Merged
Approved by: Leo Arias
Approved revision: 242
Merged at revision: 244
Proposed branch: lp:~canonical-platform-qa/sudoku-app/clean_tests1
Merge into: lp:sudoku-app
Diff against target: 1085 lines (+492/-211)
7 files modified
sudoku-app.qml (+8/-4)
tests/autopilot/sudoku_app/__init__.py (+56/-48)
tests/autopilot/sudoku_app/pages/__init__.py (+21/-0)
tests/autopilot/sudoku_app/pages/_settings_page.py (+235/-0)
tests/autopilot/sudoku_app/tests/__init__.py (+36/-20)
tests/autopilot/sudoku_app/tests/settings/test_profiles.py (+72/-0)
tests/autopilot/sudoku_app/tests/test_sudoku.py (+64/-139)
To merge this branch: bzr merge lp:~canonical-platform-qa/sudoku-app/clean_tests1
Reviewer Review Type Date Requested Status
Nicholas Skaggs (community) Approve
Chris Gagnon (community) Needs Fixing
Ubuntu Phone Apps Jenkins Bot continuous-integration Approve
Review via email: mp+228236@code.launchpad.net

Commit message

Refactor autopilot tests for profiles.

To post a comment you must log in.
Revision history for this message
Ubuntu Phone Apps Jenkins Bot (ubuntu-phone-apps-jenkins-bot) wrote :
review: Needs Fixing (continuous-integration)
240. By Leo Arias

Avoid the pyflakes error.

Revision history for this message
Ubuntu Phone Apps Jenkins Bot (ubuntu-phone-apps-jenkins-bot) wrote :
review: Approve (continuous-integration)
Revision history for this message
Ubuntu Phone Apps Jenkins Bot (ubuntu-phone-apps-jenkins-bot) wrote :
review: Approve (continuous-integration)
Revision history for this message
Chris Gagnon (chris.gagnon) wrote :

The closing ) should be on it's own line if you want to stay compliant with the autopilot project coding format.

other than that it looks good to me

review: Needs Fixing
Revision history for this message
Nicholas Skaggs (nskaggs) wrote :

Though I won't hold the MP for it, you really should propogate using the emulators import as we've deprecated it for the helpers.

    emulators as toolkit_emulators,

I will disagree with Chris on the closing ); I really hate hanging )'s. The other thing of note is the backup and restore db, but that too can be another MP. test_new_game_button can probably be done as a scenario; as can others in the test_settings (theme, difficulty, etc).

Overall, a major improvement.

review: Approve
241. By Leo Arias

Merged with trunk.

242. By Leo Arias

Follow autopilot style for closing parentheses.

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

> The closing ) should be on it's own line if you want to stay compliant with
> the autopilot project coding format.
>
>
> other than that it looks good to me

Done. However, I think that for these projects we should stick to enforce things that are automatically checked by flake8.
We need a clear direction anyway, so lets talk about it during a stand up.

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

> Though I won't hold the MP for it, you really should propogate using the
> emulators import as we've deprecated it for the helpers.
>
> emulators as toolkit_emulators,
>
>
> I will disagree with Chris on the closing ); I really hate hanging )'s. The
> other thing of note is the backup and restore db, but that too can be another
> MP. test_new_game_button can probably be done as a scenario; as can others in
> the test_settings (theme, difficulty, etc).
>
> Overall, a major improvement.

Yes, it's already to big to also remove the remaining toolkit_emulators. I made the change on the files that I already have to touch extensively.
All the remaining tests also need to be cleaned, and some need to be downgraded to qml tests. But that goes to the todo as time permits.

Thanks to both for the review. I'll top-approve now.

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1=== modified file 'sudoku-app.qml'
2--- sudoku-app.qml 2014-07-23 03:15:57 +0000
3+++ sudoku-app.qml 2014-07-29 23:33:54 +0000
4@@ -746,7 +746,7 @@
5 }
6 }
7 }
8- ToolbarButton {
9+ ToolbarButton {
10 action: Action {
11 objectName: "hintbutton"
12 id: revealHintAction
13@@ -895,6 +895,8 @@
14 property alias themeIndex: themeSelector.selectedIndex;
15
16 page: Page {
17+ objectName: "settingsPage"
18+
19 anchors {
20 left: parent.left
21 right: parent.right
22@@ -906,15 +908,15 @@
23 Component {
24 id: profileSelector
25 Dialog {
26+ objectName: "selectProfileDialog"
27 title: i18n.tr("Select profile")
28
29-
30-
31 Column{
32 height: mainColumnSettings.height*2/3
33 ListView {
34
35 id: profileListView
36+ objectName: "profileListView"
37 clip: true
38 width: parent.width
39 height: parent.height - units.gu(12)
40@@ -935,7 +937,6 @@
41 }
42
43 SudokuDialogButton{
44-
45 anchors.horizontalCenter: parent.horizontalCenter
46 id:cancelButton
47 buttonText: i18n.tr("Cancel")
48@@ -956,12 +957,14 @@
49 Component {
50 id: manageProfileSelector
51 Dialog {
52+ objectName: "manageProfileDialog"
53 title: i18n.tr("Select profile")
54
55 Column{
56 height: mainColumnSettings.height*2/3
57 ListView {
58 id: manageProfileListView
59+ objectName: "manageProfileListView"
60 clip: true
61 width: parent.width
62 height: parent.height - units.gu(12)
63@@ -988,6 +991,7 @@
64
65 anchors.horizontalCenter: parent.horizontalCenter
66 id:cancelButton
67+ objectName: "cancelButton"
68 buttonText: i18n.tr("Cancel")
69 width: parent.width/2;
70 size: units.gu(5)
71
72=== removed file 'tests/autopilot/sudoku_app/__init__.py'
73--- tests/autopilot/sudoku_app/__init__.py 2013-07-11 16:46:16 +0000
74+++ tests/autopilot/sudoku_app/__init__.py 1970-01-01 00:00:00 +0000
75@@ -1,6 +0,0 @@
76-# -*- Mode: Python; coding: utf-8; indent-tabs-mode: nil; tab-width: 4 -*-
77-# Copyright 2013 Canonical
78-#
79-# This program is free software: you can redistribute it and/or modify it
80-# under the terms of the GNU General Public License version 3, as published
81-# by the Free Software Foundation.
82
83=== renamed file 'tests/autopilot/sudoku_app/emulators.py' => 'tests/autopilot/sudoku_app/__init__.py'
84--- tests/autopilot/sudoku_app/emulators.py 2014-05-15 13:04:32 +0000
85+++ tests/autopilot/sudoku_app/__init__.py 2014-07-29 23:33:54 +0000
86@@ -1,17 +1,51 @@
87 # -*- Mode: Python; coding: utf-8; indent-tabs-mode: nil; tab-width: 4 -*-
88-# Copyright 2013 Canonical
89+# Copyright 2013, 2014 Canonical
90 #
91-# This program is free software: you can redistribute it and/or modify it
92-# under the terms of the GNU General Public License version 3, as published
93+# This program is free software; you can redistribute it and/or modify
94+# it under the terms of the GNU General Public License version 3, as published
95 # by the Free Software Foundation.
96+#
97+# This program is distributed in the hope that it will be useful,
98+# but WITHOUT ANY WARRANTY; without even the implied warranty of
99+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
100+# GNU General Public License for more details.
101+#
102+# You should have received a copy of the GNU General Public License
103+# along with this program. If not, see <http://www.gnu.org/licenses/>.
104
105 """Sudoku app autopilot emulators."""
106
107-from ubuntuuitoolkit import emulators as toolkit_emulators
108-import ubuntuuitoolkit._custom_proxy_objects
109-
110-
111-class MainView(toolkit_emulators.MainView):
112+import logging
113+
114+import autopilot.logging
115+import ubuntuuitoolkit
116+from autopilot import introspection
117+
118+# Not directly used because of the way autopilot loads custom proxy objects.
119+from sudoku_app import pages
120+pages
121+
122+
123+logger = logging.getLogger(__name__)
124+
125+
126+class SudokuApp:
127+
128+ """Autopilot helper for the Sudoku application."""
129+
130+ def __init__(self, app_proxy):
131+ self.app_proxy = app_proxy
132+ self.main_view = self.app_proxy.select_single(MainView)
133+
134+
135+class MainView(ubuntuuitoolkit.MainView):
136+
137+ @autopilot.logging.log_action(logger.info)
138+ def go_to_settings(self):
139+ self.switch_to_tab('settingsTab')
140+ settings_page = self.select_single(objectName='settingsPage')
141+ settings_page.visible.wait_for(True)
142+ return settings_page
143
144 def get_blank_inputs(self):
145 # generate a list of blank input fields from the game board
146@@ -28,7 +62,7 @@
147 return button
148
149 def get_number_dialog(self):
150- return self.wait_select_single("Dialog", objectName="picknumberscreen")
151+ return self.wait_select_single(objectName='picknumberscreen')
152
153 def get_hints_switch(self):
154 return self.select_single("CheckBox", objectName="hintsSwitch")
155@@ -51,45 +85,6 @@
156 return self.wait_select_single(ubuntuuitoolkit.ToolbarButton,
157 objectName="hintbutton")
158
159- def get_current_profile(self):
160- return self.select_single("SingleValue", objectName="Current profile")
161-
162- def get_sudoku_user_profile(self):
163- return self.select_single("Standard", text="Sudoku User")
164-
165- def get_user_profile_cancel_button(self):
166- return self.select_single("Button", objectName="profileCancelButton")
167-
168- def get_add_profile(self):
169- return self.select_single("SingleValue", objectName="Add profile")
170-
171- def get_add_profile_dialog(self):
172- return self.wait_select_single("Dialog", objectName="Add new profile")
173-
174- def get_add_profile_Lastname_field(self):
175- return self.select_single("TextField", objectName="Lastname")
176-
177- def get_add_profile_Firstname_field(self):
178- return self.select_single("TextField", objectName="Firstname")
179-
180- def get_add_profile_OKbutton(self):
181- return self.select_single("SudokuDialogButton", objectName="OKbutton")
182-
183- def get_manage_profiles(self):
184- return self.wait_select_single(
185- "SingleValue", objectName="Manage profiles")
186-
187- def firstname_Mylastname_profile(self):
188- return self.wait_select_single(
189- "Standard", text="Myfirstname Mylastname")
190-
191- def get_edit_profile_dialog(self):
192- return self.select_single("Dialog", objectName="Edit profile")
193-
194- def get_edit_profile_delete_button(self):
195- return self.select_single("SudokuDialogButton",
196- objectName="deleteButton")
197-
198 def get_new_game_easy_button(self):
199 return self.wait_select_single("NewGameSelectionButton",
200 objectName="easyGameButton")
201@@ -109,3 +104,16 @@
202 def get_new_game_button(self, objectName):
203 return self.wait_select_single("NewGameSelectionButton",
204 objectName=objectName)
205+
206+
207+class PickNumberDialog(ubuntuuitoolkit.UbuntuUIToolkitCustomProxyObjectBase):
208+
209+ """Autopilot helper for the Add new profile dialog."""
210+
211+ @classmethod
212+ def validate_dbus_object(cls, path, state):
213+ name = introspection.get_classname_from_path(path)
214+ if name == b'Dialog':
215+ if state['objectName'][1] == 'picknumberscreen':
216+ return True
217+ return False
218
219=== added directory 'tests/autopilot/sudoku_app/pages'
220=== added file 'tests/autopilot/sudoku_app/pages/__init__.py'
221--- tests/autopilot/sudoku_app/pages/__init__.py 1970-01-01 00:00:00 +0000
222+++ tests/autopilot/sudoku_app/pages/__init__.py 2014-07-29 23:33:54 +0000
223@@ -0,0 +1,21 @@
224+# -*- Mode: Python; coding: utf-8; indent-tabs-mode: nil; tab-width: 4 -*-
225+# Copyright 2014 Canonical
226+#
227+# This program is free software; you can redistribute it and/or modify
228+# it under the terms of the GNU General Public License version 3, as published
229+# by the Free Software Foundation.
230+#
231+# This program is distributed in the hope that it will be useful,
232+# but WITHOUT ANY WARRANTY; without even the implied warranty of
233+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
234+# GNU General Public License for more details.
235+#
236+# You should have received a copy of the GNU General Public License
237+# along with this program. If not, see <http://www.gnu.org/licenses/>.
238+
239+__all__ = [
240+ 'SettingsPage'
241+]
242+
243+
244+from sudoku_app.pages._settings_page import SettingsPage
245
246=== added file 'tests/autopilot/sudoku_app/pages/_settings_page.py'
247--- tests/autopilot/sudoku_app/pages/_settings_page.py 1970-01-01 00:00:00 +0000
248+++ tests/autopilot/sudoku_app/pages/_settings_page.py 2014-07-29 23:33:54 +0000
249@@ -0,0 +1,235 @@
250+# -*- Mode: Python; coding: utf-8; indent-tabs-mode: nil; tab-width: 4 -*-
251+# Copyright 2014 Canonical
252+#
253+# This program is free software; you can redistribute it and/or modify
254+# it under the terms of the GNU General Public License version 3, as published
255+# by the Free Software Foundation.
256+#
257+# This program is distributed in the hope that it will be useful,
258+# but WITHOUT ANY WARRANTY; without even the implied warranty of
259+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
260+# GNU General Public License for more details.
261+#
262+# You should have received a copy of the GNU General Public License
263+# along with this program. If not, see <http://www.gnu.org/licenses/>.
264+
265+import logging
266+
267+import autopilot.logging
268+import ubuntuuitoolkit
269+from autopilot import introspection
270+
271+
272+logger = logging.getLogger(__name__)
273+
274+
275+class SettingsPage(ubuntuuitoolkit.UbuntuUIToolkitCustomProxyObjectBase):
276+
277+ """Autopilot helper for the Settings page."""
278+
279+ @classmethod
280+ def validate_dbus_object(cls, path, state):
281+ name = introspection.get_classname_from_path(path)
282+ if name == b'Page10':
283+ if state['objectName'][1] == 'settingsPage':
284+ return True
285+ return False
286+
287+ @autopilot.logging.log_action(logger.info)
288+ def add_profile(self, last_name, first_name):
289+ self._click_item('Add profile')
290+ dialog = self.get_root_instance().select_single(
291+ objectName='Add new profile'
292+ )
293+ dialog.add_new_profile(last_name, first_name)
294+
295+ @autopilot.logging.log_action(logger.debug)
296+ def _click_item(self, object_name):
297+ item = self.select_single(objectName=object_name)
298+ flickable = self.select_single(
299+ ubuntuuitoolkit.QQuickFlickable,
300+ objectName='settingsContainer'
301+ )
302+ flickable.swipe_child_into_view(item)
303+ self.pointing_device.click_object(item)
304+
305+ def get_profiles(self):
306+ """Return a list with the names of the existing profiles."""
307+ manage_profile_dialog = self._go_to_manage_profiles()
308+ profiles = manage_profile_dialog.get_profiles()
309+ manage_profile_dialog.click_cancel()
310+ return profiles
311+
312+ @autopilot.logging.log_action(logger.debug)
313+ def _go_to_manage_profiles(self):
314+ self._click_item('Manage profiles')
315+ return self.get_root_instance().select_single(
316+ objectName='manageProfileDialog'
317+ )
318+
319+ @autopilot.logging.log_action(logger.debug)
320+ def change_profile(self, profile_name):
321+ select_profile_dialog = self._go_to_select_profiles()
322+ select_profile_dialog.select(profile_name)
323+
324+ @autopilot.logging.log_action(logger.debug)
325+ def _go_to_select_profiles(self):
326+ self._click_item('Current profile')
327+ return self.get_root_instance().select_single(
328+ objectName='selectProfileDialog'
329+ )
330+
331+ def get_current_profile(self):
332+ """Return the name of the current profile."""
333+ current_profile_item = self.select_single(objectName='Current profile')
334+ return current_profile_item.value
335+
336+ @autopilot.logging.log_action(logger.info)
337+ def delete_profile(self, profile_name):
338+ manage_profile_dialog = self._go_to_manage_profiles()
339+ manage_profile_dialog.delete_profile(profile_name)
340+
341+
342+class AddNewProfileDialog(
343+ ubuntuuitoolkit.UbuntuUIToolkitCustomProxyObjectBase):
344+
345+ """Autopilot helper for the Add new profile dialog."""
346+
347+ @classmethod
348+ def validate_dbus_object(cls, path, state):
349+ name = introspection.get_classname_from_path(path)
350+ if name == b'Dialog':
351+ if state['objectName'][1] == 'Add new profile':
352+ return True
353+ return False
354+
355+ @autopilot.logging.log_action(logger.debug)
356+ def add_new_profile(self, last_name, first_name):
357+ self._fill_form(last_name, first_name)
358+ self._click_ok()
359+ self.wait_until_destroyed()
360+
361+ @autopilot.logging.log_action(logger.debug)
362+ def _fill_form(self, last_name, first_name):
363+ last_name_text_field = self.select_single(
364+ ubuntuuitoolkit.TextField,
365+ objectName='Lastname'
366+ )
367+ last_name_text_field.write(last_name)
368+ first_name_text_field = self.select_single(
369+ ubuntuuitoolkit.TextField,
370+ objectName='Firstname'
371+ )
372+ first_name_text_field.write(first_name)
373+
374+ def _click_ok(self):
375+ ok_button = self.select_single(
376+ 'SudokuDialogButton',
377+ objectName='OKbutton'
378+ )
379+ self.pointing_device.click_object(ok_button)
380+
381+
382+class ProfileDialog(ubuntuuitoolkit.UbuntuUIToolkitCustomProxyObjectBase):
383+
384+ PROFILE_LIST_VIEW_OBJECT_NAME = None
385+
386+ def get_profiles(self):
387+ """Return a list with the names of the existing profiles."""
388+ profile_list_view = self._get_profile_list_view()
389+ profile_items = profile_list_view.select_many(
390+ ubuntuuitoolkit.listitems.Standard
391+ )
392+ # Sort by the position on the list.
393+ sorted_items = sorted(
394+ profile_items,
395+ key=lambda item: item.globalRect.y)
396+ profiles = [item.text for item in sorted_items]
397+ return profiles
398+
399+ def _get_profile_list_view(self):
400+ return self.select_single(
401+ ubuntuuitoolkit.QQuickListView,
402+ objectName=self.PROFILE_LIST_VIEW_OBJECT_NAME
403+ )
404+
405+ @autopilot.logging.log_action(logger.debug)
406+ def click_profile(self, profile_name):
407+ profile_list_view = self._get_profile_list_view()
408+ profile_item = profile_list_view.select_single(
409+ ubuntuuitoolkit.listitems.Standard,
410+ text=profile_name
411+ )
412+ self.pointing_device.click_object(profile_item)
413+
414+
415+class SelectProfileDialog(ProfileDialog):
416+
417+ """Autopilot helper for the Select profile dialog."""
418+
419+ PROFILE_LIST_VIEW_OBJECT_NAME = 'profileListView'
420+
421+ @classmethod
422+ def validate_dbus_object(cls, path, state):
423+ name = introspection.get_classname_from_path(path)
424+ if name == b'Dialog':
425+ if state['objectName'][1] == 'selectProfileDialog':
426+ return True
427+ return False
428+
429+ def select(self, profile_name):
430+ self.click_profile(profile_name)
431+ self.wait_until_destroyed()
432+
433+
434+class ManageProfileDialog(ProfileDialog):
435+
436+ """Autopilot helper for the Select profile dialog."""
437+
438+ PROFILE_LIST_VIEW_OBJECT_NAME = 'manageProfileListView'
439+
440+ @classmethod
441+ def validate_dbus_object(cls, path, state):
442+ name = introspection.get_classname_from_path(path)
443+ if name == b'Dialog':
444+ if state['objectName'][1] == 'manageProfileDialog':
445+ return True
446+ return False
447+
448+ @autopilot.logging.log_action(logger.debug)
449+ def click_cancel(self):
450+ cancel_button = self.select_single(
451+ 'SudokuDialogButton',
452+ objectName='cancelButton'
453+ )
454+ self.pointing_device.click_object(cancel_button)
455+ self.wait_until_destroyed()
456+
457+ def delete_profile(self, profile_name):
458+ root = self.get_root_instance()
459+ self._open_profile(profile_name)
460+ self.wait_until_destroyed()
461+ edit_profile_dialog = root.select_single(objectName='Edit profile')
462+ edit_profile_dialog.delete()
463+
464+ @autopilot.logging.log_action(logger.debug)
465+ def _open_profile(self, profile_name):
466+ self.click_profile(profile_name)
467+
468+
469+class EditProfileDialog(ubuntuuitoolkit.UbuntuUIToolkitCustomProxyObjectBase):
470+
471+ @classmethod
472+ def validate_dbus_object(cls, path, state):
473+ name = introspection.get_classname_from_path(path)
474+ if name == b'Dialog':
475+ if state['objectName'][1] == 'Edit profile':
476+ return True
477+ return False
478+
479+ @autopilot.logging.log_action(logger.debug)
480+ def delete(self):
481+ delete_button = self.select_single(
482+ 'SudokuDialogButton', objectName='deleteButton')
483+ self.pointing_device.click_object(delete_button)
484+ self.wait_until_destroyed()
485
486=== modified file 'tests/autopilot/sudoku_app/tests/__init__.py'
487--- tests/autopilot/sudoku_app/tests/__init__.py 2014-07-02 13:52:53 +0000
488+++ tests/autopilot/sudoku_app/tests/__init__.py 2014-07-29 23:33:54 +0000
489@@ -1,9 +1,17 @@
490 # -*- Mode: Python; coding: utf-8; indent-tabs-mode: nil; tab-width: 4 -*-
491-# Copyright 2013 Canonical
492+# Copyright 2013, 2014 Canonical
493 #
494-# This program is free software: you can redistribute it and/or modify it
495-# under the terms of the GNU General Public License version 3, as published
496+# This program is free software; you can redistribute it and/or modify
497+# it under the terms of the GNU General Public License version 3, as published
498 # by the Free Software Foundation.
499+#
500+# This program is distributed in the hope that it will be useful,
501+# but WITHOUT ANY WARRANTY; without even the implied warranty of
502+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
503+# GNU General Public License for more details.
504+#
505+# You should have received a copy of the GNU General Public License
506+# along with this program. If not, see <http://www.gnu.org/licenses/>.
507
508 """Sudoku app autopilot tests."""
509
510@@ -12,14 +20,16 @@
511 import logging
512
513 from autopilot.input import Mouse, Touch, Pointer
514+from autopilot.matchers import Eventually
515 from autopilot.platform import model
516 from autopilot.testcase import AutopilotTestCase
517-
518+from testtools.matchers import Equals
519 from ubuntuuitoolkit import (
520 base,
521 emulators as toolkit_emulators,
522 )
523-from sudoku_app import emulators
524+
525+import sudoku_app
526
527 logger = logging.getLogger(__name__)
528
529@@ -49,31 +59,40 @@
530 self.addCleanup(self.restore_sqlite_db)
531
532 if os.path.exists(self.local_location):
533- self.launch_test_local()
534+ app_proxy = self.launch_test_local()
535 elif os.path.exists('/usr/share/sudoku-app/sudoku-app.qml'):
536- self.launch_test_installed()
537+ app_proxy = self.launch_test_installed()
538 else:
539- self.launch_test_click()
540+ app_proxy = self.launch_test_click()
541+
542+ self.app = sudoku_app.SudokuApp(app_proxy)
543+ self.assertThat(
544+ self.app.main_view.visible,
545+ Eventually(Equals(True))
546+ )
547
548 def launch_test_local(self):
549- self.app = self.launch_test_application(
550+ return self.launch_test_application(
551 base.get_qmlscene_launch_command(),
552 self.local_location,
553 app_type='qt',
554- emulator_base=toolkit_emulators.UbuntuUIToolkitEmulatorBase)
555+ emulator_base=toolkit_emulators.UbuntuUIToolkitEmulatorBase
556+ )
557
558 def launch_test_installed(self):
559- self.app = self.launch_test_application(
560+ return self.launch_test_application(
561 base.get_qmlscene_launch_command(),
562 "/usr/share/sudoku-app/sudoku-app.qml",
563 "--desktop_file_hint=/usr/share/applications/sudoku-app.desktop",
564 app_type='qt',
565- emulator_base=toolkit_emulators.UbuntuUIToolkitEmulatorBase)
566+ emulator_base=toolkit_emulators.UbuntuUIToolkitEmulatorBase
567+ )
568
569 def launch_test_click(self):
570- self.app = self.launch_click_package(
571+ return self.launch_click_package(
572 'com.ubuntu.sudoku',
573- emulator_base=toolkit_emulators.UbuntuUIToolkitEmulatorBase)
574+ emulator_base=toolkit_emulators.UbuntuUIToolkitEmulatorBase
575+ )
576
577 def temp_move_sqlite_db(self):
578 try:
579@@ -96,14 +115,11 @@
580 try:
581 shutil.rmtree(self.sqlite_dir)
582 except:
583- logger.error("Failed to remove test database and restore" /
584- "database")
585+ logger.error(
586+ 'Failed to remove test database and restore database'
587+ )
588 return
589 try:
590 shutil.move(self.backup_dir, self.sqlite_dir)
591 except:
592 logger.error("Failed to restore database")
593-
594- @property
595- def main_view(self):
596- return self.app.select_single(emulators.MainView)
597
598=== added directory 'tests/autopilot/sudoku_app/tests/settings'
599=== added file 'tests/autopilot/sudoku_app/tests/settings/__init__.py'
600=== added file 'tests/autopilot/sudoku_app/tests/settings/test_profiles.py'
601--- tests/autopilot/sudoku_app/tests/settings/test_profiles.py 1970-01-01 00:00:00 +0000
602+++ tests/autopilot/sudoku_app/tests/settings/test_profiles.py 2014-07-29 23:33:54 +0000
603@@ -0,0 +1,72 @@
604+# -*- Mode: Python; coding: utf-8; indent-tabs-mode: nil; tab-width: 4 -*-
605+# Copyright 2013, 2014 Canonical
606+#
607+# This program is free software; you can redistribute it and/or modify
608+# it under the terms of the GNU General Public License version 3, as published
609+# by the Free Software Foundation.
610+#
611+# This program is distributed in the hope that it will be useful,
612+# but WITHOUT ANY WARRANTY; without even the implied warranty of
613+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
614+# GNU General Public License for more details.
615+#
616+# You should have received a copy of the GNU General Public License
617+# along with this program. If not, see <http://www.gnu.org/licenses/>.
618+
619+import uuid
620+
621+from sudoku_app import tests
622+
623+
624+class ProfilesTestCase(tests.SudokuTestCase):
625+
626+ def setUp(self):
627+ super().setUp()
628+ self.settings_page = self.app.main_view.go_to_settings()
629+
630+ def add_new_test_profile(self):
631+ # There is no need to delete the profile created because we are using a
632+ # clean database.
633+ unique_id = uuid.uuid1()
634+ test_last_name = 'Test last name {}'.format(unique_id)
635+ test_first_name = 'Test first name {}'.format(unique_id)
636+
637+ self.settings_page.add_profile(test_last_name, test_first_name)
638+
639+ return test_first_name, test_last_name
640+
641+ def format_profile_name(self, first_name, last_name):
642+ return '{} {}'.format(first_name, last_name)
643+
644+ def test_add_new_profile_must_make_it_available_in_manage_profiles(self):
645+ test_first_name, test_last_name = self.add_new_test_profile()
646+
647+ profiles = self.settings_page.get_profiles()
648+ self.assertIn(
649+ self.format_profile_name(test_first_name, test_last_name),
650+ profiles
651+ )
652+
653+ def test_change_profile_must_update_selected_profile(self):
654+ test_first_name, test_last_name = self.add_new_test_profile()
655+ formated_test_profile_name = self.format_profile_name(
656+ test_first_name, test_last_name
657+ )
658+
659+ self.settings_page.change_profile(formated_test_profile_name)
660+
661+ self.assertEqual(
662+ self.settings_page.get_current_profile(),
663+ formated_test_profile_name
664+ )
665+
666+ def test_delete_profile_must_remove_it_from_manage_profiles(self):
667+ test_first_name, test_last_name = self.add_new_test_profile()
668+ formated_test_profile_name = self.format_profile_name(
669+ test_first_name, test_last_name
670+ )
671+
672+ self.settings_page.delete_profile(formated_test_profile_name)
673+
674+ profiles = self.settings_page.get_profiles()
675+ self.assertNotIn(formated_test_profile_name, profiles)
676
677=== modified file 'tests/autopilot/sudoku_app/tests/test_sudoku.py'
678--- tests/autopilot/sudoku_app/tests/test_sudoku.py 2014-07-02 13:52:53 +0000
679+++ tests/autopilot/sudoku_app/tests/test_sudoku.py 2014-07-29 23:33:54 +0000
680@@ -1,9 +1,17 @@
681 # -*- Mode: Python; coding: utf-8; indent-tabs-mode: nil; tab-width: 4 -*-
682-# Copyright 2013 Canonical
683+# Copyright 2013, 2014 Canonical
684 #
685-# This program is free software: you can redistribute it and/or modify it
686-# under the terms of the GNU General Public License version 3, as published
687+# This program is free software; you can redistribute it and/or modify
688+# it under the terms of the GNU General Public License version 3, as published
689 # by the Free Software Foundation.
690+#
691+# This program is distributed in the hope that it will be useful,
692+# but WITHOUT ANY WARRANTY; without even the implied warranty of
693+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
694+# GNU General Public License for more details.
695+#
696+# You should have received a copy of the GNU General Public License
697+# along with this program. If not, see <http://www.gnu.org/licenses/>.
698
699 """Sudoku app autopilot tests."""
700
701@@ -15,18 +23,13 @@
702
703 class TestMainWindow(SudokuTestCase):
704
705- def setUp(self):
706- super(TestMainWindow, self).setUp()
707- self.assertThat(
708- self.main_view.visible, Eventually(Equals(True)))
709-
710 def test_enter_and_clear_number(self):
711 # find the first button that has a blank value
712- gridButtons = self.main_view.get_blank_inputs()
713+ gridButtons = self.app.main_view.get_blank_inputs()
714 gridButton = gridButtons[0]
715
716 # create a value function to check later using id
717- buttonValue = lambda: self.main_view.select_single(
718+ buttonValue = lambda: self.app.main_view.select_single(
719 "QQuickText", id=gridButton.id).text
720
721 # double check that it's blank
722@@ -36,10 +39,10 @@
723 self.pointing_device.click_object(gridButton)
724
725 # assert that we can see the input screen
726- self.main_view.get_number_dialog()
727+ self.app.main_view.get_number_dialog()
728
729 # set a value, choose 4
730- dialogButton = self.main_view.get_dialog_button("4")
731+ dialogButton = self.app.main_view.get_dialog_button("4")
732 self.pointing_device.click_object(dialogButton)
733
734 # check the value to ensure it worked
735@@ -49,10 +52,10 @@
736 self.pointing_device.click_object(gridButton)
737
738 # make sure we can see the input screen
739- self.main_view.get_number_dialog()
740+ self.app.main_view.get_number_dialog()
741
742 # set a value, choose clear
743- dialogButton = self.main_view.get_dialog_button("Clear")
744+ dialogButton = self.app.main_view.get_dialog_button("Clear")
745 self.pointing_device.click_object(dialogButton)
746
747 # check the value to ensure it worked
748@@ -60,24 +63,25 @@
749
750 def test_best_scores_tab(self):
751 # switch to best scores tab
752- self.main_view.switch_to_tab("highscoresTab")
753+ self.app.main_view.switch_to_tab("highscoresTab")
754
755 # make sure we are in the right place
756- self.main_view.wait_select_single("Tab", objectName="highscoresTab")
757+ self.app.main_view.wait_select_single(
758+ "Tab", objectName="highscoresTab")
759
760 # click current user button
761- header = self.main_view.get_header()
762+ header = self.app.main_view.get_header()
763 header.click_action_button('currentuserbutton')
764
765 # check label
766- label = lambda: self.main_view.wait_select_single(
767+ label = lambda: self.app.main_view.wait_select_single(
768 "Header", objectName="highscoreslabel").text
769 self.assertThat(
770 label,
771 Eventually(NotEquals("<b>Best scores for all players</b>")))
772
773 # click all users button
774- header = self.main_view.get_header()
775+ header = self.app.main_view.get_header()
776 header.click_action_button('allusersbutton')
777
778 # check label again
779@@ -87,11 +91,11 @@
780
781 def test_enter_and_cancel(self):
782 # find the first button that has a blank value
783- gridButtons = self.main_view.get_blank_inputs()
784+ gridButtons = self.app.main_view.get_blank_inputs()
785 gridButton = gridButtons[0]
786
787 # create a value function to check later using id
788- buttonValue = lambda: self.main_view.select_single(
789+ buttonValue = lambda: self.app.main_view.select_single(
790 "QQuickText", id=gridButton.id).text
791
792 # double check that it's blank
793@@ -101,10 +105,10 @@
794 self.pointing_device.click_object(gridButton)
795
796 # make that we can see the input screen
797- self.main_view.get_number_dialog()
798+ self.app.main_view.get_number_dialog()
799
800 # set a value, choose 4
801- dialogButton = self.main_view.get_dialog_button("4")
802+ dialogButton = self.app.main_view.get_dialog_button("4")
803 self.pointing_device.click_object(dialogButton)
804
805 # check the value to ensure it worked
806@@ -114,10 +118,10 @@
807 self.pointing_device.click_object(gridButton)
808
809 # make sure that we can see the input screen
810- self.main_view.get_number_dialog()
811+ self.app.main_view.get_number_dialog()
812
813 # set a value, choose clear
814- dialogButton = self.main_view.get_dialog_button("Cancel")
815+ dialogButton = self.app.main_view.get_dialog_button("Cancel")
816 self.pointing_device.click_object(dialogButton)
817
818 # check the value to ensure it worked
819@@ -145,75 +149,75 @@
820
821 def test_about_tab(self):
822 # Switch to the 'About' tab
823- self.main_view.switch_to_tab("aboutTab")
824+ self.app.main_view.switch_to_tab("aboutTab")
825
826 # Check image loads
827- aboutImage = lambda: self.main_view.select_single(
828+ aboutImage = lambda: self.app.main_view.select_single(
829 "QQuickImage", objectName="aboutImage").progress
830 self.assertThat(aboutImage, Eventually(Equals(1.0)))
831
832 # Check the 'Author(s):' label is displayed
833- aboutLabel = lambda: self.main_view.select_single(
834+ aboutLabel = lambda: self.app.main_view.select_single(
835 "Label", objectName="authorLabel").text
836 self.assertThat(aboutLabel, Eventually(Equals("Author(s): ")))
837
838 # Check the 'Contact:' label is displayed
839- contactLabel = lambda: self.main_view.select_single(
840+ contactLabel = lambda: self.app.main_view.select_single(
841 "Label", objectName="contactLabel").text
842 self.assertThat(contactLabel, Eventually(Equals("Contact: ")))
843
844 # Check correct Launchpad URL: is displayed
845- urlLabel = lambda: self.main_view.select_single(
846+ urlLabel = lambda: self.app.main_view.select_single(
847 "Label", objectName="urlLabel").text
848 self.assertThat(urlLabel, Eventually(Equals(
849 '<a href="https://launchpad.net/sudoku-app">'
850 'https://launchpad.net/sudoku-app</a>')))
851
852 # Check the 'Version:' label is displayed
853- versionLabel = lambda: self.main_view.select_single(
854+ versionLabel = lambda: self.app.main_view.select_single(
855 "Label", objectName="versionLabel").text
856 self.assertThat(versionLabel, Eventually(Equals("Version: ")))
857
858 # Check correct version is displayed
859- version = lambda: self.main_view.select_single(
860+ version = lambda: self.app.main_view.select_single(
861 "Label", objectName="version").text
862 self.assertThat(version, Eventually(Equals("1.5")))
863
864 # Check correct year is displayed
865- yearLabel = lambda: self.main_view.select_single(
866+ yearLabel = lambda: self.app.main_view.select_single(
867 "Label", objectName="yearLabel").text
868 self.assertThat(yearLabel, Eventually(Equals("2013")))
869
870 def test_hint_button(self):
871 # open settings tab
872- self.main_view.switch_to_tab("settingsTab")
873+ self.app.main_view.switch_to_tab("settingsTab")
874
875 # click on hints switch to enable hints toolbar button
876- hintsSwitchClickable = self.main_view.get_hints_switchClickable()
877+ hintsSwitchClickable = self.app.main_view.get_hints_switchClickable()
878
879 self.pointing_device.click_object(hintsSwitchClickable)
880
881 # verify hints switch is clicked
882- self.assertThat(self.main_view.get_hints_switch().checked,
883+ self.assertThat(self.app.main_view.get_hints_switch().checked,
884 Eventually(Equals(True)))
885
886 # exit settings tab by clicking on sudoku tab
887- self.main_view.switch_to_tab("MainTab")
888+ self.app.main_view.switch_to_tab("MainTab")
889
890 # click on hint button on toolbar
891- header = self.main_view.get_header()
892+ header = self.app.main_view.get_header()
893 header.click_action_button('hintbutton')
894
895- number_of_hints = lambda: self.main_view.select_single(
896+ number_of_hints = lambda: self.app.main_view.select_single(
897 objectName="blockgrid").numberOfHints
898 self.assertThat(number_of_hints, Eventually(Equals(1)))
899
900 def test_theme_change(self):
901 # open settings tab
902- self.main_view.switch_to_tab("settingsTab")
903+ self.app.main_view.switch_to_tab("settingsTab")
904
905 # ******** check theme selector ********
906- themeSelector = self.main_view.get_theme_selector()
907+ themeSelector = self.app.main_view.get_theme_selector()
908
909 # select UbuntuColours option
910 themeSelector.select_option('Label', text='UbuntuColours')
911@@ -238,10 +242,10 @@
912
913 def test_difficulty_selector(self):
914 # open settings tab
915- self.main_view.switch_to_tab("settingsTab")
916+ self.app.main_view.switch_to_tab("settingsTab")
917
918 # ******** check difficulty selector ********
919- difficulty = self.main_view.get_difficulty_selector()
920+ difficulty = self.app.main_view.get_difficulty_selector()
921
922 # select Easy
923 difficulty.select_option('Label', text='Easy')
924@@ -280,104 +284,24 @@
925
926 def test_hint_switch(self):
927 # open settings tab
928- self.main_view.switch_to_tab("settingsTab")
929+ self.app.main_view.switch_to_tab("settingsTab")
930
931 # ******** check hint switch ********
932 # select hints switch
933- hintsSwitchClickable = self.main_view.get_hints_switchClickable()
934+ hintsSwitchClickable = self.app.main_view.get_hints_switchClickable()
935 self.assertThat(hintsSwitchClickable.text, Eventually(Equals("Hints")))
936- hintsSwitch = self.main_view.get_hints_switch()
937+ hintsSwitch = self.app.main_view.get_hints_switch()
938
939 # switch it on or off depending on it's state
940 self.pointing_device.click_object(hintsSwitchClickable)
941 self.assertThat(hintsSwitch.checked, Eventually(Equals(True)))
942
943- def test_profiles(self):
944- # open settings tab
945- self.main_view.switch_to_tab("settingsTab")
946-
947- # ******** check profile settings ********
948- # select current profile
949- currentProfile = self.main_view.get_current_profile()
950- self.pointing_device.click_object(currentProfile)
951-
952- # let's change profile
953- # select "sudoku user" profile
954- sudokuUserProfile = self.main_view.get_sudoku_user_profile()
955- self.pointing_device.click_object(sudokuUserProfile)
956-
957- # verify changed profile
958- currentProfile = self.main_view.get_current_profile()
959- self.assertThat(currentProfile.value, Equals("Sudoku User"))
960-
961- # let's add a user profile
962- # verify add profile page opens
963- sudokuAddProfile = self.main_view.get_add_profile()
964- self.pointing_device.click_object(sudokuAddProfile)
965-
966- sudokuAddProfileDialog = self.main_view.get_add_profile_dialog()
967-
968- # insert Lastname
969- lastName = self.main_view.get_add_profile_Lastname_field()
970- self.pointing_device.click_object(lastName)
971- self.assertThat(lastName.placeholderText,
972- Eventually(Equals("Lastname")))
973- self.keyboard.type("Mylastname")
974- self.assertThat(lastName.text, Eventually(Equals("Mylastname")))
975-
976- # insert Firstname
977- firstName = self.main_view.get_add_profile_Firstname_field()
978- self.pointing_device.click_object(firstName)
979- self.assertThat(firstName.placeholderText,
980- Eventually(Equals("Firstname")))
981- self.keyboard.type("Myfirstname")
982- self.assertThat(firstName.text, Eventually(Equals("Myfirstname")))
983-
984- # click OK button
985- OKButton = self.main_view.get_add_profile_OKbutton()
986- self.assertThat(OKButton.buttonText, Eventually(Equals("OK")))
987- self.pointing_device.click_object(OKButton)
988- sudokuAddProfileDialog.wait_until_destroyed()
989-
990- # ******** check manage profiles ********
991- # select manage profile
992- x, y, _, _ = self.main_view.globalRect
993- line_x = x + self.main_view.width * 0.50
994- start_y = y + self.main_view.height * 0.75
995- stop_y = y + self.main_view.height * 0.6
996-
997- self.pointing_device.drag(line_x, start_y, line_x, stop_y)
998- self._wait_to_stop_moving()
999-
1000- manageProfile = self.main_view.get_manage_profiles()
1001- self.pointing_device.click_object(manageProfile)
1002-
1003- # click on the new profile just added
1004- myProfile = self.main_view.wait_select_single(
1005- "Standard",
1006- text="Myfirstname Mylastname")
1007- self.assertThat(myProfile.text,
1008- Eventually(Equals("Myfirstname Mylastname")))
1009- self.pointing_device.click_object(myProfile)
1010-
1011- # click on delete
1012- deleteButton = self.main_view.get_edit_profile_delete_button()
1013- self.assertThat(deleteButton.buttonText, Eventually(Equals("Delete")))
1014- self.pointing_device.click_object(deleteButton)
1015-
1016- # check and make sure the profile is gone
1017-
1018- def _wait_to_stop_moving(self):
1019- self.main_view.select_single(
1020- 'QQuickFlickable',
1021- objectName='settingsContainer').moving.wait_for(False)
1022-
1023 def _set_difficulty(self, selection, label):
1024 # open settings tab
1025- self.main_view.switch_to_tab("settingsTab")
1026+ self.app.main_view.switch_to_tab("settingsTab")
1027
1028 # set the difficulty of the game
1029- difficulty = self.main_view.get_difficulty_selector()
1030+ difficulty = self.app.main_view.get_difficulty_selector()
1031
1032 # select Easy
1033 difficulty.select_option('Label', text=label)
1034@@ -388,39 +312,40 @@
1035
1036 def _verify_game_start(self, askmode=False, button=None):
1037 # check the game starts properly (according to difficulty)
1038- self.main_view.switch_to_tab("MainTab")
1039- header = self.main_view.get_header()
1040+ self.app.main_view.switch_to_tab("MainTab")
1041+ header = self.app.main_view.get_header()
1042 header.click_action_button('newgamebutton')
1043
1044 # if we're in ask mode, make sure we can grab all the buttons
1045 # and click the proper button
1046 if askmode:
1047 self.assertThat(
1048- self.main_view.get_new_game_easy_button().buttonText,
1049+ self.app.main_view.get_new_game_easy_button().buttonText,
1050 Eventually(Equals("Easy")))
1051 self.assertThat(
1052- self.main_view.get_new_game_moderate_button().buttonText,
1053+ self.app.main_view.get_new_game_moderate_button().buttonText,
1054 Eventually(Equals("Moderate")))
1055 self.assertThat(
1056- self.main_view.get_new_game_hard_button().buttonText,
1057+ self.app.main_view.get_new_game_hard_button().buttonText,
1058 Eventually(Equals("Hard")))
1059 self.assertThat(
1060- self.main_view.get_new_game_ultrahard_button().buttonText,
1061+ self.app.main_view.get_new_game_ultrahard_button().buttonText,
1062 Eventually(Equals("Ultra\nHard")))
1063 self.pointing_device.click_object(
1064- self.main_view.get_new_game_button(button))
1065+ self.app.main_view.get_new_game_button(button))
1066
1067- number_of_hints = lambda: self.main_view.select_single(
1068+ number_of_hints = lambda: self.app.main_view.select_single(
1069 objectName="blockgrid").numberOfHints
1070- number_of_actions = lambda: self.main_view.select_single(
1071+ number_of_actions = lambda: self.app.main_view.select_single(
1072 objectName="blockgrid").numberOfActions
1073
1074 self.assertThat(number_of_hints, Eventually(Equals(0)))
1075 self.assertThat(number_of_actions, Eventually(Equals(0)))
1076
1077 # verify clock is moving
1078- game_seconds = self.main_view.select_single(
1079+ game_seconds = self.app.main_view.select_single(
1080 objectName="blockgrid").gameSeconds
1081 self.assertThat(
1082- self.main_view.select_single(objectName="blockgrid").gameSeconds,
1083+ self.app.main_view.select_single(
1084+ objectName="blockgrid").gameSeconds,
1085 Eventually(NotEquals(game_seconds)))

Subscribers

People subscribed via source and target branches