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