Merge lp:~om26er/address-book-app/abook_navigation_favorites into lp:address-book-app

Proposed by Omer Akram
Status: Superseded
Proposed branch: lp:~om26er/address-book-app/abook_navigation_favorites
Merge into: lp:address-book-app
Diff against target: 1149 lines (+582/-248)
22 files modified
debian/control (+1/-0)
src/imports/ContactList/ContactListPage.qml (+0/-1)
src/imports/ContactView/ContactDetailFavoriteView.qml (+14/-8)
src/imports/ContactView/ContactDetailPhoneNumbersView.qml (+1/-17)
src/imports/ContactView/ContactView.qml (+10/-0)
src/imports/Ubuntu/Contacts/CMakeLists.txt (+0/-1)
src/imports/Ubuntu/Contacts/ContactListView.qml (+70/-73)
src/imports/Ubuntu/Contacts/FavoriteDelegate.qml (+0/-112)
src/imports/Ubuntu/Contacts/qmldir (+0/-1)
tests/autopilot/address_book_app/emulators/__init__.py (+12/-0)
tests/autopilot/address_book_app/emulators/contact_list_page.py (+152/-26)
tests/autopilot/address_book_app/emulators/contact_view.py (+94/-0)
tests/autopilot/address_book_app/emulators/main_window.py (+3/-3)
tests/autopilot/address_book_app/helpers.py (+55/-0)
tests/autopilot/address_book_app/tests/__init__.py (+25/-6)
tests/autopilot/address_book_app/tests/test_add_contact.py (+4/-0)
tests/autopilot/address_book_app/tests/test_contactlist.py (+4/-0)
tests/autopilot/address_book_app/tests/test_delete_contact.py (+1/-0)
tests/autopilot/address_book_app/tests/test_edit_contact.py (+4/-0)
tests/autopilot/address_book_app/tests/test_favorites.py (+130/-0)
tests/autopilot/address_book_app/tests/test_multiple_pick_mode.py (+1/-0)
tests/autopilot/address_book_app/tests/test_single_pick_mode.py (+1/-0)
To merge this branch: bzr merge lp:~om26er/address-book-app/abook_navigation_favorites
Reviewer Review Type Date Requested Status
Ubuntu Phablet Team Pending
Review via email: mp+211430@code.launchpad.net

This proposal has been superseded by a proposal from 2014-03-17.

To post a comment you must log in.
161. By Omer Akram

remove unused test code

162. By Omer Akram

fix pep8 and pyflakes warnings for test_favorite, did not touch other modules

163. By Omer Akram

remove syncevolution dep

164. By Omer Akram

remove unused test code, fix things

165. By Omer Akram

make dummy backend code reusable by other test cases

166. By Omer Akram

add docstrings for new tests

167. By Omer Akram

make starting of dummy backend a reusable helper for other apps to consume

168. By Omer Akram

adapt autopilot tests to UI changes in trunk

169. By Omer Akram

merge trunk

Unmerged revisions

169. By Omer Akram

merge trunk

168. By Omer Akram

adapt autopilot tests to UI changes in trunk

167. By Omer Akram

make starting of dummy backend a reusable helper for other apps to consume

166. By Omer Akram

add docstrings for new tests

165. By Omer Akram

make dummy backend code reusable by other test cases

164. By Omer Akram

remove unused test code, fix things

163. By Omer Akram

remove syncevolution dep

162. By Omer Akram

fix pep8 and pyflakes warnings for test_favorite, did not touch other modules

161. By Omer Akram

remove unused test code

160. By Omer Akram

don't use memory backend for favorite tests, use the new dummy backend from the service

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1=== modified file 'debian/control'
2--- debian/control 2013-12-11 15:03:32 +0000
3+++ debian/control 2014-03-17 23:53:00 +0000
4@@ -68,6 +68,7 @@
5 libqt5test5,
6 libqt5widgets5,
7 ubuntu-ui-toolkit-autopilot,
8+ syncevolution,
9 address-book-app (>= ${binary:Version}),
10 Description: Test package for address-book-app
11 Autopilot tests for the address-book-app package
12
13=== modified file 'src/imports/ContactList/ContactListPage.qml'
14--- src/imports/ContactList/ContactListPage.qml 2014-01-30 21:13:08 +0000
15+++ src/imports/ContactList/ContactListPage.qml 2014-03-17 23:53:00 +0000
16@@ -72,7 +72,6 @@
17 id: contactList
18 objectName: "contactListView"
19
20- showFavoritePhoneLabel: false
21 multiSelectionEnabled: true
22 acceptAction.text: pickMode ? i18n.tr("Select") : i18n.tr("Delete")
23 multipleSelection: !pickMode ||
24
25=== modified file 'src/imports/ContactView/ContactDetailFavoriteView.qml'
26--- src/imports/ContactView/ContactDetailFavoriteView.qml 2013-07-04 20:57:19 +0000
27+++ src/imports/ContactView/ContactDetailFavoriteView.qml 2014-03-17 23:53:00 +0000
28@@ -24,15 +24,21 @@
29 id: root
30
31 detail: root.contact ? root.contact.favorite : null
32- Image {
33+ showDivider: false
34+
35+ Icon {
36+ id: icon
37+ objectName: 'favoriteIcon'
38+
39 anchors.fill: parent
40- source: root.detail && root.detail.favorite ? "artwork:/favorite-selected.png" : "artwork:/favorite-unselected.png"
41+ name: root.detail && root.detail.favorite ? "favorite-selected" : "favorite-unselected"
42+ color: UbuntuColors.orange
43 MouseArea {
44- anchors.fill: parent
45- onClicked: {
46- root.detail.favorite = !root.detail.favorite
47- //TODO: save favorite if not in edit mode
48- }
49+ anchors.fill: parent
50+ onClicked: {
51+ root.detail.favorite = !root.detail.favorite
52+ root.contact.save()
53+ }
54 }
55- }
56+ }
57 }
58
59=== modified file 'src/imports/ContactView/ContactDetailPhoneNumbersView.qml'
60--- src/imports/ContactView/ContactDetailPhoneNumbersView.qml 2014-01-14 20:43:59 +0000
61+++ src/imports/ContactView/ContactDetailPhoneNumbersView.qml 2014-03-17 23:53:00 +0000
62@@ -36,28 +36,12 @@
63
64 detailDelegate: ContactDetailPhoneNumberView {
65 property variant detailType: detail && root.contact && root.typeModel.ready ? root.getType(detail) : null
66- property bool isPreferred: root.contact && root.contact.preferredDetails && detail && root.contact.isPreferredDetail("TEL", detail)
67-
68- action: Action {
69- objectName: "favoriteAction"
70-
71- text: i18n.tr("Favorite")
72- iconSource: (contact.favorite.favorite && isPreferred) ? "artwork:/favorite-selected.svg" : "artwork:/favorite-unselected.svg"
73- }
74+
75 contact: root.contact
76 fields: root.fields
77 typeLabel: detailType ? detailType.label : ""
78
79 height: implicitHeight
80 width: root.width
81- onClicked: {
82- if (isPreferred && contact.favorite.favorite) {
83- contact.favorite.favorite = false
84- } else {
85- root.contact.setPreferredDetail("TEL", detail)
86- contact.favorite.favorite = true
87- }
88- contact.save()
89- }
90 }
91 }
92
93=== modified file 'src/imports/ContactView/ContactView.qml'
94--- src/imports/ContactView/ContactView.qml 2013-11-14 17:48:48 +0000
95+++ src/imports/ContactView/ContactView.qml 2014-03-17 23:53:00 +0000
96@@ -81,6 +81,16 @@
97 right: parent.right
98 }
99 height: implicitHeight
100+ ContactDetailFavoriteView {
101+ contact: root.contact
102+ anchors {
103+ left: parent.left
104+ bottom: parent.bottom
105+ margins: units.gu(2)
106+ }
107+ width: units.gu(4)
108+ height: units.gu(4)
109+ }
110 }
111
112 ContactDetailPhoneNumbersView {
113
114=== modified file 'src/imports/Ubuntu/Contacts/CMakeLists.txt'
115--- src/imports/Ubuntu/Contacts/CMakeLists.txt 2013-12-16 15:04:12 +0000
116+++ src/imports/Ubuntu/Contacts/CMakeLists.txt 2014-03-17 23:53:00 +0000
117@@ -11,7 +11,6 @@
118 ContactSearchListView.qml
119 ContactSimpleListView.qml
120 DialogButtons.qml
121- FavoriteDelegate.qml
122 MultipleSelectionListView.qml
123 MultipleSelectionVisualModel.qml
124 qmldir
125
126=== modified file 'src/imports/Ubuntu/Contacts/ContactListView.qml'
127--- src/imports/Ubuntu/Contacts/ContactListView.qml 2013-11-14 13:25:28 +0000
128+++ src/imports/Ubuntu/Contacts/ContactListView.qml 2014-03-17 23:53:00 +0000
129@@ -41,78 +41,75 @@
130 ContactSimpleListView {
131 id: root
132
133- /*!
134- \qmlproperty bool showFavoritePhoneLabel
135-
136- This property holds if the phone label should appear on favorite contact or not
137- By default this is set to true.
138- */
139- property bool showFavoritePhoneLabel: true
140-
141- header: Column {
142- objectName: "listHeader"
143-
144- width: parent.width
145- height: favouritesList.count > 0 ? childrenRect.height : 0
146-
147- ContactSimpleListView {
148- id: favouritesList
149- objectName: "favouritesList"
150-
151- manager: root.manager
152- header: ListItem.Header {
153- height: units.gu(5)
154+ property bool showFavourites: false
155+
156+ header: Rectangle {
157+ id: itemHeader
158+
159+ height: units.gu(4)
160+ anchors {
161+ left: parent.left
162+ right: parent.right
163+ }
164+ color: UbuntuColors.coolGrey
165+
166+ Row {
167+ anchors.fill: parent
168+ Label {
169+ id: lblAll
170+
171+ anchors {
172+ top: parent.top
173+ bottom: parent.bottom
174+ }
175+ width: parent.width / 2
176+ text: i18n.tr("All")
177+ horizontalAlignment: Text.AlignHCenter
178+ verticalAlignment: Text.AlignVCenter
179+ color: root.showFavourites ? UbuntuColors.warmGrey : UbuntuColors.orange
180+ MouseArea {
181+ anchors.fill: parent
182+ onClicked: root.showFavourites = false
183+ }
184+ }
185+
186+ Rectangle {
187+ anchors {
188+ top: parent.top
189+ bottom: parent.bottom
190+ margins: units.gu(1)
191+ }
192+ width: 1
193+ }
194+
195+ Label {
196+ id: lblFavourites
197+
198+ anchors {
199+ top: parent.top
200+ bottom: parent.bottom
201+ }
202+ width: parent.width / 2
203 text: i18n.tr("Favourites")
204- }
205-
206- anchors {
207- left: parent.left
208- right: parent.right
209- }
210-
211- height: (count > 0 && !root.isInSelectionMode) ? contentHeight : 0
212- onContactClicked: root.contactClicked(contact)
213- defaultAvatarImageUrl: root.defaultAvatarImageUrl
214- multiSelectionEnabled: false
215- interactive: false
216- showSections: false
217-
218- fetchHint: FetchHint {
219- optimizationHints: FetchHint.AllRequired
220- detailTypesHint: [ ContactDetail.Avatar,
221- ContactDetail.Favorite,
222- ContactDetail.Name,
223- ContactDetail.PhoneNumber ]
224- }
225-
226- filter: DetailFilter {
227- detail: ContactDetail.Favorite
228- field: Favorite.Favorite
229- value: true
230- matchFlags: DetailFilter.MatchExactly
231- }
232-
233- listDelegate: FavoriteDelegate {
234- showPhoneLabel: root.showFavoritePhoneLabel
235- defaultAvatarUrl: favouritesList.defaultAvatarImageUrl
236- onContactClicked: _fetchContact(index, contact)
237- }
238-
239- Behavior on height {
240- UbuntuNumberAnimation {}
241- }
242-
243- // WORKAROUND: Due a bug on the SDK Page component the page is nto correct positioned if it changes
244- // the size dynamically
245- onHeightChanged: {
246- root.contentY = -contentHeight * 2
247- root.returnToBounds()
248- }
249- }
250- ListItem.Header {
251- height: favouritesList.count > 0 ? units.gu(5) : 0
252- visible: height > 0
253- text: i18n.tr("All contacts")
254- }
255- }
256+ horizontalAlignment: Text.AlignHCenter
257+ verticalAlignment: Text.AlignVCenter
258+ color: root.showFavourites ? UbuntuColors.orange : UbuntuColors.warmGrey
259+ MouseArea {
260+ anchors.fill: parent
261+ onClicked: root.showFavourites = true
262+ }
263+ }
264+ }
265+ }
266+
267+ DetailFilter {
268+ id: favouritesFilter
269+
270+ detail: ContactDetail.Favorite
271+ field: Favorite.Favorite
272+ value: true
273+ matchFlags: DetailFilter.MatchExactly
274+ }
275+
276+ filter: showFavourites ? favouritesFilter : null
277 }
278
279=== removed file 'src/imports/Ubuntu/Contacts/FavoriteDelegate.qml'
280--- src/imports/Ubuntu/Contacts/FavoriteDelegate.qml 2013-10-11 00:12:19 +0000
281+++ src/imports/Ubuntu/Contacts/FavoriteDelegate.qml 1970-01-01 00:00:00 +0000
282@@ -1,112 +0,0 @@
283-/*
284- * Copyright (C) 2012-2013 Canonical, Ltd.
285- *
286- * This program is free software; you can redistribute it and/or modify
287- * it under the terms of the GNU General Public License as published by
288- * the Free Software Foundation; version 3.
289- *
290- * This program is distributed in the hope that it will be useful,
291- * but WITHOUT ANY WARRANTY; without even the implied warranty of
292- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
293- * GNU General Public License for more details.
294- *
295- * You should have received a copy of the GNU General Public License
296- * along with this program. If not, see <http://www.gnu.org/licenses/>.
297- */
298-
299-import QtQuick 2.0
300-import Ubuntu.Components 0.1
301-import Ubuntu.Components.ListItems 0.1 as ListItem
302-import QtContacts 5.0
303-import "Contacts.js" as ContactsJS
304-
305-ListItem.Empty {
306- id: favoriteItem
307-
308- property int index: -1
309- property bool showAvatar: true
310- property string defaultAvatarUrl: ""
311- property int titleDetail: ContactDetail.Name
312- property variant titleFields: [ Name.FirstName, Name.LastName ]
313- property bool showPhoneLabel: true
314-
315- signal contactClicked(var index, var contact)
316-
317- implicitHeight: units.gu(8)
318- width: parent ? parent.width : 0
319-
320- Rectangle {
321- anchors {
322- fill: parent
323- bottomMargin: units.dp(1)
324- }
325- color: UbuntuColors.warmGrey
326- opacity: 0.07
327- }
328-
329- UbuntuShape {
330- id: avatar
331-
332- anchors {
333- left: parent.left
334- leftMargin: units.gu(2)
335- top: parent.top
336- topMargin: units.gu(1)
337- bottom: parent.bottom
338- bottomMargin: units.gu(1)
339- }
340- width: favoriteItem.showAvatar ? height : 0
341- visible: favoriteItem.showAvatar
342- radius: "medium"
343- image: Image {
344- property bool isDefaultAvatar: (source == favoriteItem.defaultAvatarUrl)
345-
346- fillMode: Image.PreserveAspectCrop
347- asynchronous: true
348- source: ContactsJS.getAvatar(contact, favoriteItem.defaultAvatarUrl)
349- sourceSize.width: isDefaultAvatar ? undefined : width * 1.5
350- sourceSize.height: isDefaultAvatar ? undefined : height * 1.5
351- }
352- }
353-
354- Column {
355- anchors {
356- top: parent.top
357- topMargin: units.gu(2)
358- left: avatar.right
359- leftMargin: units.gu(2)
360- right: parent.right
361- bottom: parent.bottom
362- }
363-
364- Label {
365- id: name
366-
367- anchors {
368- left: parent.left
369- right: parent.right
370- }
371-
372- height: favoriteItem.showPhoneLabel ? paintedHeight : paintedHeight * 2
373- verticalAlignment: Text.AlignVCenter
374- text: ContactsJS.formatToDisplay(contact, favoriteItem.titleDetail, favoriteItem.titleFields)
375- fontSize: "medium"
376- }
377-
378- Label {
379- id: label
380-
381- anchors {
382- left: parent.left
383- right: parent.right
384- }
385-
386- opacity: 0.2
387- height: favoriteItem.showPhoneLabel ? paintedHeight : 0
388- text: favoriteItem.showPhoneLabel && contact.phoneNumbers ? ContactsJS.getFavoritePhoneLabel(contact, "") : ""
389- fontSize: "medium"
390- }
391- }
392-
393- onClicked: favoriteItem.contactClicked(index, contact)
394-}
395
396=== modified file 'src/imports/Ubuntu/Contacts/qmldir'
397--- src/imports/Ubuntu/Contacts/qmldir 2013-10-21 19:25:51 +0000
398+++ src/imports/Ubuntu/Contacts/qmldir 2014-03-17 23:53:00 +0000
399@@ -10,7 +10,6 @@
400
401 internal ContactDetailPickerDelegate ContactDetailPickerDelegate.qml
402 internal ContactDetailPickerPhoneNumberDelegate ContactDetailPickerPhoneNumberDelegate.qml
403-internal ContactFavoriteListDelegate ContactFavoriteDelegate.qml
404 internal DialogButtons DialogButtons.qml
405 internal OrganicView OrganicView.qml
406 internal ContactList ContactList.js
407
408=== modified file 'tests/autopilot/address_book_app/emulators/__init__.py'
409--- tests/autopilot/address_book_app/emulators/__init__.py 2013-07-09 18:42:30 +0000
410+++ tests/autopilot/address_book_app/emulators/__init__.py 2014-03-17 23:53:00 +0000
411@@ -4,3 +4,15 @@
412 # This program is free software: you can redistribute it and/or modify it
413 # under the terms of the GNU General Public License version 3, as published
414 # by the Free Software Foundation.
415+
416+
417+def get_nearest_parent_matching_type(child, desired_type):
418+ """Climb down the widget tree, return first instance of desired_type"""
419+ while type(child).__name__ != desired_type:
420+ parent = child.get_parent()
421+ if type(child).__name__ == 'AddressBookApp':
422+ # we reached the root and found nothing
423+ return None
424+ else:
425+ child = parent
426+ return child
427
428=== modified file 'tests/autopilot/address_book_app/emulators/contact_list_page.py'
429--- tests/autopilot/address_book_app/emulators/contact_list_page.py 2014-02-13 16:44:12 +0000
430+++ tests/autopilot/address_book_app/emulators/contact_list_page.py 2014-03-17 23:53:00 +0000
431@@ -1,20 +1,24 @@
432 # -*- Mode: Python; coding: utf-8; indent-tabs-mode: nil; tab-width: 4 -*-
433-
434-""" ContactListPage emulator for Addressbook App tests """
435-
436 # Copyright 2014 Canonical
437 #
438 # This program is free software: you can redistribute it and/or modify it
439 # under the terms of the GNU General Public License version 3, as published
440 # by the Free Software Foundation.
441
442+""" ContactListPage emulator for Addressbook App tests """
443+
444 import logging
445+from time import sleep
446
447+from autopilot import logging as autopilot_logging
448 from autopilot.introspection.dbus import StateNotFoundError
449+from autopilot.matchers import Eventually
450+from testtools.matchers import GreaterThan
451 from ubuntuuitoolkit import emulators as uitk
452
453-LOGGER = logging.getLogger(__name__)
454-from time import sleep
455+from address_book_app.emulators import get_nearest_parent_matching_type
456+
457+logger = logging.getLogger(__name__)
458
459
460 class ContactListPage(uitk.UbuntuUIToolkitEmulatorBase):
461@@ -27,9 +31,12 @@
462 super(ContactListPage, self).__init__(*args)
463
464 def get_contacts(self):
465- """
466- Returns a list of ContactDelegate objects and populate
467- self.selection_marks
468+ """Returns a list of ContactDelegate objects.
469+
470+ Also populate self.selection_marks.
471+
472+ :return: The list of ContactDelegate objects.
473+
474 """
475 sleep(1)
476 self.contacts = self.select_many("ContactDelegate")
477@@ -41,10 +48,12 @@
478 self.selection_marks.append(mark)
479 return self.contacts
480
481+ @autopilot_logging.log_action(logger.info)
482 def select_contacts_by_index(self, indices):
483- """ Select contacts corresponding to the list of index in indices
484-
485- :param indices: List of integers
486+ """Select contacts corresponding to the list of index in indices.
487+
488+ :param indices: List of integer indices of contacts to select.
489+
490 """
491 self.deselect_all()
492
493@@ -53,8 +62,9 @@
494 self.selected_marks.append(self.selection_marks[idx])
495 self.pointing_device.click_object(self.selection_marks[idx])
496
497+ @autopilot_logging.log_action(logger.info)
498 def deselect_all(self):
499- """Deselect every contacts"""
500+ """Deselect every contact."""
501 contacts = self.select_many("ContactDelegate")
502 self.selected_marks = []
503 for contact in contacts:
504@@ -63,28 +73,144 @@
505 objectName="selectionMark")
506 self.pointing_device.click_object(mark)
507
508+ @autopilot_logging.log_action(logger.info)
509 def click_button(self, objectname):
510 """Press a button that matches objectname
511
512- :param objectname: Name of the object
513+ :param objectname: The name of the object.
514+ :raise StateNotFoundError: When a matching button is not found.
515+
516 """
517- try:
518- buttons = self.select_many("Button",
519- objectName=objectname)
520- for button in buttons:
521- if button.visible:
522- self.pointing_device.click_object(button)
523- except StateNotFoundError:
524- LOGGER.error(
525- 'Button with objectName "{0}" not found.'.format(objectname)
526- )
527- raise
528+ button = self.select_single("Button",
529+ objectName=objectname,
530+ visible=True)
531+ self.pointing_device.click_object(button)
532
533+ @autopilot_logging.log_action(logger.info)
534 def cancel(self):
535- """Press the cancel button displayed when pick mode is enabled"""
536+ """Press the cancel button displayed when pick mode is enabled."""
537 self.click_button("DialogButtons.rejectButton")
538
539+ @autopilot_logging.log_action(logger.info)
540 def delete(self):
541- """Press the delete button displayed when pick mode is enabled"""
542+ """Press the delete button displayed when pick mode is enabled."""
543 self.click_button("DialogButtons.acceptButton")
544 self.get_contacts()
545+
546+ def get_contact_by_name(self,
547+ contact_name,
548+ parent_delegate_type='ContactDelegate'):
549+ """Find a label with text matching contact name.
550+
551+ :param contact_name: The name of the contact, e.g. 'Fulano de Tal'.
552+ :param parent_delegate_type: 'ContactDelegate' or 'FavoriteDelegate'.
553+ :return: The label for a matching contact.
554+ :raises StateNotFoundError: If the contact_name is not found.
555+
556+ """
557+ try:
558+ assert(lambda: len(self.select_many("Label",
559+ text=contact_name)),
560+ Eventually(GreaterThan(0)))
561+ contact_name_labels = self.select_many(
562+ "Label",
563+ text=contact_name)
564+ for contact_name_label in contact_name_labels:
565+ # we could have a contact or a favorite
566+ if get_nearest_parent_matching_type(
567+ contact_name_label,
568+ parent_delegate_type):
569+ return contact_name_label
570+ raise StateNotFoundError('Label')
571+ except StateNotFoundError:
572+ logger.error("Contact {} not found.".format(contact_name))
573+ raise
574+
575+ def get_favorite_by_name(self, contact_name):
576+ """Find a label with text matching contact name under Favorites.
577+
578+ :param contact_name: Name of the contact, e.g. 'Fulano de Tal'.
579+ :return: The label for the metching favorite.
580+ :raises StateNotFoundError: If the contact_name is not found.
581+
582+ """
583+ return self.get_contact_by_name(
584+ contact_name,
585+ parent_delegate_type='FavoriteDelegate')
586+
587+ @autopilot_logging.log_action(logger.info)
588+ def click_contact_by_name(self,
589+ contact_name,
590+ parent_delegate_type='ContactDelegate'):
591+ """Click a contact with label matching the given contact name.
592+
593+ :param contact_name: Name of a contact, e.g. 'Fulano de Tal'.
594+ :param parent_delegate_type: 'ContactDelegate' or 'FavoriteDelegate'.
595+ :raises StateNotFoundError: If the contact_name is not found.
596+
597+ """
598+ contact_name_label = self.get_contact_by_name(
599+ contact_name, parent_delegate_type)
600+ self.pointing_device.click_object(contact_name_label)
601+
602+ @autopilot_logging.log_action(logger.info)
603+ def open_contact_view_by_contact_name(self, contact_name):
604+ """Open the contact view page for a contact with the given name.
605+
606+ :param contact_name: The name of the contact, e.g. 'Abe Lincoln'.
607+ :return: The ContactView for the contact.
608+ :raises StateNotFoundError: If the name is not found.
609+
610+ """
611+ self.click_contact_by_name(contact_name)
612+
613+ @autopilot_logging.log_action(logger.info)
614+ def click_favorite_by_name(self,
615+ contact_name):
616+ """Click a favorite with label matching the given contact name.
617+
618+ :param contact_name: Name of a contact, e.g. 'Fulano de Tal'
619+ :raises StateNotFoundError: If the contact_name is not found.
620+
621+ """
622+ contact_name_label = self.get_favorite_by_name(
623+ contact_name)
624+ self.pointing_device.click_object(contact_name_label)
625+
626+ @autopilot_logging.log_action(logger.info)
627+ def open_contact_view_by_favorite_name(self, favorite_name):
628+ """Open the contact view page for a favorite with the given name.
629+
630+ :param favorite_name: The name of the favorite, e.g. 'Abe Lincoln'.
631+ :return: The ContactView for the contact.
632+ :raises StateNotFoundError: If the name is not found.
633+
634+ """
635+ self.click_favorite_by_name(favorite_name)
636+
637+ @autopilot_logging.log_action(logger.info)
638+ def click_phone_number(self, phone_number):
639+ """Click a label matching the given phone number.
640+
641+ :param phone_number: A phone number, e.g. '3321 1232'.
642+ :raises StateNotFoundError: If the phone_number is not found.
643+
644+ """
645+ phone_number_label = self.wait_select_single('Label',
646+ text=phone_number)
647+ self.pointing_device.click_object(phone_number_label)
648+
649+ def contact_is_favorite(self, name):
650+ """Is the given contact name a favorite?
651+
652+ :param contact_name: The name of a contact e.g. John.
653+ :raises StateNotFoundError: If the contact name is not found.
654+ :returns: Boolean.
655+
656+ """
657+ try:
658+ self.get_favorite_by_name(name)
659+ return True
660+ except StateNotFoundError:
661+ logger.error("Favorite contact {} not found.".format(name))
662+ return False
663
664=== added file 'tests/autopilot/address_book_app/emulators/contact_view.py'
665--- tests/autopilot/address_book_app/emulators/contact_view.py 1970-01-01 00:00:00 +0000
666+++ tests/autopilot/address_book_app/emulators/contact_view.py 2014-03-17 23:53:00 +0000
667@@ -0,0 +1,94 @@
668+# -*- Mode: Python; coding: utf-8; indent-tabs-mode: nil; tab-width: 4 -*-
669+# Copyright 2014 Canonical
670+#
671+# This program is free software: you can redistribute it and/or modify it
672+# under the terms of the GNU General Public License version 3, as published
673+# by the Free Software Foundation.
674+
675+""" ContactView emulator for Addressbook App tests """
676+
677+import logging
678+
679+from autopilot import logging as autopilot_logging
680+from autopilot.introspection.dbus import StateNotFoundError
681+from ubuntuuitoolkit import emulators as uitk
682+
683+from address_book_app.emulators import get_nearest_parent_matching_type
684+
685+logger = logging.getLogger(__name__)
686+
687+
688+class ContactView(uitk.UbuntuUIToolkitEmulatorBase):
689+ """ContactView emulator class"""
690+
691+ def __init__(self, *args):
692+ super(ContactView, self).__init__(*args)
693+
694+ @autopilot_logging.log_action(logger.info)
695+ def click_button(self, objectname):
696+ """Press a button that matches objectname.
697+
698+ :param objectname: The name of the object.
699+ :raises StateNotFoundError: If the matching object is not found.
700+
701+ """
702+ buttons = self.select_single("Button",
703+ objectName=objectname,
704+ visible=True)
705+ self.pointing_device.click_object(button)
706+
707+ @autopilot_logging.log_action(logger.info)
708+ def cancel(self):
709+ """Press the cancel button displayed when pick mode is enabled."""
710+ self.click_button("DialogButtons.rejectButton")
711+
712+ @autopilot_logging.log_action(logger.info)
713+ def delete(self):
714+ """Press the delete button displayed when pick mode is enabled."""
715+ self.click_button("DialogButtons.acceptButton")
716+ self.get_contacts()
717+
718+ def get_contact_detail_phone_number_view(self, phone_number):
719+ """Returns the View associated with the given phone number.
720+
721+ :param phone_number: The number listed for contact, e.g. '8675309'.
722+ :return: The matching view.
723+ :raises StateNotFoundError: If the phone_number is not found.
724+
725+ """
726+ phone_number_label = self.select_single('Label',
727+ text=phone_number)
728+ return get_nearest_parent_matching_type(
729+ phone_number_label,
730+ 'ContactDetailPhoneNumberView')
731+
732+ def _star_is_illuminated_for_phone_number(self, phone_number):
733+ """Is the star associated with the given phone number illuminated?
734+
735+ :param phone_number: The number listed for contact, e.g. '8675309'.
736+ :return: A Boolean.
737+ :raises StateNotFoundError: If the phone_number is not found.
738+
739+ """
740+ contact_detail = self.get_contact_detail_phone_number_view(
741+ phone_number)
742+ return contact_detail.iconSource == 'artwork:/favorite-selected.svg'
743+
744+ @autopilot_logging.log_action(logger.info)
745+ def click_phone_number(self, phone_number):
746+ """Tap/click the label with text matching the given phone number.
747+
748+ :param phone_number: The number listed for contact, e.g. '8675309'.
749+ :raises StateNotFoundError: If the phone_number is not found.
750+
751+ """
752+ phone_number_label = self.wait_select_single('Label',
753+ text=phone_number)
754+ self.pointing_device.click_object(phone_number_label)
755+
756+ @autopilot_logging.log_action(logger.info)
757+ def click_favorite_icon(self):
758+ """Tap/click on the favorite icon to make a contact favorite."""
759+ favorite_icon = self.select_single('Icon', objectName='favoriteIcon')
760+
761+ self.pointing_device.click_object(favorite_icon)
762
763=== modified file 'tests/autopilot/address_book_app/emulators/main_window.py'
764--- tests/autopilot/address_book_app/emulators/main_window.py 2014-02-13 16:44:12 +0000
765+++ tests/autopilot/address_book_app/emulators/main_window.py 2014-03-17 23:53:00 +0000
766@@ -13,7 +13,7 @@
767
768 def get_contact_list_page(self):
769 return self. wait_select_single("ContactListPage",
770- objectName="contactListPage")
771+ objectName="contactListPage")
772
773 def get_contact_edit_page(self):
774 return self.wait_select_single("ContactEditor",
775@@ -35,7 +35,7 @@
776 """
777 Returns a ContactListView iobject for the current window
778 """
779- return self.wait_select_single( "ContactListView",
780+ return self.wait_select_single("ContactListView",
781 objectName="contactListView")
782
783 def get_button(self, name):
784@@ -45,7 +45,7 @@
785 Arguments:
786 name: Name of the button
787 """
788- return self.wait_select_single( "Button", objectName=name)
789+ return self.wait_select_single("Button", objectName=name)
790
791 def cancel(self):
792 """
793
794=== added file 'tests/autopilot/address_book_app/helpers.py'
795--- tests/autopilot/address_book_app/helpers.py 1970-01-01 00:00:00 +0000
796+++ tests/autopilot/address_book_app/helpers.py 2014-03-17 23:53:00 +0000
797@@ -0,0 +1,55 @@
798+# -*- Mode: Python; coding: utf-8; indent-tabs-mode: nil; tab-width: 4 -*-
799+# Copyright 2014 Canonical
800+#
801+# This program is free software: you can redistribute it and/or modify it
802+# under the terms of the GNU General Public License version 3, as published
803+# by the Free Software Foundation.
804+
805+"""Utilities for the evolution contacts db."""
806+
807+import os
808+import subprocess
809+import tempfile
810+
811+
812+def backup_evolution_contacts_db(backup_filepath=None):
813+ """Back up the existing evolution contacts db to a file.
814+
815+ :param backup_filepath: file to which to write, if None make tmpfile.
816+ :returns: backup filepath used.
817+ """
818+ if backup_filepath is None:
819+ backup_filepath = tempfile.mktemp(suffix=".syncevolution")
820+ subprocess.call([
821+ 'syncevolution',
822+ '--export',
823+ backup_filepath,
824+ 'backend=evolution-contacts',
825+ ])
826+ return backup_filepath
827+
828+
829+def restore_evolution_contacts_db(backup_filepath):
830+ """Restore the evolution contacts db from the backup.
831+
832+ :param backup_filepath: file from which to restore.
833+ """
834+ # NOTE restoring from empty file creates a phantom contact
835+ if os.path.getsize(backup_filepath) > 0:
836+ subprocess.call([
837+ 'syncevolution',
838+ '--import',
839+ backup_filepath,
840+ 'backend=evolution-contacts'
841+ ])
842+
843+
844+def wipe_evolution_contacts_db():
845+ """Wipe the existing evolution contacts db clean of contacts."""
846+ subprocess.call([
847+ 'syncevolution',
848+ '--delete-items',
849+ 'backend=evolution-contacts',
850+ '--luids',
851+ '*',
852+ ])
853
854=== modified file 'tests/autopilot/address_book_app/tests/__init__.py'
855--- tests/autopilot/address_book_app/tests/__init__.py 2014-01-30 08:35:04 +0000
856+++ tests/autopilot/address_book_app/tests/__init__.py 2014-03-17 23:53:00 +0000
857@@ -18,6 +18,9 @@
858 from testtools.matchers import Equals
859
860 from address_book_app.emulators.main_window import MainWindow
861+from address_book_app.helpers import (backup_evolution_contacts_db,
862+ restore_evolution_contacts_db,
863+ wipe_evolution_contacts_db)
864 from ubuntuuitoolkit import emulators as toolkit_emulators
865
866
867@@ -31,6 +34,7 @@
868 VCARD_PATH_DEV = os.path.abspath("../data/vcard.vcf")
869 ARGS = []
870 PRELOAD_VCARD = False
871+ QTCONTACTS_MANAGER_OVERRIDE_MEMORY = True
872
873 def setUp(self):
874 self.pointing_device = toolkit_emulators.get_pointing_device()
875@@ -40,13 +44,25 @@
876 if model() != "Desktop":
877 subprocess.check_call(["/sbin/initctl", "stop", "maliit-server"])
878
879+ self.backup_filepath = backup_evolution_contacts_db()
880+ wipe_evolution_contacts_db()
881+
882 if 'AUTOPILOT_APP' in os.environ:
883 self.app_bin = os.environ['AUTOPILOT_APP']
884 else:
885 self.app_bin = AddressBookAppTestCase.DEFAULT_DEV_LOCATION
886
887- os.environ['QTCONTACTS_MANAGER_OVERRIDE'] = 'memory'
888+ print "Running from: %s" % (self.app_bin)
889+ # NOTE defeats favoriting: contacts don't show up in "Favorites" header
890+ if AddressBookAppTestCase.QTCONTACTS_MANAGER_OVERRIDE_MEMORY:
891+ os.environ['QTCONTACTS_MANAGER_OVERRIDE'] = 'memory'
892+ else:
893+ try:
894+ del(os.environ['QTCONTACTS_MANAGER_OVERRIDE'])
895+ except KeyError:
896+ pass
897 vcard_data = ""
898+
899 if AddressBookAppTestCase.PRELOAD_VCARD:
900 # Use vcard from source tree and fallback on installed vcard (from
901 # address-book-app-autopilot package)
902@@ -55,16 +71,16 @@
903 else:
904 vcard_data = AddressBookAppTestCase.VCARD_PATH_BIN
905
906- os.environ["ADDRESS_BOOK_TEST_DATA"] = vcard_data
907- if vcard_data != "": print "Using vcard %s" % vcard_data
908+ os.environ['ADDRESS_BOOK_TEST_DATA'] = vcard_data
909+ if vcard_data != '': print 'Using vcard %s' % vcard_data
910 if os.path.exists(self.app_bin):
911- print "Running from: %s" % (self.app_bin)
912+ print 'Running from: %s' % (self.app_bin)
913 self.launch_test_local()
914 elif os.path.exists(self.DEB_LOCALTION):
915- print "Running from: %s" % (self.DEB_LOCALTION)
916+ print 'Running from: %s' % (self.DEB_LOCALTION)
917 self.launch_test_installed()
918 else:
919- print "Running from click package: address-book-app"
920+ print 'Running from click package: address-book-app'
921 self.launch_click_installed()
922
923 AddressBookAppTestCase.ARGS = []
924@@ -73,6 +89,9 @@
925
926 def tearDown(self):
927 super(AddressBookAppTestCase, self).tearDown()
928+ wipe_evolution_contacts_db()
929+ restore_evolution_contacts_db(self.backup_filepath)
930+ os.remove(self.backup_filepath)
931
932 # start the vkb
933 if model() != "Desktop":
934
935=== modified file 'tests/autopilot/address_book_app/tests/test_add_contact.py'
936--- tests/autopilot/address_book_app/tests/test_add_contact.py 2014-01-28 10:20:07 +0000
937+++ tests/autopilot/address_book_app/tests/test_add_contact.py 2014-03-17 23:53:00 +0000
938@@ -18,6 +18,10 @@
939 class TestAddContact(AddressBookAppTestCase):
940 """ Tests the Add contact """
941
942+ def setUp(self):
943+ AddressBookAppTestCase.QTCONTACTS_MANAGER_OVERRIDE_MEMORY = True
944+ super(TestAddContact, self).setUp()
945+
946 def test_add_and_cancel_contact(self):
947 list_page = self.main_window.get_contact_list_page()
948
949
950=== modified file 'tests/autopilot/address_book_app/tests/test_contactlist.py'
951--- tests/autopilot/address_book_app/tests/test_contactlist.py 2013-11-21 18:53:19 +0000
952+++ tests/autopilot/address_book_app/tests/test_contactlist.py 2014-03-17 23:53:00 +0000
953@@ -18,6 +18,10 @@
954 class TestContactList(AddressBookAppTestCase):
955 """Tests the contact list features"""
956
957+ def setUp(self):
958+ AddressBookAppTestCase.QTCONTACTS_MANAGER_OVERRIDE_MEMORY = True
959+ super(TestContactList, self).setUp()
960+
961 def test_contact_list(self):
962 contact_list = self.main_window.get_contact_list_page()
963 self.assertThat(contact_list.visible, Eventually(Equals(True)))
964
965=== modified file 'tests/autopilot/address_book_app/tests/test_delete_contact.py'
966--- tests/autopilot/address_book_app/tests/test_delete_contact.py 2014-02-13 16:44:12 +0000
967+++ tests/autopilot/address_book_app/tests/test_delete_contact.py 2014-03-17 23:53:00 +0000
968@@ -40,6 +40,7 @@
969 ]
970
971 def setUp(self):
972+ AddressBookAppTestCase.QTCONTACTS_MANAGER_OVERRIDE_MEMORY = True
973 AddressBookAppTestCase.PRELOAD_VCARD = True
974 super(TestDeleteSelectContact, self).setUp()
975
976
977=== modified file 'tests/autopilot/address_book_app/tests/test_edit_contact.py'
978--- tests/autopilot/address_book_app/tests/test_edit_contact.py 2014-01-28 10:20:07 +0000
979+++ tests/autopilot/address_book_app/tests/test_edit_contact.py 2014-03-17 23:53:00 +0000
980@@ -17,6 +17,10 @@
981 class TestEditContact(AddressBookAppTestCase):
982 """Tests edit a contact"""
983
984+ def setUp(self):
985+ AddressBookAppTestCase.QTCONTACTS_MANAGER_OVERRIDE_MEMORY = True
986+ super(TestEditContact, self).setUp()
987+
988 def test_add_new_phone(self):
989 self.add_contact("Fulano", "de Tal", ["3321 2300"])
990 edit_page = self.edit_contact(0)
991
992=== added file 'tests/autopilot/address_book_app/tests/test_favorites.py'
993--- tests/autopilot/address_book_app/tests/test_favorites.py 1970-01-01 00:00:00 +0000
994+++ tests/autopilot/address_book_app/tests/test_favorites.py 2014-03-17 23:53:00 +0000
995@@ -0,0 +1,130 @@
996+# -*- Mode: Python; coding: utf-8; indent-tabs-mode: nil; tab-width: 4 -*-
997+# Copyright 2014 Canonical
998+#
999+# This program is free software: you can redistribute it and/or modify it
1000+# under the terms of the GNU General Public License version 3, as published
1001+# by the Free Software Foundation.
1002+
1003+"""Tests of favoriting contacts for the Addressbook App"""
1004+
1005+from __future__ import absolute_import
1006+
1007+import subprocess
1008+
1009+from autopilot.introspection.dbus import StateNotFoundError
1010+from autopilot.matchers import Eventually
1011+from testtools.matchers import Equals
1012+
1013+from address_book_app.tests import AddressBookAppTestCase
1014+
1015+import dbus
1016+
1017+DUMMY_BACKEND_ADDRESS = 'com.canonical.test.pim'
1018+
1019+
1020+class TestFavorite(AddressBookAppTestCase):
1021+ """Test for favoriting contacts"""
1022+
1023+ def setUp(self):
1024+ AddressBookAppTestCase.QTCONTACTS_MANAGER_OVERRIDE_MEMORY = False
1025+ AddressBookAppTestCase.PRELOAD_VCARD = True
1026+ self.ensure_dummy_service_backend_running()
1027+ super(TestFavorite, self).setUp()
1028+
1029+ def _set_contact_service_dummy_backend_environment_variables(self):
1030+ self.patch_environment(
1031+ 'CANONICAL_PIN_SERVICE_NAME', DUMMY_BACKEND_ADDRESS
1032+ )
1033+ self.patch_environment(
1034+ 'FOLKS_BACKEND_PATH', self._get_dummy_backend_so_location()
1035+ )
1036+ self.patch_environment('FOLKS_BACKENDS_ALLOWED', 'dummy')
1037+ self.patch_environment('FOLKS_PRIMARY_STORE', 'dummy')
1038+
1039+ def _get_dummy_backend_so_location(self):
1040+ output = subprocess.check_output(
1041+ [
1042+ 'find', '/usr/lib', '-name', 'dummy.so'
1043+ ]).split()
1044+
1045+ if len(output) is 1:
1046+ return output[0]
1047+ elif len(output) > 1:
1048+ for path in output:
1049+ if 'address-book-service' in path:
1050+ return path[0]
1051+ else:
1052+ raise RuntimeError(
1053+ 'Dummy backend .so not found is address-book-service-dummy'
1054+ 'installed ?'
1055+ )
1056+
1057+ def _get_address_book_service_binary_location(self):
1058+ output = subprocess.check_output(
1059+ [
1060+ 'find', '/usr/lib', '-name', 'address-book-service'
1061+ ]).split()
1062+
1063+ for path in output:
1064+ if 'address-book-service/address-book-service' in path:
1065+ return path
1066+
1067+ def _start_service_dummy_backend(self):
1068+ self.service = subprocess.Popen(
1069+ [self._get_address_book_service_binary_location()]
1070+ )
1071+ self.addCleanup(self.service.kill)
1072+
1073+ self.assertThat(
1074+ lambda: self._is_dummy_backend_up(), Eventually(Equals(True))
1075+ )
1076+
1077+ def _is_dummy_backend_up(self):
1078+ interfaces = dbus.SessionBus().list_names()
1079+
1080+ return DUMMY_BACKEND_ADDRESS in interfaces
1081+
1082+ def ensure_dummy_service_backend_running(self):
1083+ self._set_contact_service_dummy_backend_environment_variables()
1084+ self._start_service_dummy_backend()
1085+
1086+ def _add_contact_as_favorite(self, name):
1087+ contact_list_page = self.main_window.get_contact_list_page()
1088+ contact_list_page.open_contact_view_by_contact_name(name)
1089+ contact_view = self.main_window.get_contact_view_page()
1090+ contact_view.click_favorite_icon()
1091+ self.main_window.go_back()
1092+
1093+ return contact_list_page
1094+
1095+ def test_add_favorite(self):
1096+ contact_list_page = self._add_contact_as_favorite('teste test34')
1097+
1098+ self.assertTrue(contact_list_page.contact_is_favorite('teste test34'))
1099+
1100+ def test_save_multiple_favorites(self):
1101+ contact_list_page = self._add_contact_as_favorite('teste test34')
1102+ self.assertTrue(contact_list_page.contact_is_favorite('teste test34'))
1103+
1104+ contact_list_page = self._add_contact_as_favorite('teste teste2')
1105+ self.assertTrue(contact_list_page.contact_is_favorite('teste teste2'))
1106+
1107+ def test_remove_favorite(self):
1108+ contact_list_page = self._add_contact_as_favorite('teste test34')
1109+ contact_list_page.open_contact_view_by_contact_name('teste test34')
1110+ contact_view = self.main_window.get_contact_view_page()
1111+ contact_view.click_favorite_icon()
1112+ self.main_window.go_back()
1113+ self.assertFalse(contact_list_page.contact_is_favorite('teste test34'))
1114+
1115+ def test_delete_favorite(self):
1116+ contact_list_page = self._add_contact_as_favorite('teste test34')
1117+ contact_list_page.click_favorite_by_name('teste test34')
1118+ toolbar = self.main_window.open_toolbar()
1119+ toolbar.click_button('delete')
1120+ self.assertRaises(
1121+ StateNotFoundError,
1122+ lambda: contact_list_page.get_favorite_by_name('teste test34'))
1123+ self.assertRaises(
1124+ StateNotFoundError,
1125+ lambda: contact_list_page.get_contact_by_name('teste test34'))
1126
1127=== modified file 'tests/autopilot/address_book_app/tests/test_multiple_pick_mode.py'
1128--- tests/autopilot/address_book_app/tests/test_multiple_pick_mode.py 2013-12-13 19:31:33 +0000
1129+++ tests/autopilot/address_book_app/tests/test_multiple_pick_mode.py 2014-03-17 23:53:00 +0000
1130@@ -20,6 +20,7 @@
1131
1132 def setUp(self):
1133 self.ARGS.append("addressbook:///pick?single=false")
1134+ AddressBookAppTestCase.QTCONTACTS_MANAGER_OVERRIDE_MEMORY = True
1135 AddressBookAppTestCase.PRELOAD_VCARD = True
1136 super(TestMultiplePickerMode, self).setUp()
1137
1138
1139=== modified file 'tests/autopilot/address_book_app/tests/test_single_pick_mode.py'
1140--- tests/autopilot/address_book_app/tests/test_single_pick_mode.py 2013-12-13 19:31:33 +0000
1141+++ tests/autopilot/address_book_app/tests/test_single_pick_mode.py 2014-03-17 23:53:00 +0000
1142@@ -21,6 +21,7 @@
1143 def setUp(self):
1144 AddressBookAppTestCase.ARGS.append("addressbook:///pick?single=true")
1145 AddressBookAppTestCase.PRELOAD_VCARD = True
1146+ AddressBookAppTestCase.QTCONTACTS_MANAGER_OVERRIDE_MEMORY = True
1147 super(TestSinglePickerMode, self).setUp()
1148
1149 def test_select_single_contact(self):

Subscribers

People subscribed via source and target branches

to all changes: