Merge lp:~elopio/address-book-app/workaround1327325-warning_to_file into lp:address-book-app
- workaround1327325-warning_to_file
- Merge into trunk
Proposed by
Leo Arias
Status: | Superseded |
---|---|
Proposed branch: | lp:~elopio/address-book-app/workaround1327325-warning_to_file |
Merge into: | lp:address-book-app |
Diff against target: |
3628 lines (+1367/-954) 43 files modified
debian/control (+1/-0) src/imports/Common/CMakeLists.txt (+1/-0) src/imports/Common/ContactDetailBase.qml (+0/-2) src/imports/Common/ContactDetailGroupBase.qml (+1/-12) src/imports/Common/ContactDetailItem.qml (+0/-15) src/imports/Common/RemoveContactsDialog.qml (+77/-0) src/imports/ContactEdit/CMakeLists.txt (+0/-1) src/imports/ContactEdit/ContactDetailGroupWithTypeEditor.qml (+0/-3) src/imports/ContactEdit/ContactDetailNameEditor.qml (+1/-0) src/imports/ContactEdit/ContactDetailSyncTargetEditor.qml (+18/-7) src/imports/ContactEdit/ContactDetailWithTypeEditor.qml (+2/-18) src/imports/ContactEdit/ContactEditor.qml (+113/-58) src/imports/ContactEdit/EditToolbar.qml (+0/-66) src/imports/ContactEdit/TextInputDetail.qml (+56/-28) src/imports/ContactList/CMakeLists.txt (+1/-0) src/imports/ContactList/ContactListPage.qml (+117/-50) src/imports/ContactList/PageWithBottomEdge.qml (+355/-0) src/imports/ContactView/ContactDetailAvatarView.qml (+9/-2) src/imports/ContactView/ContactDetailSyncTargetView.qml (+3/-0) src/imports/ContactView/ContactView.qml (+12/-16) src/imports/MainWindow.qml (+1/-5) src/imports/Ubuntu/Contacts/ContactListView.qml (+2/-2) src/imports/Ubuntu/Contacts/ContactSimpleListView.qml (+15/-5) src/imports/Ubuntu/Contacts/MultipleSelectionListView.qml (+8/-50) src/imports/Ubuntu/Contacts/OrganicView.qml (+0/-135) tests/autopilot/address_book_app/__init__.py (+0/-8) tests/autopilot/address_book_app/_errors.py (+21/-0) tests/autopilot/address_book_app/emulators/__init__.py (+0/-6) tests/autopilot/address_book_app/emulators/main_window.py (+0/-342) tests/autopilot/address_book_app/pages/__init__.py (+25/-0) tests/autopilot/address_book_app/pages/_common.py (+53/-0) tests/autopilot/address_book_app/pages/_contact_editor.py (+272/-0) tests/autopilot/address_book_app/pages/_contact_list_page.py (+70/-26) tests/autopilot/address_book_app/pages/_contact_view.py (+28/-0) tests/autopilot/address_book_app/tests/__init__.py (+11/-16) tests/autopilot/address_book_app/tests/test_add_contact.py (+38/-36) tests/autopilot/address_book_app/tests/test_contactlist.py (+1/-1) tests/autopilot/address_book_app/tests/test_create_new_from_uri.py (+10/-8) tests/autopilot/address_book_app/tests/test_custom_proxy_objects.py (+1/-3) tests/autopilot/address_book_app/tests/test_delete_contact.py (+3/-8) tests/autopilot/address_book_app/tests/test_edit_contact.py (+39/-23) tests/autopilot/address_book_app/tests/test_multiple_pick_mode.py (+1/-1) tests/autopilot/address_book_app/tests/test_single_pick_mode.py (+1/-1) |
To merge this branch: | bzr merge lp:~elopio/address-book-app/workaround1327325-warning_to_file |
Related bugs: |
Reviewer | Review Type | Date Requested | Status |
---|---|---|---|
PS Jenkins bot | continuous-integration | Approve | |
Ubuntu Phablet Team | Pending | ||
Review via email:
|
This proposal has been superseded by a proposal from 2014-06-06.
Commit message
Log warnings to a file temporary, because of bug 1327325.
Description of the change
To post a comment you must log in.
Revision history for this message
![](/+icing/build/overlay/assets/skins/sam/images/close.gif)
PS Jenkins bot (ps-jenkins) wrote : | # |
review:
Approve
(continuous-integration)
Unmerged revisions
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 2014-05-13 18:53:02 +0000 |
3 | +++ debian/control 2014-06-06 17:32:09 +0000 |
4 | @@ -73,5 +73,6 @@ |
5 | python-testscenarios, |
6 | ubuntu-ui-toolkit-autopilot, |
7 | address-book-app (>= ${binary:Version}), |
8 | + ubuntu-mobile-icons, |
9 | Description: Test package for address-book-app |
10 | Autopilot tests for the address-book-app package |
11 | |
12 | === modified file 'src/imports/Common/CMakeLists.txt' |
13 | --- src/imports/Common/CMakeLists.txt 2013-07-31 19:36:55 +0000 |
14 | +++ src/imports/Common/CMakeLists.txt 2014-06-06 17:32:09 +0000 |
15 | @@ -3,6 +3,7 @@ |
16 | ContactDetailItem.qml |
17 | ContactDetailGroupBase.qml |
18 | ContactDetailGroupWithTypeBase.qml |
19 | + RemoveContactsDialog.qml |
20 | ) |
21 | |
22 | install(FILES ${CONTACT_COMMON_QMLS} |
23 | |
24 | === modified file 'src/imports/Common/ContactDetailBase.qml' |
25 | --- src/imports/Common/ContactDetailBase.qml 2014-05-06 13:18:07 +0000 |
26 | +++ src/imports/Common/ContactDetailBase.qml 2014-06-06 17:32:09 +0000 |
27 | @@ -19,7 +19,6 @@ |
28 | import Ubuntu.Components 0.1 |
29 | import Ubuntu.Components.ListItems 0.1 as ListItem |
30 | |
31 | - |
32 | ListItem.Empty { |
33 | id: root |
34 | objectName: detail ? "base_" + detailToString(detail.type, -1) + "_" + index : "" |
35 | @@ -94,7 +93,6 @@ |
36 | } |
37 | |
38 | highlightWhenPressed: false |
39 | - focus: false |
40 | |
41 | Rectangle { |
42 | anchors.fill: parent |
43 | |
44 | === modified file 'src/imports/Common/ContactDetailGroupBase.qml' |
45 | --- src/imports/Common/ContactDetailGroupBase.qml 2014-05-06 13:18:07 +0000 |
46 | +++ src/imports/Common/ContactDetailGroupBase.qml 2014-06-06 17:32:09 +0000 |
47 | @@ -129,7 +129,6 @@ |
48 | Loader { |
49 | id: detailItem |
50 | |
51 | - focus: true |
52 | sourceComponent: root.detailDelegate |
53 | Binding { |
54 | target: detailItem.item |
55 | @@ -149,24 +148,14 @@ |
56 | newFields.push(detailItem.item) |
57 | root.newFieldAdded(detailItem.item) |
58 | root.inputFields = newFields |
59 | - if (item.focus && root.loaded && root.focus) { |
60 | + if (root.loaded) { |
61 | item.forceActiveFocus() |
62 | } |
63 | } |
64 | } |
65 | - activeFocusOnTab: true |
66 | } |
67 | } |
68 | } |
69 | |
70 | Component.onCompleted: root.loaded = true |
71 | - |
72 | - // reset focus back to first field |
73 | - onActiveFocusChanged: { |
74 | - if (!activeFocus) { |
75 | - for(var i=0; i < detailFields.count; i++) { |
76 | - detailFields.itemAt(i).focus = (i === 0) |
77 | - } |
78 | - } |
79 | - } |
80 | } |
81 | |
82 | === modified file 'src/imports/Common/ContactDetailItem.qml' |
83 | --- src/imports/Common/ContactDetailItem.qml 2014-05-06 17:19:55 +0000 |
84 | +++ src/imports/Common/ContactDetailItem.qml 2014-06-06 17:32:09 +0000 |
85 | @@ -25,7 +25,6 @@ |
86 | property Component fieldDelegate: null |
87 | |
88 | implicitHeight: fieldsColumn.height |
89 | - |
90 | Column { |
91 | id: fieldsColumn |
92 | |
93 | @@ -42,10 +41,8 @@ |
94 | model: root.fields |
95 | Loader { |
96 | id: field |
97 | - focus: true |
98 | |
99 | sourceComponent: fieldDelegate |
100 | - |
101 | Binding { |
102 | target: item |
103 | property: "field" |
104 | @@ -57,18 +54,6 @@ |
105 | property: "detail" |
106 | value: root.detail |
107 | } |
108 | - |
109 | - KeyNavigation.backtab : index > 0 ? fieldRepeater.itemAt(index - 1) : null |
110 | - KeyNavigation.tab: index < fieldRepeater.count - 1 ? fieldRepeater.itemAt(index + 1) : null |
111 | - } |
112 | - } |
113 | - } |
114 | - |
115 | - // reset focus back to first field |
116 | - onActiveFocusChanged: { |
117 | - if (!activeFocus) { |
118 | - for(var i=0; i < fieldRepeater.count; i++) { |
119 | - fieldRepeater.itemAt(i).focus = (i === 0) |
120 | } |
121 | } |
122 | } |
123 | |
124 | === added file 'src/imports/Common/RemoveContactsDialog.qml' |
125 | --- src/imports/Common/RemoveContactsDialog.qml 1970-01-01 00:00:00 +0000 |
126 | +++ src/imports/Common/RemoveContactsDialog.qml 2014-06-06 17:32:09 +0000 |
127 | @@ -0,0 +1,77 @@ |
128 | +/* |
129 | + * Copyright (C) 2014 Canonical, Ltd. |
130 | + * |
131 | + * This program is free software; you can redistribute it and/or modify |
132 | + * it under the terms of the GNU General Public License as published by |
133 | + * the Free Software Foundation; version 3. |
134 | + * |
135 | + * This program is distributed in the hope that it will be useful, |
136 | + * but WITHOUT ANY WARRANTY; without even the implied warranty of |
137 | + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
138 | + * GNU General Public License for more details. |
139 | + * |
140 | + * You should have received a copy of the GNU General Public License |
141 | + * along with this program. If not, see <http://www.gnu.org/licenses/>. |
142 | + */ |
143 | + |
144 | +import QtQuick 2.2 |
145 | +import Ubuntu.Components 0.1 |
146 | +import Ubuntu.Components.Popups 0.1 as Popups |
147 | + |
148 | +Popups.Dialog { |
149 | + objectName: "removeContactsDialog" |
150 | + |
151 | + property var contacts: [] |
152 | + |
153 | + signal canceled() |
154 | + signal accepted() |
155 | + |
156 | + function removeContacts(model) |
157 | + { |
158 | + var ids = [] |
159 | + for(var i=0, iMax=contacts.length; i < iMax; i++) { |
160 | + ids.push(contacts[i].contactId) |
161 | + } |
162 | + model.removeContacts(ids) |
163 | + } |
164 | + |
165 | + title: { |
166 | + if (contacts.length == 0) { |
167 | + return i18n.tr("No contact selected.") |
168 | + } else if (contacts.length == 1) { |
169 | + return contacts[0].displayLabel.label |
170 | + } else { |
171 | + return i18n.tr("Multiple contacts") |
172 | + } |
173 | + } |
174 | + text: { |
175 | + if (contacts.length == 1) { |
176 | + return i18n.tr("Are you sure that you want to remove this contact?") |
177 | + } else { |
178 | + return i18n.tr("Are you sure that you want to remove all selected contacts?") |
179 | + } |
180 | + } |
181 | + |
182 | + Button { |
183 | + objectName: "removeContactsDialog.Yes" |
184 | + anchors { |
185 | + left: parent.left |
186 | + right: parent.right |
187 | + margins: units.gu(1) |
188 | + } |
189 | + text: i18n.tr("Yes") |
190 | + onClicked: accepted() |
191 | + } |
192 | + |
193 | + Button { |
194 | + objectName: "removeContactsDialog.No" |
195 | + anchors { |
196 | + left: parent.left |
197 | + right: parent.right |
198 | + margins: units.gu(1) |
199 | + } |
200 | + gradient: UbuntuColors.greyGradient |
201 | + text: i18n.tr("No") |
202 | + onClicked: canceled() |
203 | + } |
204 | +} |
205 | |
206 | === modified file 'src/imports/ContactEdit/CMakeLists.txt' |
207 | --- src/imports/ContactEdit/CMakeLists.txt 2014-03-05 22:22:01 +0000 |
208 | +++ src/imports/ContactEdit/CMakeLists.txt 2014-06-06 17:32:09 +0000 |
209 | @@ -11,7 +11,6 @@ |
210 | ContactDetailWithTypeEditor.qml |
211 | ContactEditor.qml |
212 | ContactFetchError.qml |
213 | - EditToolbar.qml |
214 | KeyboardRectangle.qml |
215 | StandardAnimation.qml |
216 | TextInputDetail.qml |
217 | |
218 | === modified file 'src/imports/ContactEdit/ContactDetailGroupWithTypeEditor.qml' |
219 | --- src/imports/ContactEdit/ContactDetailGroupWithTypeEditor.qml 2014-05-13 18:53:02 +0000 |
220 | +++ src/imports/ContactEdit/ContactDetailGroupWithTypeEditor.qml 2014-06-06 17:32:09 +0000 |
221 | @@ -95,8 +95,6 @@ |
222 | |
223 | return changed |
224 | } |
225 | - |
226 | - activeFocusOnTab: detailsCount > 0 |
227 | minimumHeight: units.gu(5) |
228 | headerDelegate: ListItem.Empty { |
229 | id: header |
230 | @@ -130,7 +128,6 @@ |
231 | } |
232 | width: units.gu(2) |
233 | height: units.gu(2) |
234 | - color: "white" |
235 | name: "add" |
236 | } |
237 | |
238 | |
239 | === modified file 'src/imports/ContactEdit/ContactDetailNameEditor.qml' |
240 | --- src/imports/ContactEdit/ContactDetailNameEditor.qml 2014-05-08 15:45:48 +0000 |
241 | +++ src/imports/ContactEdit/ContactDetailNameEditor.qml 2014-06-06 17:32:09 +0000 |
242 | @@ -74,6 +74,7 @@ |
243 | root.emptyFields = newEmtpyFields |
244 | } |
245 | |
246 | + focus: true |
247 | width: root.width - units.gu(4) |
248 | x: units.gu(2) |
249 | detail: root.detail |
250 | |
251 | === modified file 'src/imports/ContactEdit/ContactDetailSyncTargetEditor.qml' |
252 | --- src/imports/ContactEdit/ContactDetailSyncTargetEditor.qml 2014-05-20 14:56:42 +0000 |
253 | +++ src/imports/ContactEdit/ContactDetailSyncTargetEditor.qml 2014-06-06 17:32:09 +0000 |
254 | @@ -1,4 +1,4 @@ |
255 | -/* |
256 | + /* |
257 | * Copyright (C) 2012-2013 Canonical, Ltd. |
258 | * |
259 | * This program is free software; you can redistribute it and/or modify |
260 | @@ -24,6 +24,8 @@ |
261 | ContactDetailBase { |
262 | id: root |
263 | |
264 | + property bool active: false |
265 | + |
266 | function save() { |
267 | // only changes the target sync for new contacts |
268 | if (!isNewContact) { |
269 | @@ -41,7 +43,7 @@ |
270 | } |
271 | |
272 | function getSelectedSource() { |
273 | - var selectedContact = sourceModel.contacts[sources.selectedIndex] |
274 | + var selectedContact = sources.model.contacts[sources.selectedIndex] |
275 | if (selectedContact) { |
276 | return selectedContact.guid.guid |
277 | } else { |
278 | @@ -53,7 +55,8 @@ |
279 | property real myHeight: sources.containerHeight + units.gu(4) + label.height |
280 | |
281 | detail: contact ? contact.detail(ContactDetail.SyncTarget) : null |
282 | - implicitHeight: isNewContact && (sourceModel.contacts.length > 1) ? myHeight : 0 |
283 | + implicitHeight: isNewContact && sources.model && (sources.model.contacts.length > 1) ? myHeight : 0 |
284 | + |
285 | |
286 | ContactModel { |
287 | id: sourceModel |
288 | @@ -65,6 +68,7 @@ |
289 | value: Type.Group |
290 | matchFlags: DetailFilter.MatchExactly |
291 | } |
292 | + autoUpdate: false |
293 | } |
294 | |
295 | Label { |
296 | @@ -77,12 +81,13 @@ |
297 | right: parent.right |
298 | margins: units.gu(2) |
299 | } |
300 | - height: root.active ? units.gu(4) : units.gu(3) |
301 | + height: units.gu(4) |
302 | } |
303 | |
304 | OptionSelector { |
305 | id: sources |
306 | |
307 | + model: sourceModel |
308 | anchors { |
309 | left: parent.left |
310 | leftMargin: units.gu(2) |
311 | @@ -94,13 +99,19 @@ |
312 | bottomMargin: units.gu(2) |
313 | } |
314 | |
315 | - model: sourceModel |
316 | delegate: OptionSelectorDelegate { |
317 | text: contact.displayLabel.label |
318 | height: units.gu(5) |
319 | } |
320 | |
321 | - containerHeight: sourceModel.contacts.length > 4 ? itemHeight * 4 : itemHeight * sourceModel.contacts.length |
322 | - } |
323 | + containerHeight: sources.model && sources.model.contacts.length > 4 ? itemHeight * 4 : sources.model ? itemHeight * sources.model.contacts.length : 0 |
324 | + } |
325 | + |
326 | + onActiveChanged: { |
327 | + if (active) { |
328 | + sourceModel.update() |
329 | + } |
330 | + } |
331 | + |
332 | } |
333 | |
334 | |
335 | === modified file 'src/imports/ContactEdit/ContactDetailWithTypeEditor.qml' |
336 | --- src/imports/ContactEdit/ContactDetailWithTypeEditor.qml 2014-05-13 18:53:02 +0000 |
337 | +++ src/imports/ContactEdit/ContactDetailWithTypeEditor.qml 2014-06-06 17:32:09 +0000 |
338 | @@ -68,7 +68,6 @@ |
339 | return detailchanged |
340 | } |
341 | |
342 | - focus: true |
343 | // disable listview mouse area |
344 | __mouseArea.visible: false |
345 | enabled: root.detail ? !root.detail.readOnly : false |
346 | @@ -107,22 +106,19 @@ |
347 | right: detailTypeSelector.right |
348 | top: detailTypeSelector.bottom |
349 | } |
350 | - focus: true |
351 | height: childrenRect.height |
352 | |
353 | Repeater { |
354 | id: fieldRepeater |
355 | + |
356 | model: root.fields |
357 | - |
358 | - focus: true |
359 | TextInputDetail { |
360 | id: detail |
361 | objectName: root.detail ? detailToString(root.detail.type, modelData) + "_" + root.index : "" |
362 | |
363 | - Component.onCompleted: focus = (index === 0) |
364 | - focus: false |
365 | detail: root.detail |
366 | field: modelData |
367 | + focus: true |
368 | placeholderText: root.placeholderTexts[index] |
369 | inputMethodHints: root.inputMethodHints |
370 | autoFormat: root.usePhoneFormat |
371 | @@ -133,9 +129,6 @@ |
372 | } |
373 | height: root.active ? root.itemHeight + units.gu(1) : root.itemHeight |
374 | onRemoveClicked: root.contact.removeDetail(root.detail) |
375 | - // FIXME: SDK still using QtQuick 2.0 change this to activeFocusOnTab: true on version 2.2 |
376 | - KeyNavigation.backtab : index > 0 ? fieldRepeater.itemAt(index - 1) : null |
377 | - KeyNavigation.tab: index < fieldRepeater.count - 1 ? fieldRepeater.itemAt(index + 1) : null |
378 | } |
379 | } |
380 | Keys.onReleased: { |
381 | @@ -148,13 +141,4 @@ |
382 | } |
383 | } |
384 | } |
385 | - |
386 | - // reset focus back to first field |
387 | - onActiveFocusChanged: { |
388 | - if (!activeFocus) { |
389 | - for(var i=0; i < fieldRepeater.count; i++) { |
390 | - fieldRepeater.itemAt(i).focus = (i === 0) |
391 | - } |
392 | - } |
393 | - } |
394 | } |
395 | |
396 | === modified file 'src/imports/ContactEdit/ContactEditor.qml' |
397 | --- src/imports/ContactEdit/ContactEditor.qml 2014-05-08 15:45:48 +0000 |
398 | +++ src/imports/ContactEdit/ContactEditor.qml 2014-06-06 17:32:09 +0000 |
399 | @@ -21,25 +21,23 @@ |
400 | import Ubuntu.Components.Popups 0.1 |
401 | import Ubuntu.Contacts 0.1 as ContactsUI |
402 | |
403 | +import "../Common" |
404 | + |
405 | Page { |
406 | id: contactEditor |
407 | objectName: "contactEditorPage" |
408 | |
409 | property QtObject contact: null |
410 | property alias model: contactFetch.model |
411 | + property QtObject activeItem: null |
412 | readonly property bool isNewContact: contact && (contact.contactId === "qtcontacts:::") |
413 | |
414 | // this is used to add a phone number to a existing contact |
415 | property string contactId: "" |
416 | property string newPhoneNumber: "" |
417 | |
418 | - property QtObject activeItem: null |
419 | - |
420 | - // we use a custom toolbar in this view |
421 | - tools: ToolbarItems { |
422 | - locked: true |
423 | - opened: false |
424 | - } |
425 | + // priv |
426 | + property bool _edgeReady: false |
427 | |
428 | function cancel() { |
429 | for(var i = 0; i < contents.children.length; ++i) { |
430 | @@ -90,22 +88,22 @@ |
431 | } |
432 | |
433 | function makeMeVisible(item) { |
434 | - if (!item) { |
435 | + if (!_edgeReady || !item) { |
436 | return |
437 | } |
438 | |
439 | activeItem = item |
440 | - var position = scrollArea.contentItem.mapFromItem(item, 0, item.y); |
441 | + var position = scrollArea.contentItem.mapFromItem(item, 0, activeItem.y); |
442 | |
443 | // check if the item is already visible |
444 | var bottomY = scrollArea.contentY + scrollArea.height |
445 | - var itemBottom = position.y + item.height |
446 | + var itemBottom = position.y + (item.height * 3) // extra margin |
447 | if (position.y >= scrollArea.contentY && itemBottom <= bottomY) { |
448 | return; |
449 | } |
450 | |
451 | // if it is not, try to scroll and make it visible |
452 | - var targetY = position.y + item.height - scrollArea.height |
453 | + var targetY = itemBottom - scrollArea.height |
454 | if (targetY >= 0 && position.y) { |
455 | scrollArea.contentY = targetY; |
456 | } else if (position.y < scrollArea.contentY) { |
457 | @@ -115,6 +113,17 @@ |
458 | scrollArea.returnToBounds() |
459 | } |
460 | |
461 | + function ready() |
462 | + { |
463 | + enabled = true |
464 | + if (isNewContact) { |
465 | + _edgeReady = true |
466 | + nameEditor.fieldDelegates[0].forceActiveFocus() |
467 | + } |
468 | + } |
469 | + |
470 | + title: i18n.tr("Edit") |
471 | + |
472 | ContactFetchError { |
473 | id: fetchErrorDialog |
474 | } |
475 | @@ -135,6 +144,7 @@ |
476 | |
477 | interval: 200 |
478 | running: false |
479 | + repeat: false |
480 | onTriggered: { |
481 | // get last phone field and set focus |
482 | var lastPhoneField = phonesEditor.detailDelegates[phonesEditor.detailDelegates.length - 2].item |
483 | @@ -142,23 +152,19 @@ |
484 | } |
485 | } |
486 | |
487 | - flickable: null |
488 | Flickable { |
489 | id: scrollArea |
490 | objectName: "scrollArea" |
491 | |
492 | flickableDirection: Flickable.VerticalFlick |
493 | anchors { |
494 | - left: parent.left |
495 | - right: parent.right |
496 | - top: parent.top |
497 | - bottom: toolbar.top |
498 | - bottomMargin: units.gu(2) |
499 | + fill: parent |
500 | + bottomMargin: keyboard.height |
501 | } |
502 | contentHeight: contents.height |
503 | contentWidth: parent.width |
504 | |
505 | - // after add a new field we need to wait for the contentHeight to change to scroll to the correct position |
506 | + //after add a new field we need to wait for the contentHeight to change to scroll to the correct position |
507 | onContentHeightChanged: contactEditor.makeMeVisible(contactEditor.activeItem) |
508 | |
509 | Column { |
510 | @@ -166,37 +172,21 @@ |
511 | |
512 | anchors { |
513 | top: parent.top |
514 | - topMargin: units.gu(2) |
515 | left: parent.left |
516 | right: parent.right |
517 | } |
518 | height: childrenRect.height |
519 | |
520 | - // WORKAROUND: SDK does not support QtQuick 2.2 properties yet, because of that we need create |
521 | - // a external element and that allow us to use activeFocusOnTab |
522 | - // FIXME: Remove FocusScope element as soon as the SDK get support for QtQuick 2.2 |
523 | - FocusScope { |
524 | - function save() { |
525 | - return nameEditor.save() |
526 | - } |
527 | - |
528 | - function isEmpty() { |
529 | - return nameEditor.cancel() |
530 | - } |
531 | - |
532 | - activeFocusOnTab: true |
533 | + ContactDetailNameEditor { |
534 | + id: nameEditor |
535 | + |
536 | + |
537 | anchors { |
538 | left: parent.left |
539 | right: parent.right |
540 | } |
541 | height: nameEditor.implicitHeight + units.gu(3) |
542 | - |
543 | - ContactDetailNameEditor { |
544 | - id: nameEditor |
545 | - |
546 | - contact: contactEditor.contact |
547 | - anchors.fill: parent |
548 | - } |
549 | + contact: contactEditor.contact |
550 | } |
551 | |
552 | ContactDetailAvatarEditor { |
553 | @@ -273,6 +263,7 @@ |
554 | ContactDetailSyncTargetEditor { |
555 | id: syncTargetEditor |
556 | |
557 | + active: contactEditor.active |
558 | contact: contactEditor.contact |
559 | anchors { |
560 | left: parent.left |
561 | @@ -280,25 +271,33 @@ |
562 | } |
563 | height: implicitHeight |
564 | } |
565 | - } |
566 | - } |
567 | - |
568 | - EditToolbar { |
569 | - id: toolbar |
570 | - anchors { |
571 | - left: parent.left |
572 | - right: parent.right |
573 | - bottom: keyboard.top |
574 | - } |
575 | - height: units.gu(6) |
576 | - acceptAction: Action { |
577 | - text: i18n.tr("Save") |
578 | - enabled: !nameEditor.isEmpty() || !phonesEditor.isEmpty() |
579 | - onTriggered: contactEditor.save() |
580 | - } |
581 | - rejectAction: Action { |
582 | - text: i18n.tr("Cancel") |
583 | - onTriggered: contactEditor.cancel() |
584 | + |
585 | + // We need this extra element to correct align the deleteButton |
586 | + Item { |
587 | + anchors { |
588 | + left: parent.left |
589 | + right: parent.right |
590 | + } |
591 | + height: deleteButton.height + units.gu(2) |
592 | + |
593 | + Button { |
594 | + id: deleteButton |
595 | + |
596 | + text: i18n.tr("Delete") |
597 | + visible: !contactEditor.isNewContact |
598 | + anchors { |
599 | + margins: units.gu(2) |
600 | + top: parent.top |
601 | + left: parent.left |
602 | + right: parent.right |
603 | + } |
604 | + |
605 | + onClicked: { |
606 | + var dialog = PopupUtils.open(removeContactDialog, null) |
607 | + dialog.contacts = [contactEditor.contact] |
608 | + } |
609 | + } |
610 | + } |
611 | } |
612 | } |
613 | |
614 | @@ -312,6 +311,34 @@ |
615 | } |
616 | } |
617 | |
618 | + tools: ToolbarItems { |
619 | + id: toolbar |
620 | + |
621 | + back: ToolbarButton { |
622 | + action: Action { |
623 | + objectName: "cancel" |
624 | + |
625 | + iconName: "close" |
626 | + text: i18n.tr("Cancel") |
627 | + onTriggered: { |
628 | + contactEditor.cancel() |
629 | + contactEditor.active = false |
630 | + } |
631 | + } |
632 | + } |
633 | + |
634 | + ToolbarButton { |
635 | + action: Action { |
636 | + objectName: "save" |
637 | + |
638 | + iconName: "save" |
639 | + text: i18n.tr("Save") |
640 | + enabled: !nameEditor.isEmpty() || !phonesEditor.isEmpty() |
641 | + onTriggered: contactEditor.save() |
642 | + } |
643 | + } |
644 | + } |
645 | + |
646 | // This will load the contact information and add the new phone number |
647 | // when the app was launched with the URI: addressbook:///addphone?id=<contact-id>&phone=<phone-number> |
648 | onContactChanged: { |
649 | @@ -334,4 +361,32 @@ |
650 | nameEditor.forceActiveFocus() |
651 | } |
652 | } |
653 | + |
654 | + Component { |
655 | + id: removeContactDialog |
656 | + |
657 | + RemoveContactsDialog { |
658 | + id: removeContactsDialogMessage |
659 | + |
660 | + property var popPages: false |
661 | + |
662 | + onCanceled: { |
663 | + PopupUtils.close(removeContactsDialogMessage) |
664 | + } |
665 | + |
666 | + onAccepted: { |
667 | + popPages = true |
668 | + removeContacts(contactEditor.model) |
669 | + PopupUtils.close(removeContactsDialogMessage) |
670 | + } |
671 | + |
672 | + // WORKAROUND: SDK element crash if pop the page where the dialog was created |
673 | + Component.onDestruction: { |
674 | + if (popPages) { |
675 | + contactEditor.pageStack.pop() // editor page |
676 | + contactEditor.pageStack.pop() // view page |
677 | + } |
678 | + } |
679 | + } |
680 | + } |
681 | } |
682 | |
683 | === removed file 'src/imports/ContactEdit/EditToolbar.qml' |
684 | --- src/imports/ContactEdit/EditToolbar.qml 2014-05-08 15:46:09 +0000 |
685 | +++ src/imports/ContactEdit/EditToolbar.qml 1970-01-01 00:00:00 +0000 |
686 | @@ -1,66 +0,0 @@ |
687 | -/* |
688 | - * Copyright (C) 2012-2013 Canonical, Ltd. |
689 | - * |
690 | - * This program is free software; you can redistribute it and/or modify |
691 | - * it under the terms of the GNU General Public License as published by |
692 | - * the Free Software Foundation; version 3. |
693 | - * |
694 | - * This program is distributed in the hope that it will be useful, |
695 | - * but WITHOUT ANY WARRANTY; without even the implied warranty of |
696 | - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
697 | - * GNU General Public License for more details. |
698 | - * |
699 | - * You should have received a copy of the GNU General Public License |
700 | - * along with this program. If not, see <http://www.gnu.org/licenses/>. |
701 | - */ |
702 | - |
703 | -import QtQuick 2.2 |
704 | -import Ubuntu.Components 0.1 |
705 | -import Ubuntu.Components.ListItems 0.1 as ListItem |
706 | - |
707 | -Rectangle { |
708 | - id: root |
709 | - |
710 | - signal reject() |
711 | - signal accept() |
712 | - |
713 | - property alias acceptAction: accept.action |
714 | - property alias rejectAction: reject.action |
715 | - |
716 | - color: "gray" |
717 | - |
718 | - // WORKAROUND: avoid the mouse click get stolen by the Flickable area |
719 | - MouseArea { |
720 | - anchors.fill: parent |
721 | - } |
722 | - |
723 | - Button { |
724 | - id: reject |
725 | - objectName: "reject" |
726 | - |
727 | - action: Action { |
728 | - text: i18n.tr("Cancel") |
729 | - } |
730 | - anchors { |
731 | - left: parent.left |
732 | - leftMargin: units.gu(1) |
733 | - verticalCenter: parent.verticalCenter |
734 | - } |
735 | - onClicked: root.reject() |
736 | - } |
737 | - |
738 | - Button { |
739 | - id: accept |
740 | - objectName: "accept" |
741 | - |
742 | - action: Action { |
743 | - text: i18n.tr("Done") |
744 | - } |
745 | - anchors { |
746 | - right: parent.right |
747 | - rightMargin: units.gu(1) |
748 | - verticalCenter: parent.verticalCenter |
749 | - } |
750 | - onClicked: root.accept() |
751 | - } |
752 | -} |
753 | |
754 | === modified file 'src/imports/ContactEdit/TextInputDetail.qml' |
755 | --- src/imports/ContactEdit/TextInputDetail.qml 2014-05-16 20:30:35 +0000 |
756 | +++ src/imports/ContactEdit/TextInputDetail.qml 2014-06-06 17:32:09 +0000 |
757 | @@ -22,48 +22,76 @@ |
758 | //style |
759 | import Ubuntu.Components.Themes.Ambiance 0.1 |
760 | |
761 | -PhoneNumberField { |
762 | +FocusScope { |
763 | id: root |
764 | |
765 | property QtObject detail |
766 | property int field: -1 |
767 | property variant originalValue: root.detail && (root.field >= 0) ? root.detail.value(root.field) : null |
768 | |
769 | + // proxy textField |
770 | + property alias font: field.font |
771 | + property alias placeholderText: field.placeholderText |
772 | + property alias inputMethodHints: field.inputMethodHints |
773 | + property alias text: field.text |
774 | + property alias hasClearButton: field.hasClearButton |
775 | + // proxy PhoneNumberField |
776 | + property alias autoFormat: field.autoFormat |
777 | + |
778 | signal removeClicked() |
779 | |
780 | - // TRANSLATORS: This value is used as default value for phone number format, when no coutry code is provided |
781 | - // the supported values can be found in: https://www.iso.org/obp/ui/#search |
782 | - defaultRegion: i18n.tr("US") |
783 | - autoFormat: false |
784 | - |
785 | - // Ubuntu.Keyboard |
786 | - InputMethod.extensions: { "enterKeyText": i18n.tr("Next") } |
787 | - |
788 | - readOnly: detail ? detail.readOnly : true |
789 | - focus: true |
790 | - style: TextFieldStyle { |
791 | - overlaySpacing: 0 |
792 | - frameSpacing: 0 |
793 | - background: Item {} |
794 | + //FIXME: Move this property to TextField as soon as the SDK get ported to QtQuick 2.2 |
795 | + activeFocusOnTab: true |
796 | + |
797 | + // WORKAROUND: For some reason TextField.focus property get reset to false |
798 | + // we need do a deep investigation on that |
799 | + Binding { |
800 | + target: field |
801 | + property: "focus" |
802 | + value: visible |
803 | } |
804 | |
805 | - Component.onCompleted: makeMeVisible(root) |
806 | - onActiveFocusChanged: { |
807 | - if (activeFocus) { |
808 | - makeMeVisible(root) |
809 | + onActiveFocusChanged: { |
810 | + if (activeFocus && field.visible) { |
811 | + field.forceActiveFocus() |
812 | } |
813 | } |
814 | |
815 | - // default style |
816 | - font { |
817 | - family: "Ubuntu" |
818 | - pixelSize: activeFocus ? FontUtils.sizeToPixels("large") : FontUtils.sizeToPixels("medium") |
819 | - } |
820 | - |
821 | - Keys.onReturnPressed: application.sendTabEvent(); |
822 | onOriginalValueChanged: { |
823 | if (originalValue && (originalValue !== "")) { |
824 | - text = originalValue |
825 | - } |
826 | + field.text = originalValue |
827 | + } |
828 | + } |
829 | + |
830 | + PhoneNumberField { |
831 | + id: field |
832 | + |
833 | + anchors.fill: parent |
834 | + |
835 | + // TRANSLATORS: This value is used as default value for phone number format, when no coutry code is provided |
836 | + // the supported values can be found in: https://www.iso.org/obp/ui/#search |
837 | + defaultRegion: i18n.tr("US") |
838 | + autoFormat: false |
839 | + |
840 | + // Ubuntu.Keyboard |
841 | + InputMethod.extensions: { "enterKeyText": i18n.tr("Next") } |
842 | + readOnly: root.detail ? root.detail.readOnly : true |
843 | + style: TextFieldStyle { |
844 | + overlaySpacing: 0 |
845 | + frameSpacing: 0 |
846 | + background: Item {} |
847 | + } |
848 | + onActiveFocusChanged: { |
849 | + if (activeFocus) { |
850 | + makeMeVisible(root) |
851 | + } |
852 | + } |
853 | + |
854 | + // default style |
855 | + font { |
856 | + family: "Ubuntu" |
857 | + pixelSize: activeFocus ? FontUtils.sizeToPixels("large") : FontUtils.sizeToPixels("medium") |
858 | + } |
859 | + Keys.onReturnPressed: application.sendTabEvent(); |
860 | } |
861 | } |
862 | |
863 | === modified file 'src/imports/ContactList/CMakeLists.txt' |
864 | --- src/imports/ContactList/CMakeLists.txt 2014-02-18 14:09:02 +0000 |
865 | +++ src/imports/ContactList/CMakeLists.txt 2014-06-06 17:32:09 +0000 |
866 | @@ -2,6 +2,7 @@ |
867 | ContactListPage.qml |
868 | ContactExporter.qml |
869 | OnlineAccountsMessage.qml |
870 | + PageWithBottomEdge.qml |
871 | ) |
872 | |
873 | install(FILES ${CONTACT_LIST_QMLS} |
874 | |
875 | === modified file 'src/imports/ContactList/ContactListPage.qml' |
876 | --- src/imports/ContactList/ContactListPage.qml 2014-05-06 13:18:07 +0000 |
877 | +++ src/imports/ContactList/ContactListPage.qml 2014-06-06 17:32:09 +0000 |
878 | @@ -21,8 +21,10 @@ |
879 | import Ubuntu.Components.ListItems 0.1 as ListItem |
880 | import Ubuntu.Contacts 0.1 as ContactsUI |
881 | import Ubuntu.Components.Popups 0.1 as Popups |
882 | +import "../ContactEdit" |
883 | +import "../Common" |
884 | |
885 | -Page { |
886 | +PageWithBottomEdge { |
887 | id: mainPage |
888 | objectName: "contactListPage" |
889 | |
890 | @@ -54,7 +56,20 @@ |
891 | return newContact |
892 | } |
893 | |
894 | - title: i18n.tr("Contacts") |
895 | + title: contactList.isInSelectionMode ? i18n.tr("Select Contacts") : i18n.tr("Contacts") |
896 | + |
897 | + //bottom edge page |
898 | + bottomEdgePageComponent: ContactEditor { |
899 | + //WORKAROUND: SKD changes the page header as soon as the page get created |
900 | + // setting active false will avoid that |
901 | + active: false |
902 | + enabled: false |
903 | + |
904 | + model: contactList.listModel |
905 | + contact: mainPage.createEmptyContact("") |
906 | + } |
907 | + bottomEdgeTitle: "+" |
908 | + bottomEdgeEnabled: !contactList.isInSelectionMode |
909 | |
910 | Component { |
911 | id: onlineAccountsDialog |
912 | @@ -75,21 +90,31 @@ |
913 | } |
914 | } |
915 | |
916 | + Component { |
917 | + id: removeContactDialog |
918 | + |
919 | + RemoveContactsDialog { |
920 | + id: removeContactsDialogMessage |
921 | + |
922 | + onCanceled: { |
923 | + PopupUtils.close(removeContactsDialogMessage) |
924 | + } |
925 | + |
926 | + onAccepted: { |
927 | + removeContacts(contactList.listModel) |
928 | + PopupUtils.close(removeContactsDialogMessage) |
929 | + } |
930 | + } |
931 | + } |
932 | + |
933 | ContactsUI.ContactListView { |
934 | id: contactList |
935 | objectName: "contactListView" |
936 | |
937 | multiSelectionEnabled: true |
938 | - acceptAction.text: pickMode ? i18n.tr("Select") : i18n.tr("Delete") |
939 | multipleSelection: !pickMode || |
940 | ((contactContentHub && contactContentHub.multipleItems) || mainPage.pickMultipleContacts) |
941 | - anchors { |
942 | - // This extra margin is necessary because the toolbar area overlaps the last item in the view |
943 | - // in the selection mode we remove it to avoid visual problems due the selection bar appears |
944 | - // inside of the listview |
945 | - bottomMargin: contactList.isInSelectionMode ? 0 : units.gu(2) |
946 | - fill: parent |
947 | - } |
948 | + anchors.fill: parent |
949 | swipeToDelete: !pickMode |
950 | |
951 | onCountChanged: { |
952 | @@ -118,11 +143,14 @@ |
953 | exporter.contacts = contacts |
954 | exporter.start() |
955 | } else { |
956 | - var ids = [] |
957 | - for (var i=0; i < items.count; i++) { |
958 | - ids.push(items.get(i).model.contact.contactId) |
959 | + var contacts = [] |
960 | + |
961 | + for (var i=0, iMax=items.count; i < iMax; i++) { |
962 | + contacts.push(items.get(i).model.contact) |
963 | } |
964 | - contactList.listModel.removeContacts(ids) |
965 | + |
966 | + var dialog = PopupUtils.open(removeContactDialog, null) |
967 | + dialog.contacts = contacts |
968 | } |
969 | } |
970 | |
971 | @@ -136,15 +164,8 @@ |
972 | } |
973 | } |
974 | |
975 | - onIsInSelectionModeChanged: { |
976 | - if (isInSelectionMode) { |
977 | - toolbar.opened = false |
978 | - } |
979 | - } |
980 | - |
981 | onError: pageStack.contactModelError(error) |
982 | |
983 | - |
984 | Column { |
985 | id: indicator |
986 | |
987 | @@ -165,42 +186,64 @@ |
988 | } |
989 | } |
990 | |
991 | - tools: ToolbarItems { |
992 | - id: toolbar |
993 | - |
994 | - locked: contactList.isInSelectionMode |
995 | + ToolbarItems { |
996 | + id: toolbarItemsSelectionMode |
997 | + |
998 | + visible: false |
999 | + back: ToolbarButton { |
1000 | + action: Action { |
1001 | + text: i18n.tr("Cancel selection") |
1002 | + iconName: "close" |
1003 | + onTriggered: contactList.cancelSelection() |
1004 | + } |
1005 | + } |
1006 | + |
1007 | + ToolbarButton { |
1008 | + action: Action { |
1009 | + objectName: "selectAll" |
1010 | + text: i18n.tr("Select All") |
1011 | + iconName: "filter" |
1012 | + onTriggered: { |
1013 | + if (contactList.selectedItems.count == contactList.count) { |
1014 | + contactList.clearSelection() |
1015 | + } else { |
1016 | + contactList.selectAll() |
1017 | + } |
1018 | + } |
1019 | + visible: contactList.isInSelectionMode |
1020 | + } |
1021 | + } |
1022 | + |
1023 | + ToolbarButton { |
1024 | + |
1025 | + action: Action { |
1026 | + objectName: "doneSelection" |
1027 | + text: mainPage.pickMode ? i18n.tr("Select") : i18n.tr("Delete") |
1028 | + iconName: mainPage.pickMode ? "select" : "delete" |
1029 | + onTriggered: contactList.endSelection() |
1030 | + visible: contactList.isInSelectionMode |
1031 | + } |
1032 | + } |
1033 | + } |
1034 | + |
1035 | + ToolbarItems { |
1036 | + id: toolbarItemsNormalMode |
1037 | + |
1038 | + visible: false |
1039 | ToolbarButton { |
1040 | objectName: "Sync" |
1041 | - visible: mainPage.syncEnabled |
1042 | action: Action { |
1043 | + visible: mainPage.syncEnabled |
1044 | text: application.syncing ? i18n.tr("Syncing") : i18n.tr("Sync") |
1045 | iconName: "reload" |
1046 | enabled: !application.syncing |
1047 | onTriggered: application.startSync() |
1048 | } |
1049 | } |
1050 | - ToolbarButton { |
1051 | - action: Action { |
1052 | - objectName: "selectButton" |
1053 | - text: i18n.tr("Select") |
1054 | - iconName: "select" |
1055 | - onTriggered: contactList.startSelection() |
1056 | - } |
1057 | - } |
1058 | - ToolbarButton { |
1059 | - objectName: "Add" |
1060 | - action: Action { |
1061 | - text: i18n.tr("Add") |
1062 | - iconName: "add" |
1063 | - onTriggered: { |
1064 | - var newContact = mainPage.createEmptyContact("") |
1065 | - pageStack.push(Qt.resolvedUrl("../ContactEdit/ContactEditor.qml"), |
1066 | - {model: contactList.listModel, contact: newContact}) |
1067 | - } |
1068 | - } |
1069 | - } |
1070 | } |
1071 | |
1072 | + tools: contactList.isInSelectionMode ? toolbarItemsSelectionMode : toolbarItemsNormalMode |
1073 | + |
1074 | // WORKAROUND: Avoid the gap btw the header and the contact list when the list moves |
1075 | // see bug #1296764 |
1076 | onActiveChanged: { |
1077 | @@ -217,6 +260,19 @@ |
1078 | } |
1079 | } |
1080 | |
1081 | + // We need to reset the page proprerties in case of the page was created pre-populated, |
1082 | + // with phonenumber or contact. |
1083 | + onBottomEdgeDismissed: { |
1084 | + //WORKAROUND: SKD changes the page header as soon as the page get created |
1085 | + // setting active false will avoid that |
1086 | + var newContact = mainPage.createEmptyContact("") |
1087 | + mainPage.setBottomEdgePage(Qt.resolvedUrl("../ContactEdit/ContactEditor.qml"), |
1088 | + {model: contactList.listModel, |
1089 | + contact: newContact, |
1090 | + active: false, |
1091 | + enabled: false}) |
1092 | + } |
1093 | + |
1094 | Connections { |
1095 | target: pageStack |
1096 | onContactRequested: { |
1097 | @@ -225,12 +281,23 @@ |
1098 | } |
1099 | onCreateContactRequested: { |
1100 | var newContact = mainPage.createEmptyContact(phoneNumber) |
1101 | - pageStack.push(Qt.resolvedUrl("../ContactEdit/ContactEditor.qml"), |
1102 | - {model: contactList.listModel, contact: newContact}) |
1103 | + //WORKAROUND: SKD changes the page header as soon as the page get created |
1104 | + // setting active false will avoid that |
1105 | + mainPage.showBottomEdgePage(Qt.resolvedUrl("../ContactEdit/ContactEditor.qml"), |
1106 | + {model: contactList.listModel, |
1107 | + contact: newContact, |
1108 | + active: false, |
1109 | + enabled: false}) |
1110 | } |
1111 | onEditContatRequested: { |
1112 | - pageStack.push(Qt.resolvedUrl("../ContactEdit/ContactEditor.qml"), |
1113 | - {model: contactList.listModel, contactId: contactId, newPhoneNumber: phoneNumber }) |
1114 | + //WORKAROUND: SKD changes the page header as soon as the page get created |
1115 | + // setting active false will avoid that |
1116 | + mainPage.showBottomEdgePage(Qt.resolvedUrl("../ContactEdit/ContactEditor.qml"), |
1117 | + {model: contactList.listModel, |
1118 | + contactId: contactId, |
1119 | + newPhoneNumber: phoneNumber, |
1120 | + active: false, |
1121 | + enabled: false}) |
1122 | } |
1123 | onContactCreated: { |
1124 | mainPage.contactIndex = contact |
1125 | |
1126 | === added file 'src/imports/ContactList/PageWithBottomEdge.qml' |
1127 | --- src/imports/ContactList/PageWithBottomEdge.qml 1970-01-01 00:00:00 +0000 |
1128 | +++ src/imports/ContactList/PageWithBottomEdge.qml 2014-06-06 17:32:09 +0000 |
1129 | @@ -0,0 +1,355 @@ |
1130 | +/* |
1131 | + * Copyright (C) 2014 Canonical, Ltd. |
1132 | + * |
1133 | + * This program is free software; you can redistribute it and/or modify |
1134 | + * it under the terms of the GNU General Public License as published by |
1135 | + * the Free Software Foundation; version 3. |
1136 | + * |
1137 | + * This program is distributed in the hope that it will be useful, |
1138 | + * but WITHOUT ANY WARRANTY; without even the implied warranty of |
1139 | + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
1140 | + * GNU General Public License for more details. |
1141 | + * |
1142 | + * You should have received a copy of the GNU General Public License |
1143 | + * along with this program. If not, see <http://www.gnu.org/licenses/>. |
1144 | + */ |
1145 | + |
1146 | +/* |
1147 | + Example: |
1148 | + |
1149 | + MainView { |
1150 | + objectName: "mainView" |
1151 | + |
1152 | + applicationName: "com.ubuntu.developer.boiko.bottomedge" |
1153 | + |
1154 | + width: units.gu(100) |
1155 | + height: units.gu(75) |
1156 | + |
1157 | + Component { |
1158 | + id: pageComponent |
1159 | + |
1160 | + PageWithBottomEdge { |
1161 | + id: mainPage |
1162 | + title: i18n.tr("Main Page") |
1163 | + |
1164 | + Rectangle { |
1165 | + anchors.fill: parent |
1166 | + color: "white" |
1167 | + } |
1168 | + |
1169 | + bottomEdgePageComponent: Page { |
1170 | + title: "Contents" |
1171 | + anchors.fill: parent |
1172 | + //anchors.topMargin: contentsPage.flickable.contentY |
1173 | + |
1174 | + ListView { |
1175 | + anchors.fill: parent |
1176 | + model: 50 |
1177 | + delegate: ListItems.Standard { |
1178 | + text: "One Content Item: " + index |
1179 | + } |
1180 | + } |
1181 | + } |
1182 | + bottomEdgeTitle: i18n.tr("Bottom edge action") |
1183 | + } |
1184 | + } |
1185 | + |
1186 | + PageStack { |
1187 | + id: stack |
1188 | + Component.onCompleted: stack.push(pageComponent) |
1189 | + } |
1190 | + } |
1191 | + |
1192 | +*/ |
1193 | + |
1194 | +import QtQuick 2.0 |
1195 | +import Ubuntu.Components 0.1 |
1196 | + |
1197 | +Page { |
1198 | + id: page |
1199 | + |
1200 | + property alias bottomEdgePageComponent: edgeLoader.sourceComponent |
1201 | + property alias bottomEdgePageSource: edgeLoader.source |
1202 | + property alias bottomEdgeTitle: tipLabel.text |
1203 | + property alias bottomEdgeEnabled: bottomEdge.visible |
1204 | + property int bottomEdgeExpandThreshold: page.height * 0.2 |
1205 | + property int bottomEdgeExposedArea: bottomEdge.state !== "expanded" ? (page.height - bottomEdge.y - bottomEdge.tipHeight) : _areaWhenExpanded |
1206 | + property bool reloadBottomEdgePage: true |
1207 | + |
1208 | + readonly property alias bottomEdgePage: edgeLoader.item |
1209 | + readonly property bool isReady: (tip.opacity === 0.0) |
1210 | + readonly property bool isCollapsed: (tip.opacity === 1.0) |
1211 | + readonly property bool bottomEdgePageLoaded: (edgeLoader.status == Loader.Ready) |
1212 | + |
1213 | + property bool _showEdgePageWhenReady: false |
1214 | + property int _areaWhenExpanded: 0 |
1215 | + |
1216 | + signal bottomEdgeReleased() |
1217 | + signal bottomEdgeDismissed() |
1218 | + |
1219 | + |
1220 | + function showBottomEdgePage(source, properties) |
1221 | + { |
1222 | + edgeLoader.setSource(source, properties) |
1223 | + _showEdgePageWhenReady = true |
1224 | + } |
1225 | + |
1226 | + function setBottomEdgePage(source, properties) |
1227 | + { |
1228 | + edgeLoader.setSource(source, properties) |
1229 | + } |
1230 | + |
1231 | + function _pushPage() |
1232 | + { |
1233 | + if (edgeLoader.status === Loader.Ready) { |
1234 | + edgeLoader.item.active = true |
1235 | + page.pageStack.push(edgeLoader.item) |
1236 | + if (edgeLoader.item.flickable) { |
1237 | + edgeLoader.item.flickable.contentY = -page.header.height |
1238 | + edgeLoader.item.flickable.returnToBounds() |
1239 | + } |
1240 | + if (edgeLoader.item.ready) |
1241 | + edgeLoader.item.ready() |
1242 | + } |
1243 | + } |
1244 | + |
1245 | + |
1246 | + Component.onCompleted: { |
1247 | + // avoid a binding on the expanded height value |
1248 | + var expandedHeight = height; |
1249 | + _areaWhenExpanded = expandedHeight; |
1250 | + } |
1251 | + |
1252 | + onActiveChanged: { |
1253 | + if (active) { |
1254 | + bottomEdge.state = "collapsed" |
1255 | + } |
1256 | + } |
1257 | + |
1258 | + onBottomEdgePageLoadedChanged: { |
1259 | + if (_showEdgePageWhenReady && bottomEdgePageLoaded) { |
1260 | + bottomEdge.state = "expanded" |
1261 | + _showEdgePageWhenReady = false |
1262 | + } |
1263 | + } |
1264 | + |
1265 | + Rectangle { |
1266 | + id: bgVisual |
1267 | + |
1268 | + color: "black" |
1269 | + anchors.fill: page |
1270 | + opacity: 0.7 * ((page.height - bottomEdge.y) / page.height) |
1271 | + z: 1 |
1272 | + } |
1273 | + |
1274 | + Rectangle { |
1275 | + id: bottomEdge |
1276 | + objectName: "bottomEdge" |
1277 | + |
1278 | + readonly property int tipHeight: units.gu(3) |
1279 | + readonly property int pageStartY: 0 |
1280 | + |
1281 | + z: 1 |
1282 | + color: Theme.palette.normal.background |
1283 | + parent: page |
1284 | + anchors { |
1285 | + left: parent.left |
1286 | + right: parent.right |
1287 | + } |
1288 | + height: page.height |
1289 | + y: height |
1290 | + |
1291 | + Rectangle { |
1292 | + id: shadow |
1293 | + |
1294 | + anchors { |
1295 | + left: parent.left |
1296 | + right: parent.right |
1297 | + } |
1298 | + height: units.gu(1) |
1299 | + y: -height |
1300 | + opacity: 0.0 |
1301 | + gradient: Gradient { |
1302 | + GradientStop { position: 0.0; color: "transparent" } |
1303 | + GradientStop { position: 1.0; color: Qt.rgba(0, 0, 0, 0.2) } |
1304 | + } |
1305 | + } |
1306 | + |
1307 | + Item { |
1308 | + id: tipContainer |
1309 | + objectName: "bottomEdgeTip" |
1310 | + |
1311 | + width: childrenRect.width |
1312 | + height: bottomEdge.tipHeight |
1313 | + clip: true |
1314 | + y: -bottomEdge.tipHeight |
1315 | + anchors.horizontalCenter: parent.horizontalCenter |
1316 | + |
1317 | + UbuntuShape { |
1318 | + id: tip |
1319 | + |
1320 | + width: tipLabel.paintedWidth + units.gu(6) |
1321 | + height: bottomEdge.tipHeight + units.gu(1) |
1322 | + color: Theme.palette.normal.overlay |
1323 | + Label { |
1324 | + id: tipLabel |
1325 | + |
1326 | + anchors { |
1327 | + top: parent.top |
1328 | + left: parent.left |
1329 | + right: parent.right |
1330 | + } |
1331 | + height: bottomEdge.tipHeight |
1332 | + verticalAlignment: Text.AlignVCenter |
1333 | + horizontalAlignment: Text.AlignHCenter |
1334 | + } |
1335 | + } |
1336 | + } |
1337 | + |
1338 | + MouseArea { |
1339 | + preventStealing: true |
1340 | + drag.axis: Drag.YAxis |
1341 | + drag.target: bottomEdge |
1342 | + drag.minimumY: bottomEdge.pageStartY |
1343 | + drag.maximumY: page.height |
1344 | + |
1345 | + anchors { |
1346 | + left: parent.left |
1347 | + right: parent.right |
1348 | + } |
1349 | + height: bottomEdge.tipHeight |
1350 | + y: -height |
1351 | + |
1352 | + onReleased: { |
1353 | + page.bottomEdgeReleased() |
1354 | + if (bottomEdge.y < (page.height - bottomEdgeExpandThreshold - bottomEdge.tipHeight)) { |
1355 | + bottomEdge.state = "expanded" |
1356 | + } else { |
1357 | + bottomEdge.state = "collapsed" |
1358 | + bottomEdge.y = bottomEdge.height |
1359 | + } |
1360 | + } |
1361 | + |
1362 | + onPressed: { |
1363 | + bottomEdge.state = "floating" |
1364 | + bottomEdge.y -= bottomEdge.tipHeight |
1365 | + } |
1366 | + } |
1367 | + |
1368 | + Behavior on y { |
1369 | + UbuntuNumberAnimation {} |
1370 | + } |
1371 | + |
1372 | + state: "collapsed" |
1373 | + states: [ |
1374 | + State { |
1375 | + name: "collapsed" |
1376 | + PropertyChanges { |
1377 | + target: bottomEdge |
1378 | + y: bottomEdge.height |
1379 | + } |
1380 | + PropertyChanges { |
1381 | + target: tip |
1382 | + opacity: 1.0 |
1383 | + } |
1384 | + }, |
1385 | + State { |
1386 | + name: "expanded" |
1387 | + PropertyChanges { |
1388 | + target: bottomEdge |
1389 | + y: bottomEdge.pageStartY |
1390 | + } |
1391 | + PropertyChanges { |
1392 | + target: tip |
1393 | + opacity: 0.0 |
1394 | + } |
1395 | + }, |
1396 | + State { |
1397 | + name: "floating" |
1398 | + PropertyChanges { |
1399 | + target: shadow |
1400 | + opacity: 1.0 |
1401 | + } |
1402 | + } |
1403 | + ] |
1404 | + |
1405 | + transitions: [ |
1406 | + Transition { |
1407 | + to: "expanded" |
1408 | + SequentialAnimation { |
1409 | + UbuntuNumberAnimation { |
1410 | + targets: [bottomEdge,tip] |
1411 | + properties: "y,opacity" |
1412 | + duration: UbuntuAnimation.SlowDuration |
1413 | + } |
1414 | + ScriptAction { |
1415 | + script: page._pushPage() |
1416 | + } |
1417 | + } |
1418 | + }, |
1419 | + Transition { |
1420 | + from: "expanded" |
1421 | + to: "collapsed" |
1422 | + SequentialAnimation { |
1423 | + ScriptAction { |
1424 | + script: { |
1425 | + edgeLoader.item.parent = edgeLoader |
1426 | + edgeLoader.item.anchors.fill = edgeLoader |
1427 | + edgeLoader.item.active = false |
1428 | + } |
1429 | + } |
1430 | + UbuntuNumberAnimation { |
1431 | + targets: [bottomEdge,tip] |
1432 | + properties: "y,opacity" |
1433 | + duration: UbuntuAnimation.SlowDuration |
1434 | + } |
1435 | + ScriptAction { |
1436 | + script: { |
1437 | + // destroy current bottom page |
1438 | + if (page.reloadBottomEdgePage) { |
1439 | + edgeLoader.active = false |
1440 | + } |
1441 | + |
1442 | + // notify |
1443 | + page.bottomEdgeDismissed() |
1444 | + |
1445 | + // load a new bottom page in memory |
1446 | + edgeLoader.active = true |
1447 | + } |
1448 | + } |
1449 | + } |
1450 | + }, |
1451 | + Transition { |
1452 | + from: "floating" |
1453 | + to: "collapsed" |
1454 | + UbuntuNumberAnimation { |
1455 | + targets: [bottomEdge,tip] |
1456 | + properties: "y,opacity" |
1457 | + } |
1458 | + } |
1459 | + ] |
1460 | + |
1461 | + Loader { |
1462 | + id: edgeLoader |
1463 | + |
1464 | + z: 1 |
1465 | + active: true |
1466 | + asynchronous: true |
1467 | + anchors.fill: parent |
1468 | + |
1469 | + //WORKAROUND: The SDK move the page contents down to allocate space for the header we need to avoid that during the page dragging |
1470 | + Binding { |
1471 | + target: edgeLoader |
1472 | + property: "anchors.topMargin" |
1473 | + value: edgeLoader.item && edgeLoader.item.flickable ? edgeLoader.item.flickable.contentY : 0 |
1474 | + when: (edgeLoader.status === Loader.Ready && !page.isReady) |
1475 | + } |
1476 | + |
1477 | + onLoaded: { |
1478 | + if (page.isReady && edgeLoader.item.active != true) { |
1479 | + page._pushPage() |
1480 | + } |
1481 | + } |
1482 | + } |
1483 | + } |
1484 | +} |
1485 | |
1486 | === modified file 'src/imports/ContactView/ContactDetailAvatarView.qml' |
1487 | --- src/imports/ContactView/ContactDetailAvatarView.qml 2014-05-06 13:18:07 +0000 |
1488 | +++ src/imports/ContactView/ContactDetailAvatarView.qml 2014-06-06 17:32:09 +0000 |
1489 | @@ -44,7 +44,7 @@ |
1490 | // update the contact detail in case of the contact change |
1491 | Connections { |
1492 | target: root.contact |
1493 | - onContactChanged: { |
1494 | + onContactChanged: { |
1495 | if (root.contact) { |
1496 | root.detail = contact.detail(ContactDetail.Avatar) |
1497 | } else { |
1498 | @@ -53,7 +53,14 @@ |
1499 | } |
1500 | } |
1501 | |
1502 | - onDetailChanged: updateAvatar.restart() |
1503 | + onDetailChanged: { |
1504 | + var newAvatar = root.getAvatar(root.detail) |
1505 | + if (newAvatar !== defaultAvatar) { |
1506 | + avatar.source = newAvatar |
1507 | + } else { |
1508 | + updateAvatar.restart() |
1509 | + } |
1510 | + } |
1511 | |
1512 | // Wait some milliseconds before update the avatar, in some cases the avatac get update later and this cause the image flick |
1513 | Timer { |
1514 | |
1515 | === modified file 'src/imports/ContactView/ContactDetailSyncTargetView.qml' |
1516 | --- src/imports/ContactView/ContactDetailSyncTargetView.qml 2014-05-20 14:56:42 +0000 |
1517 | +++ src/imports/ContactView/ContactDetailSyncTargetView.qml 2014-06-06 17:32:09 +0000 |
1518 | @@ -62,5 +62,8 @@ |
1519 | value: Type.Group |
1520 | matchFlags: DetailFilter.MatchExactly |
1521 | } |
1522 | + autoUpdate: false |
1523 | } |
1524 | + |
1525 | + Component.onCompleted: sourceModel.update() |
1526 | } |
1527 | |
1528 | === modified file 'src/imports/ContactView/ContactView.qml' |
1529 | --- src/imports/ContactView/ContactView.qml 2014-05-06 13:18:07 +0000 |
1530 | +++ src/imports/ContactView/ContactView.qml 2014-06-06 17:32:09 +0000 |
1531 | @@ -51,12 +51,21 @@ |
1532 | } |
1533 | } |
1534 | |
1535 | + // Page page if the contact get removed |
1536 | + onContactChanged: { |
1537 | + if (!contact) { |
1538 | + pageStack.pop() |
1539 | + } |
1540 | + } |
1541 | + |
1542 | Flickable { |
1543 | id: flickable |
1544 | |
1545 | flickableDirection: Flickable.VerticalFlick |
1546 | anchors.fill: parent |
1547 | - contentHeight: contents.height |
1548 | + //WORKAROUND: There is a bug on SDK page that causes the page to appear flicked with small contents |
1549 | + // see bug #1223050 |
1550 | + contentHeight: Math.max(contents.height, parent.height) |
1551 | contentWidth: parent.width |
1552 | visible: !busyIndicator.visible |
1553 | |
1554 | @@ -175,21 +184,8 @@ |
1555 | |
1556 | tools: ToolbarItems { |
1557 | ToolbarButton { |
1558 | - objectName: "delete" |
1559 | - |
1560 | - action: Action { |
1561 | - text: i18n.tr("Delete") |
1562 | - iconSource: "artwork:/delete.png" |
1563 | - onTriggered: { |
1564 | - root.model.removeContact(root.contact.contactId) |
1565 | - pageStack.pop() |
1566 | - } |
1567 | - } |
1568 | - } |
1569 | - ToolbarButton { |
1570 | - objectName: "edit" |
1571 | - |
1572 | - action: Action { |
1573 | + action: Action { |
1574 | + objectName: "edit" |
1575 | text: i18n.tr("Edit") |
1576 | iconSource: "artwork:/edit.png" |
1577 | onTriggered: { |
1578 | |
1579 | === modified file 'src/imports/MainWindow.qml' |
1580 | --- src/imports/MainWindow.qml 2014-05-28 19:56:00 +0000 |
1581 | +++ src/imports/MainWindow.qml 2014-06-06 17:32:09 +0000 |
1582 | @@ -27,11 +27,7 @@ |
1583 | width: units.gu(40) |
1584 | height: units.gu(71) |
1585 | anchorToKeyboard: false |
1586 | - |
1587 | - // workaround to change the application theme. |
1588 | - // Looks like SDK use this property to guess which theme to load. |
1589 | - // See bug #1277647 |
1590 | - backgroundColor: "#221E1C" |
1591 | + useDeprecatedToolbar: false |
1592 | |
1593 | signal applicationReady() |
1594 | |
1595 | |
1596 | === modified file 'src/imports/Ubuntu/Contacts/ContactListView.qml' |
1597 | --- src/imports/Ubuntu/Contacts/ContactListView.qml 2014-05-28 19:56:00 +0000 |
1598 | +++ src/imports/Ubuntu/Contacts/ContactListView.qml 2014-06-06 17:32:09 +0000 |
1599 | @@ -1,4 +1,4 @@ |
1600 | -/* |
1601 | + /* |
1602 | * Copyright (C) 2012-2013 Canonical, Ltd. |
1603 | * |
1604 | * This program is free software; you can redistribute it and/or modify |
1605 | @@ -51,7 +51,7 @@ |
1606 | left: parent.left |
1607 | right: parent.right |
1608 | } |
1609 | - color: UbuntuColors.coolGrey |
1610 | + color: Theme.palette.normal.overlay |
1611 | |
1612 | Row { |
1613 | anchors.fill: parent |
1614 | |
1615 | === modified file 'src/imports/Ubuntu/Contacts/ContactSimpleListView.qml' |
1616 | --- src/imports/Ubuntu/Contacts/ContactSimpleListView.qml 2014-05-28 19:56:00 +0000 |
1617 | +++ src/imports/Ubuntu/Contacts/ContactSimpleListView.qml 2014-06-06 17:32:09 +0000 |
1618 | @@ -16,6 +16,7 @@ |
1619 | |
1620 | import QtQuick 2.2 |
1621 | import QtContacts 5.0 |
1622 | +import Ubuntu.Contacts 0.1 |
1623 | import Ubuntu.Components 0.1 |
1624 | import Ubuntu.Components.ListItems 0.1 as ListItem |
1625 | |
1626 | @@ -285,9 +286,6 @@ |
1627 | } |
1628 | } |
1629 | |
1630 | - acceptAction.text: i18n.dtr("address-book-app", "Delete") |
1631 | - |
1632 | - listModel: contactsModel |
1633 | onCountChanged: { |
1634 | busyIndicator.ping() |
1635 | dirtyModel.restart() |
1636 | @@ -493,8 +491,20 @@ |
1637 | ContactFetch { |
1638 | id: contactFetch |
1639 | |
1640 | - model: contactListView.listModel |
1641 | - onContactFetched: contactListView.contactClicked(contact) |
1642 | + //WORKAROUND: Use a different model to fetch contacts, due a bug on qtpim the contact get |
1643 | + // destroyed if you change the filter model |
1644 | + ContactModel { |
1645 | + id: contactFetchModel |
1646 | + |
1647 | + manager: root.manager |
1648 | + autoUpdate: false |
1649 | + } |
1650 | + |
1651 | + model: root.manager == "memory" ? root.listModel : contactFetchModel |
1652 | + onContactFetched: { |
1653 | + console.debug("Contact fetched") |
1654 | + contactListView.contactClicked(contact) |
1655 | + } |
1656 | } |
1657 | |
1658 | // This is a workaround to make sure the spinner will disappear if the model is empty |
1659 | |
1660 | === modified file 'src/imports/Ubuntu/Contacts/MultipleSelectionListView.qml' |
1661 | --- src/imports/Ubuntu/Contacts/MultipleSelectionListView.qml 2014-05-06 13:18:07 +0000 |
1662 | +++ src/imports/Ubuntu/Contacts/MultipleSelectionListView.qml 2014-06-06 17:32:09 +0000 |
1663 | @@ -71,25 +71,6 @@ |
1664 | property bool multipleSelection: true |
1665 | |
1666 | /*! |
1667 | - \qmlproperty Action acceptAction |
1668 | - |
1669 | - This property holds the action used into the accept button |
1670 | - */ |
1671 | - property alias acceptAction: sheet.acceptAction |
1672 | - /*! |
1673 | - \qmlproperty Action acceptAction |
1674 | - |
1675 | - This property holds the action used into the reject button |
1676 | - */ |
1677 | - property alias rejectAction: sheet.rejectAction |
1678 | - /*! |
1679 | - \qmlproperty bool showActionButtons |
1680 | - |
1681 | - This property holds if the default sheet element must be visible during the selection |
1682 | - By default this is set to true |
1683 | - */ |
1684 | - property alias showActionButtons: sheet.enabled |
1685 | - /*! |
1686 | \qmlproperty model listModel |
1687 | |
1688 | This property holds the model providing data for the list. |
1689 | @@ -191,42 +172,19 @@ |
1690 | selectedItems.remove(0, selectedItems.count) |
1691 | } |
1692 | } |
1693 | - |
1694 | - states: [ |
1695 | - State { |
1696 | - name: "selection" |
1697 | - PropertyChanges { |
1698 | - target: sheet |
1699 | - active: true |
1700 | - } |
1701 | - PropertyChanges { |
1702 | - target: listView |
1703 | - bottomMargin: sheet.height |
1704 | - } |
1705 | + /*! |
1706 | + Select all items in the list |
1707 | + */ |
1708 | + function selectAll() |
1709 | + { |
1710 | + if (multipleSelection) { |
1711 | + visualModel.items.addGroups(0, visualModel.items.count, ["selected"] ) |
1712 | } |
1713 | - ] |
1714 | + } |
1715 | |
1716 | model: visualModel |
1717 | |
1718 | MultipleSelectionVisualModel { |
1719 | id: visualModel |
1720 | } |
1721 | - |
1722 | - DialogButtons { |
1723 | - id: sheet |
1724 | - |
1725 | - property bool active: false |
1726 | - property bool enabled: true |
1727 | - |
1728 | - anchors { |
1729 | - left: parent.left |
1730 | - right: parent.right |
1731 | - bottom: parent.bottom |
1732 | - } |
1733 | - height: visible ? units.gu(6) : 0 |
1734 | - visible: active && enabled |
1735 | - |
1736 | - onReject: listView.cancelSelection() |
1737 | - onAccept: listView.endSelection() |
1738 | - } |
1739 | } |
1740 | |
1741 | === removed file 'src/imports/Ubuntu/Contacts/OrganicView.qml' |
1742 | --- src/imports/Ubuntu/Contacts/OrganicView.qml 2013-07-22 23:49:56 +0000 |
1743 | +++ src/imports/Ubuntu/Contacts/OrganicView.qml 1970-01-01 00:00:00 +0000 |
1744 | @@ -1,135 +0,0 @@ |
1745 | -/* |
1746 | - * Copyright (C) 2012-2013 Canonical, Ltd. |
1747 | - * |
1748 | - * This program is free software; you can redistribute it and/or modify |
1749 | - * it under the terms of the GNU General Public License as published by |
1750 | - * the Free Software Foundation; version 3. |
1751 | - * |
1752 | - * This program is distributed in the hope that it will be useful, |
1753 | - * but WITHOUT ANY WARRANTY; without even the implied warranty of |
1754 | - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
1755 | - * GNU General Public License for more details. |
1756 | - * |
1757 | - * You should have received a copy of the GNU General Public License |
1758 | - * along with this program. If not, see <http://www.gnu.org/licenses/>. |
1759 | - */ |
1760 | - |
1761 | -import QtQuick 2.0 |
1762 | -import Ubuntu.Components 0.1 |
1763 | - |
1764 | -Item { |
1765 | - id: organicList |
1766 | - |
1767 | - readonly property alias count: listView.count |
1768 | - property alias model: listView.model |
1769 | - property Component delegate |
1770 | - property Component header |
1771 | - |
1772 | - /// Size of the bigger thumbnails |
1773 | - property var bigSize: units.gu(19) |
1774 | - /// Size of the smaller thumbnails |
1775 | - property var smallSize: units.gu(12) |
1776 | - /// Space between the thumbnails |
1777 | - property alias margin: listView.spacing |
1778 | - |
1779 | - QtObject { |
1780 | - id: priv |
1781 | - |
1782 | - // readonly |
1783 | - readonly property int mediaPerPattern: 6 |
1784 | - |
1785 | - // internal, used to get the organix effect |
1786 | - /// X-position shift for the delegates in one of the organic blocks |
1787 | - readonly property var photoX: [-margin, 0, 0, 0, 0, 0] |
1788 | - /// Y-position shift for the delegates in one of the organic blocks |
1789 | - readonly property var photoY: [smallSize + margin, 0, bigSize + margin, 0, bigSize + margin, 0] |
1790 | - /// Size of the delegate in one of the organic blocks |
1791 | - readonly property var photoSize: [bigSize, smallSize, smallSize, bigSize, smallSize, smallSize] |
1792 | - /// Size to be adapted for the organic effect |
1793 | - readonly property var photoWidth: [smallSize - margin, bigSize - (smallSize + margin), 2 * smallSize - bigSize, bigSize - (smallSize + margin), smallSize, 0] |
1794 | - /// Extra space on the right, for correct scroll boundary |
1795 | - readonly property var footerWidth: [smallSize, |
1796 | - bigSize - smallSize, |
1797 | - 2 * smallSize - bigSize + margin, |
1798 | - bigSize - smallSize, |
1799 | - smallSize + margin, |
1800 | - 0] |
1801 | - /// Used to generate a spacing between the events |
1802 | - readonly property real photosTopMargin: margin / 2 |
1803 | - } |
1804 | - |
1805 | - implicitHeight: bigSize + smallSize + priv.photosTopMargin + margin + margin/2 |
1806 | - height: implicitHeight |
1807 | - |
1808 | - ListView { |
1809 | - id: listView |
1810 | - objectName: "displaced" |
1811 | - // the buffers are needed, as the listview does not draw items outside is visible area |
1812 | - // but for the organic effect, x and width are "displaced" for some items (first, last) |
1813 | - property int leftBuffer: organicList.smallSize + organicList.margin |
1814 | - property int rightBuffer: organicList.smallSize |
1815 | - |
1816 | - anchors { |
1817 | - top: parent.top |
1818 | - bottom: parent.bottom |
1819 | - left: parent.left |
1820 | - leftMargin: -leftBuffer |
1821 | - right: parent.right |
1822 | - rightMargin: -rightBuffer |
1823 | - } |
1824 | - |
1825 | - maximumFlickVelocity: units.gu(300) |
1826 | - flickDeceleration: maximumFlickVelocity / 3 |
1827 | - cacheBuffer: 0 |
1828 | - orientation: Qt.Horizontal |
1829 | - spacing: units.gu(2) |
1830 | - |
1831 | - property int ccoun: children.length |
1832 | - |
1833 | - delegate: Item { |
1834 | - id: itemDelegate |
1835 | - |
1836 | - property int patternPhoto: index % priv.mediaPerPattern |
1837 | - |
1838 | - width: priv.photoWidth[patternPhoto] |
1839 | - height: priv.photoSize[patternPhoto] |
1840 | - |
1841 | - Loader { |
1842 | - id: delegateLoader |
1843 | - |
1844 | - property var model: null |
1845 | - property int index: -1 |
1846 | - |
1847 | - x: priv.photoX[patternPhoto] |
1848 | - y: priv.photosTopMargin + priv.photoY[patternPhoto] |
1849 | - |
1850 | - width: parent.height |
1851 | - height: parent.height |
1852 | - sourceComponent: organicList.delegate |
1853 | - } |
1854 | - |
1855 | - Binding { |
1856 | - target: delegateLoader |
1857 | - property: "model" |
1858 | - value: model |
1859 | - } |
1860 | - |
1861 | - Binding { |
1862 | - target: delegateLoader |
1863 | - property: "index" |
1864 | - value: index |
1865 | - } |
1866 | - } |
1867 | - |
1868 | - header: Loader { |
1869 | - width: listView.leftBuffer + 2 * organicList.margin |
1870 | - height: organicList.smallSize |
1871 | - |
1872 | - sourceComponent: organicList.header |
1873 | - } |
1874 | - |
1875 | - footer: Item { |
1876 | - width: listView.rightBuffer + priv.footerWidth[listView.count % priv.mediaPerPattern] + listView.spacing |
1877 | - } |
1878 | - } |
1879 | -} |
1880 | |
1881 | === added file 'tests/autopilot/address_book_app/__init__.py' |
1882 | --- tests/autopilot/address_book_app/__init__.py 1970-01-01 00:00:00 +0000 |
1883 | +++ tests/autopilot/address_book_app/__init__.py 2014-06-06 17:32:09 +0000 |
1884 | @@ -0,0 +1,150 @@ |
1885 | +# -*- Mode: Python; coding: utf-8; indent-tabs-mode: nil; tab-width: 4 -*- |
1886 | +# |
1887 | +# Copyright (C) 2014 Canonical Ltd. |
1888 | +# |
1889 | +# This program is free software; you can redistribute it and/or modify |
1890 | +# it under the terms of the GNU General Public License version 3, as published |
1891 | +# by the Free Software Foundation. |
1892 | +# |
1893 | +# This program is distributed in the hope that it will be useful, |
1894 | +# but WITHOUT ANY WARRANTY; without even the implied warranty of |
1895 | +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
1896 | +# GNU General Public License for more details. |
1897 | +# |
1898 | +# You should have received a copy of the GNU General Public License |
1899 | +# along with this program. If not, see <http://www.gnu.org/licenses/>. |
1900 | + |
1901 | +"""address-book-app autopilot tests and emulators - top level package.""" |
1902 | + |
1903 | +import logging |
1904 | + |
1905 | + |
1906 | +logging.basicConfig(filename='warning.log', level=logging.WARNING) |
1907 | + |
1908 | + |
1909 | +import autopilot.logging |
1910 | +import ubuntuuitoolkit |
1911 | +from autopilot.introspection import dbus |
1912 | + |
1913 | +from address_book_app import pages |
1914 | + |
1915 | + |
1916 | +logger = logging.getLogger(__name__) |
1917 | + |
1918 | + |
1919 | +class AddressBookApp(object): |
1920 | + |
1921 | + def __init__(self, app_proxy): |
1922 | + super(AddressBookApp, self).__init__() |
1923 | + self.app_proxy = app_proxy |
1924 | + self.main_window = self.app_proxy.select_single(MainWindow) |
1925 | + |
1926 | + |
1927 | +class MainWindow(ubuntuuitoolkit.MainView): |
1928 | + """An emulator class that makes it easy to interact with the app.""" |
1929 | + |
1930 | + def get_contact_list_page(self): |
1931 | + # ContactListPage is the only page that can appears multiple times |
1932 | + # Ex.: During the pick mode we alway push a new contactListPage, to |
1933 | + # preserve the current application status. |
1934 | + contact_list_pages = self.select_many( |
1935 | + pages.ContactListPage, objectName='contactListPage') |
1936 | + |
1937 | + # alway return the page without pickMode |
1938 | + for p in contact_list_pages: |
1939 | + if not p.pickMode: |
1940 | + return p |
1941 | + return None |
1942 | + |
1943 | + def get_contact_edit_page(self): |
1944 | + # We can have two contact editor page because of bottom edge page |
1945 | + # but we will return only the active one |
1946 | + list_page = self.get_contact_list_page() |
1947 | + list_page.bottomEdgePageLoaded.wait_for(True) |
1948 | + contact_editor_pages = self.select_many( |
1949 | + pages.ContactEditor, objectName="contactEditorPage") |
1950 | + for p in contact_editor_pages: |
1951 | + if p.active: |
1952 | + return p |
1953 | + raise dbus.StateNotFoundError('contactEditorPage not found') |
1954 | + return None |
1955 | + |
1956 | + |
1957 | + def get_contact_view_page(self): |
1958 | + return self.wait_select_single("ContactView", |
1959 | + objectName="contactViewPage") |
1960 | + |
1961 | + def get_contact_list_pick_page(self): |
1962 | + pages = self.select_many("ContactListPage", |
1963 | + objectName="contactListPage") |
1964 | + for p in pages: |
1965 | + if p.pickMode: |
1966 | + return p |
1967 | + return None |
1968 | + |
1969 | + def get_contact_list_view(self): |
1970 | + """ |
1971 | + Returns a ContactListView iobject for the current window |
1972 | + """ |
1973 | + return self.wait_select_single("ContactListView", |
1974 | + objectName="contactListView") |
1975 | + |
1976 | + def get_button(self, buttonName): |
1977 | + return self.get_header()._get_action_button(buttonName) |
1978 | + |
1979 | + def open_header(self): |
1980 | + header = self.get_header() |
1981 | + if (header.y != 0): |
1982 | + edit_page = self.get_contact_edit_page() |
1983 | + flickable = edit_page.wait_select_single( |
1984 | + "QQuickFlickable", |
1985 | + objectName="scrollArea") |
1986 | + |
1987 | + while (header.y != 0): |
1988 | + globalRect = flickable.globalRect |
1989 | + start_x = globalRect.x + (globalRect.width * 0.5) |
1990 | + start_y = globalRect.y + (flickable.height * 0.1) |
1991 | + stop_y = start_y + (flickable.height * 0.1) |
1992 | + |
1993 | + self.pointing_device.drag( |
1994 | + start_x, start_y, start_x, stop_y, rate=5) |
1995 | + # wait flicking stops to move to the next field |
1996 | + flickable.flicking.wait_for(False) |
1997 | + |
1998 | + return header |
1999 | + |
2000 | + def cancel(self): |
2001 | + """ |
2002 | + Press the 'Cancel' button |
2003 | + """ |
2004 | + header = self.open_header() |
2005 | + header.click_custom_back_button() |
2006 | + |
2007 | + def save(self): |
2008 | + """ |
2009 | + Press the 'Save' button |
2010 | + """ |
2011 | + bottom_swipe_page = self.get_contact_list_page() |
2012 | + self.click_action_button("save") |
2013 | + bottom_swipe_page.isCollapsed.wait_for(True) |
2014 | + |
2015 | + def done_selection(self): |
2016 | + """ |
2017 | + Press the 'doneSelection' button |
2018 | + """ |
2019 | + bottom_swipe_page = self.get_contact_list_page() |
2020 | + self.click_action_button("doneSelection") |
2021 | + bottom_swipe_page.isCollapsed.wait_for(True) |
2022 | + |
2023 | + def get_toolbar(self): |
2024 | + """Override base class so we get our expected Toolbar subclass.""" |
2025 | + return self.select_single(ubuntuuitoolkit.Toolbar) |
2026 | + |
2027 | + @autopilot.logging.log_action(logger.info) |
2028 | + def go_to_add_contact(self): |
2029 | + """ |
2030 | + Press the 'Add' button and return the contact editor page |
2031 | + """ |
2032 | + bottom_swipe_page = self.get_contact_list_page() |
2033 | + bottom_swipe_page.revel_bottom_edge_page() |
2034 | + return self.get_contact_edit_page() |
2035 | |
2036 | === removed file 'tests/autopilot/address_book_app/__init__.py' |
2037 | --- tests/autopilot/address_book_app/__init__.py 2013-07-09 18:42:30 +0000 |
2038 | +++ tests/autopilot/address_book_app/__init__.py 1970-01-01 00:00:00 +0000 |
2039 | @@ -1,8 +0,0 @@ |
2040 | -# -*- Mode: Python; coding: utf-8; indent-tabs-mode: nil; tab-width: 4 -*- |
2041 | -# Copyright 2013 Canonical |
2042 | -# |
2043 | -# This program is free software: you can redistribute it and/or modify it |
2044 | -# under the terms of the GNU General Public License version 3, as published |
2045 | -# by the Free Software Foundation. |
2046 | - |
2047 | -"""address-book-app autopilot tests and emulators - top level package.""" |
2048 | |
2049 | === added file 'tests/autopilot/address_book_app/_errors.py' |
2050 | --- tests/autopilot/address_book_app/_errors.py 1970-01-01 00:00:00 +0000 |
2051 | +++ tests/autopilot/address_book_app/_errors.py 2014-06-06 17:32:09 +0000 |
2052 | @@ -0,0 +1,21 @@ |
2053 | +# -*- Mode: Python; coding: utf-8; indent-tabs-mode: nil; tab-width: 4 -*- |
2054 | +# |
2055 | +# Copyright (C) 2014 Canonical Ltd. |
2056 | +# |
2057 | +# This program is free software; you can redistribute it and/or modify |
2058 | +# it under the terms of the GNU General Public License version 3, as published |
2059 | +# by the Free Software Foundation. |
2060 | +# |
2061 | +# This program is distributed in the hope that it will be useful, |
2062 | +# but WITHOUT ANY WARRANTY; without even the implied warranty of |
2063 | +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
2064 | +# GNU General Public License for more details. |
2065 | +# |
2066 | +# You should have received a copy of the GNU General Public License |
2067 | +# along with this program. If not, see <http://www.gnu.org/licenses/>. |
2068 | + |
2069 | +import ubuntuuitoolkit |
2070 | + |
2071 | + |
2072 | +class AddressBookAppError(ubuntuuitoolkit.ToolkitException): |
2073 | + """Exception raised when there is an error with the emulator.""" |
2074 | |
2075 | === removed directory 'tests/autopilot/address_book_app/emulators' |
2076 | === removed file 'tests/autopilot/address_book_app/emulators/__init__.py' |
2077 | --- tests/autopilot/address_book_app/emulators/__init__.py 2013-07-09 18:42:30 +0000 |
2078 | +++ tests/autopilot/address_book_app/emulators/__init__.py 1970-01-01 00:00:00 +0000 |
2079 | @@ -1,6 +0,0 @@ |
2080 | -# -*- Mode: Python; coding: utf-8; indent-tabs-mode: nil; tab-width: 4 -*- |
2081 | -# Copyright 2013 Canonical |
2082 | -# |
2083 | -# This program is free software: you can redistribute it and/or modify it |
2084 | -# under the terms of the GNU General Public License version 3, as published |
2085 | -# by the Free Software Foundation. |
2086 | |
2087 | === removed file 'tests/autopilot/address_book_app/emulators/main_window.py' |
2088 | --- tests/autopilot/address_book_app/emulators/main_window.py 2014-05-12 19:44:47 +0000 |
2089 | +++ tests/autopilot/address_book_app/emulators/main_window.py 1970-01-01 00:00:00 +0000 |
2090 | @@ -1,342 +0,0 @@ |
2091 | -# -*- Mode: Python; coding: utf-8; indent-tabs-mode: nil; tab-width: 4 -*- |
2092 | -# Copyright 2013, 2014 Canonical |
2093 | -# |
2094 | -# This program is free software: you can redistribute it and/or modify it |
2095 | -# under the terms of the GNU General Public License version 3, as published |
2096 | -# by the Free Software Foundation. |
2097 | - |
2098 | -import collections |
2099 | -import logging |
2100 | -import time |
2101 | - |
2102 | -from address_book_app.emulators.contact_list_page import ContactListPage |
2103 | -from address_book_app.emulators.toolbar import Toolbar |
2104 | -from autopilot import logging as autopilot_logging |
2105 | -from ubuntuuitoolkit import emulators as uitk |
2106 | - |
2107 | -from address_book_app import data |
2108 | - |
2109 | - |
2110 | -logger = logging.getLogger(__name__) |
2111 | - |
2112 | - |
2113 | -_TEXT_FIELD_OBJECT_NAMES = { |
2114 | - 'first_name': 'firstName', |
2115 | - 'last_name': 'lastName', |
2116 | - 'street': 'streetAddress_{}', |
2117 | - 'locality': 'localityAddress_{}', |
2118 | - 'region': 'regionAddress_{}', |
2119 | - 'postal_code': 'postcodeAddress_{}', |
2120 | - 'country': 'countryAddress_{}' |
2121 | -} |
2122 | - |
2123 | - |
2124 | -def _get_text_field(parent, field, index=None): |
2125 | - if field not in _TEXT_FIELD_OBJECT_NAMES: |
2126 | - raise AddressBookAppError('Unknown field: {}.'.format(field)) |
2127 | - |
2128 | - object_name = _TEXT_FIELD_OBJECT_NAMES[field] |
2129 | - if index is not None: |
2130 | - object_name = object_name.format(index) |
2131 | - return parent.select_single(TextInputDetail, objectName=object_name) |
2132 | - |
2133 | - |
2134 | -class AddressBookAppError(uitk.ToolkitEmulatorException): |
2135 | - """Exception raised when there is an error with the emulator.""" |
2136 | - |
2137 | - |
2138 | -class MainWindow(uitk.MainView): |
2139 | - """An emulator class that makes it easy to interact with the app.""" |
2140 | - |
2141 | - def get_contact_list_page(self): |
2142 | - # ContactListPage is the only page that can appears multiple times |
2143 | - # Ex.: During the pick mode we alway push a new contactListPage, to |
2144 | - # preserve the current application status. |
2145 | - pages = self.select_many(ContactListPage, |
2146 | - objectName="contactListPage") |
2147 | - |
2148 | - # alway return the page without pickMode |
2149 | - for p in pages: |
2150 | - if not p.pickMode: |
2151 | - return p |
2152 | - return None |
2153 | - |
2154 | - def get_contact_edit_page(self): |
2155 | - return self.wait_select_single(ContactEditor, |
2156 | - objectName="contactEditorPage") |
2157 | - |
2158 | - def get_contact_view_page(self): |
2159 | - return self.wait_select_single("ContactView", |
2160 | - objectName="contactViewPage") |
2161 | - |
2162 | - def get_contact_list_pick_page(self): |
2163 | - pages = self.select_many("ContactListPage", |
2164 | - objectName="contactListPage") |
2165 | - for p in pages: |
2166 | - if p.pickMode: |
2167 | - return p |
2168 | - return None |
2169 | - |
2170 | - def get_contact_list_view(self): |
2171 | - """ |
2172 | - Returns a ContactListView iobject for the current window |
2173 | - """ |
2174 | - return self.wait_select_single( "ContactListView", |
2175 | - objectName="contactListView") |
2176 | - |
2177 | - def get_button(self, name): |
2178 | - """ |
2179 | - Returns a Button object matching 'name' |
2180 | - |
2181 | - Arguments: |
2182 | - name: Name of the button |
2183 | - """ |
2184 | - return self.wait_select_single( "Button", objectName=name) |
2185 | - |
2186 | - def cancel(self): |
2187 | - """ |
2188 | - Press the 'Cancel' button |
2189 | - """ |
2190 | - self.pointing_device.click_object(self.get_button("reject")) |
2191 | - |
2192 | - def save(self): |
2193 | - """ |
2194 | - Press the 'Save' button |
2195 | - """ |
2196 | - self.pointing_device.click_object(self.get_button("accept")) |
2197 | - |
2198 | - def get_toolbar(self): |
2199 | - """Override base class so we get our expected Toolbar subclass.""" |
2200 | - return self.select_single(Toolbar) |
2201 | - |
2202 | - @autopilot_logging.log_action(logger.info) |
2203 | - def go_to_add_contact(self): |
2204 | - """ |
2205 | - Press the 'Add' button and return the contact editor page |
2206 | - """ |
2207 | - toolbar = self.open_toolbar() |
2208 | - toolbar.click_button(object_name="Add") |
2209 | - return self.get_contact_edit_page() |
2210 | - |
2211 | - |
2212 | -class ContactEditor(uitk.UbuntuUIToolkitEmulatorBase): |
2213 | - """Custom proxy object for the Contact Editor.""" |
2214 | - |
2215 | - @autopilot_logging.log_action(logger.info) |
2216 | - def fill_form(self, contact_information): |
2217 | - """Fill the edit contact form. |
2218 | - |
2219 | - :param contact_information: Values of the contact to fill the form. |
2220 | - :type contact_information: data object with the attributes first_name, |
2221 | - last_name, phones, emails, social_aliases, addresses and |
2222 | - professional_details |
2223 | - |
2224 | - """ |
2225 | - if contact_information.first_name is not None: |
2226 | - self._fill_first_name(contact_information.first_name) |
2227 | - if contact_information.last_name is not None: |
2228 | - self._fill_last_name(contact_information.last_name) |
2229 | - |
2230 | - groups = collections.OrderedDict() |
2231 | - groups['phones'] = contact_information.phones |
2232 | - groups['emails'] = contact_information.emails |
2233 | - groups['ims'] = contact_information.social_aliases |
2234 | - groups['addresses'] = contact_information.addresses |
2235 | - groups['professionalDetails'] = ( |
2236 | - contact_information.professional_details) |
2237 | - |
2238 | - for key, information in groups.items(): |
2239 | - if information: |
2240 | - self._fill_detail_group( |
2241 | - object_name=key, details=information) |
2242 | - |
2243 | - def _fill_first_name(self, first_name): |
2244 | - text_field = _get_text_field(self, 'first_name') |
2245 | - text_field.write(first_name) |
2246 | - |
2247 | - def _fill_last_name(self, last_name): |
2248 | - text_field = _get_text_field(self, 'last_name') |
2249 | - text_field.write(last_name) |
2250 | - |
2251 | - def _fill_detail_group(self, object_name, details): |
2252 | - editor = self.select_single( |
2253 | - ContactDetailGroupWithTypeEditor, objectName=object_name) |
2254 | - editor.fill(details) |
2255 | - |
2256 | - def _get_form_values(self): |
2257 | - first_name = _get_text_field(self, 'first_name').text |
2258 | - last_name = _get_text_field(self, 'last_name').text |
2259 | - phones = self._get_values_from_detail_group(object_name='phones') |
2260 | - emails = self._get_values_from_detail_group(object_name='emails') |
2261 | - social_aliases = self._get_values_from_detail_group(object_name='ims') |
2262 | - addresses = self._get_values_from_detail_group(object_name='addresses') |
2263 | - professional_details = self._get_values_from_detail_group( |
2264 | - object_name='professionalDetails') |
2265 | - |
2266 | - return data.Contact( |
2267 | - first_name=first_name, last_name=last_name, phones=phones, |
2268 | - emails=emails, social_aliases=social_aliases, addresses=addresses, |
2269 | - professional_details=professional_details) |
2270 | - |
2271 | - def _get_values_from_detail_group(self, object_name): |
2272 | - editor = self.select_single( |
2273 | - ContactDetailGroupWithTypeEditor, objectName=object_name) |
2274 | - return editor.get_values(object_name) |
2275 | - |
2276 | - def wait_to_stop_moving(self): |
2277 | - flickable = self.select_single( |
2278 | - 'QQuickFlickable', objectName='scrollArea') |
2279 | - flickable.flicking.wait_for(False) |
2280 | - |
2281 | - |
2282 | -class TextInputDetail(uitk.TextField): |
2283 | - """Custom proxy object for the Text Input Detail field.""" |
2284 | - |
2285 | - |
2286 | -class ContactDetailGroupWithTypeEditor(uitk.UbuntuUIToolkitEmulatorBase): |
2287 | - """Custom proxy object for the ContactDetailGroupWithTypeEditor widget.""" |
2288 | - |
2289 | - _DETAIL_EDITORS = { |
2290 | - 'phones': 'base_phoneNumber_{}', |
2291 | - 'emails': 'base_email_{}', |
2292 | - 'ims': 'base_onlineAccount_{}', |
2293 | - 'addresses': 'base_address_{}', |
2294 | - # FIXME fix the unknown. --elopio - 2014-03-01 |
2295 | - 'professionalDetails': 'base_unknown_{}' |
2296 | - } |
2297 | - |
2298 | - def fill(self, details): |
2299 | - """Fill a contact detail group.""" |
2300 | - for index, detail in enumerate(details[:-1]): |
2301 | - self._fill_detail(index, detail) |
2302 | - self._add_detail() |
2303 | - self._fill_detail(len(details) - 1, details[-1]) |
2304 | - |
2305 | - def _fill_detail(self, index, detail): |
2306 | - detail_editor = self._get_detail_editor_by_index(index) |
2307 | - detail_editor.fill(field=self.objectName, index=index, detail=detail) |
2308 | - |
2309 | - def _get_detail_editor_by_index(self, index): |
2310 | - object_name = self._get_contact_detail_editor_object_name(index) |
2311 | - return self.select_single( |
2312 | - ContactDetailWithTypeEditor, objectName=object_name) |
2313 | - |
2314 | - def _get_contact_detail_editor_object_name(self, index): |
2315 | - return self._DETAIL_EDITORS[self.objectName].format(index) |
2316 | - |
2317 | - def _add_detail(self): |
2318 | - # TODO --elopio - 2014-03-01 |
2319 | - raise NotImplementedError('Add extra details not yet implemented.') |
2320 | - |
2321 | - def get_values(self, object_name): |
2322 | - """Return the values of a contact detail group.""" |
2323 | - values = [] |
2324 | - for index in range(self.detailsCount): |
2325 | - detail_editor = self._get_detail_editor_by_index(index) |
2326 | - value = detail_editor.get_values(field=object_name, index=index) |
2327 | - if (value): |
2328 | - values.append(value) |
2329 | - |
2330 | - return values |
2331 | - |
2332 | - |
2333 | -class ContactDetailWithTypeEditor(uitk.UbuntuUIToolkitEmulatorBase): |
2334 | - """Custom proxy object for the ContactDetailWithTypeEditor widget.""" |
2335 | - |
2336 | - def __init__(self, *args): |
2337 | - super(ContactDetailWithTypeEditor, self).__init__(*args) |
2338 | - self.main_view = self.get_root_instance().select_single(MainWindow) |
2339 | - |
2340 | - def fill(self, field, index, detail): |
2341 | - self._select_type(detail) |
2342 | - self._fill_value(field, index, detail) |
2343 | - |
2344 | - def _select_type(self, detail): |
2345 | - type_index = detail.TYPES.index(detail.type) |
2346 | - selected_type_index = self._get_selected_type_index() |
2347 | - if type_index != selected_type_index: |
2348 | - # TODO --elopio - 2014-03-01 |
2349 | - raise NotImplementedError('Type selection not yet implemented.') |
2350 | - |
2351 | - def _get_selected_type_index(self): |
2352 | - value_selector = self.select_single('ValueSelector') |
2353 | - return value_selector.currentIndex |
2354 | - |
2355 | - def _fill_value(self, field, index, detail): |
2356 | - single_values = { |
2357 | - 'phones': 'number', |
2358 | - 'emails': 'address', |
2359 | - 'ims': 'alias' |
2360 | - } |
2361 | - if field in single_values: |
2362 | - self._fill_single_field(getattr(detail, single_values[field])) |
2363 | - elif field == 'addresses': |
2364 | - self._fill_address(index, detail) |
2365 | - elif field == 'professionalDetails': |
2366 | - self._fill_professional_details(index, detail) |
2367 | - else: |
2368 | - raise AddressBookAppError('Unknown field: {}.'.format(field)) |
2369 | - |
2370 | - def _fill_single_field(self, value): |
2371 | - text_field = self.select_single(TextInputDetail) |
2372 | - self._make_field_visible_and_write(text_field, value) |
2373 | - |
2374 | - def _make_field_visible_and_write(self, text_field, value): |
2375 | - while not text_field.activeFocus: |
2376 | - # XXX We should just swipe the text field into view. |
2377 | - # Update this once bug http://pad.lv/1286479 is implemented. |
2378 | - # --elopio - 2014-03-01 |
2379 | - text_field.keyboard.press_and_release('Tab') |
2380 | - time.sleep(0.1) |
2381 | - self.main_view.get_contact_edit_page().wait_to_stop_moving() |
2382 | - |
2383 | - text_field.write(value) |
2384 | - |
2385 | - def _fill_address(self, index, address): |
2386 | - fields = collections.OrderedDict() |
2387 | - fields['street'] = address.street |
2388 | - fields['locality'] = address.locality |
2389 | - fields['region'] = address.region |
2390 | - fields['postal_code'] = address.postal_code |
2391 | - fields['country'] = address.country |
2392 | - for key, value in fields.items(): |
2393 | - text_field = _get_text_field(self, key, index) |
2394 | - self._make_field_visible_and_write(text_field, value) |
2395 | - |
2396 | - def _fill_professional_details(self, index, address): |
2397 | - # TODO --elopio - 2014-03-01 |
2398 | - raise NotImplementedError('Not yet implemented.') |
2399 | - |
2400 | - def get_values(self, field, index): |
2401 | - if field == 'phones': |
2402 | - return data.Phone( |
2403 | - type_=data.Phone.TYPES[self._get_selected_type_index()], |
2404 | - number=self._get_single_field_value()) |
2405 | - if field == 'emails': |
2406 | - return data.Email( |
2407 | - type_=data.Email.TYPES[self._get_selected_type_index()], |
2408 | - address=self._get_single_field_value()) |
2409 | - if field == 'ims': |
2410 | - return data.SocialAlias( |
2411 | - type_=data.SocialAlias.TYPES[self._get_selected_type_index()], |
2412 | - alias=self._get_single_field_value()) |
2413 | - if field == 'addresses': |
2414 | - return self._get_address_value(index) |
2415 | - if field == 'professionalDetails': |
2416 | - # TODO --elopio - 2014-03-01 |
2417 | - return None |
2418 | - raise AddressBookAppError('Unknown field: {}.'.format(field)) |
2419 | - |
2420 | - def _get_single_field_value(self): |
2421 | - return self.select_single(TextInputDetail).text |
2422 | - |
2423 | - def _get_address_value(self, index): |
2424 | - street = _get_text_field(self, 'street', index).text |
2425 | - locality = _get_text_field(self, 'locality', index).text |
2426 | - region = _get_text_field(self, 'region', index).text |
2427 | - postal_code = _get_text_field(self, 'postal_code', index).text |
2428 | - country = _get_text_field(self, 'country', index).text |
2429 | - return data.Address( |
2430 | - type_=data.Address.TYPES[self._get_selected_type_index()], |
2431 | - street=street, locality=locality, region=region, |
2432 | - postal_code=postal_code, country=country) |
2433 | |
2434 | === added directory 'tests/autopilot/address_book_app/pages' |
2435 | === added file 'tests/autopilot/address_book_app/pages/__init__.py' |
2436 | --- tests/autopilot/address_book_app/pages/__init__.py 1970-01-01 00:00:00 +0000 |
2437 | +++ tests/autopilot/address_book_app/pages/__init__.py 2014-06-06 17:32:09 +0000 |
2438 | @@ -0,0 +1,25 @@ |
2439 | +# -*- Mode: Python; coding: utf-8; indent-tabs-mode: nil; tab-width: 4 -*- |
2440 | +# |
2441 | +# Copyright (C) 2014 Canonical Ltd. |
2442 | +# |
2443 | +# This program is free software; you can redistribute it and/or modify |
2444 | +# it under the terms of the GNU General Public License version 3, as published |
2445 | +# by the Free Software Foundation. |
2446 | +# |
2447 | +# This program is distributed in the hope that it will be useful, |
2448 | +# but WITHOUT ANY WARRANTY; without even the implied warranty of |
2449 | +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
2450 | +# GNU General Public License for more details. |
2451 | +# |
2452 | +# You should have received a copy of the GNU General Public License |
2453 | +# along with this program. If not, see <http://www.gnu.org/licenses/>. |
2454 | + |
2455 | +__all__ = [ |
2456 | + 'ContactEditor', |
2457 | + 'ContactListPage', |
2458 | + 'ContactView', |
2459 | +] |
2460 | + |
2461 | +from address_book_app.pages._contact_editor import ContactEditor |
2462 | +from address_book_app.pages._contact_list_page import ContactListPage |
2463 | +from address_book_app.pages._contact_view import ContactView |
2464 | |
2465 | === added file 'tests/autopilot/address_book_app/pages/_common.py' |
2466 | --- tests/autopilot/address_book_app/pages/_common.py 1970-01-01 00:00:00 +0000 |
2467 | +++ tests/autopilot/address_book_app/pages/_common.py 2014-06-06 17:32:09 +0000 |
2468 | @@ -0,0 +1,53 @@ |
2469 | +# -*- Mode: Python; coding: utf-8; indent-tabs-mode: nil; tab-width: 4 -*- |
2470 | +# |
2471 | +# Copyright (C) 2014 Canonical Ltd. |
2472 | +# |
2473 | +# This program is free software; you can redistribute it and/or modify |
2474 | +# it under the terms of the GNU General Public License version 3, as published |
2475 | +# by the Free Software Foundation. |
2476 | +# |
2477 | +# This program is distributed in the hope that it will be useful, |
2478 | +# but WITHOUT ANY WARRANTY; without even the implied warranty of |
2479 | +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
2480 | +# GNU General Public License for more details. |
2481 | +# |
2482 | +# You should have received a copy of the GNU General Public License |
2483 | +# along with this program. If not, see <http://www.gnu.org/licenses/>. |
2484 | + |
2485 | +import logging |
2486 | + |
2487 | +import ubuntuuitoolkit |
2488 | +from autopilot.introspection import dbus |
2489 | + |
2490 | + |
2491 | +logger = logging.getLogger(__name__) |
2492 | + |
2493 | + |
2494 | +class PageWithHeader(ubuntuuitoolkit.UbuntuUIToolkitCustomProxyObjectBase): |
2495 | + |
2496 | + def get_header(self): |
2497 | + """Return the Header custom proxy object of the Page.""" |
2498 | + return self.get_root_instance().select_single( |
2499 | + 'Header', objectName='MainView_Header') |
2500 | + |
2501 | + |
2502 | +class PageWithBottomEdge(ubuntuuitoolkit.UbuntuUIToolkitCustomProxyObjectBase): |
2503 | + """An emulator class that makes it easy to interact with the bottom edge |
2504 | + swipe page""" |
2505 | + |
2506 | + def revel_bottom_edge_page(self): |
2507 | + """Bring the bottom edge page to the screen""" |
2508 | + self.bottomEdgePageLoaded.wait_for(True) |
2509 | + try: |
2510 | + action_item = self.wait_select_single( |
2511 | + 'QQuickItem', objectName='bottomEdgeTip') |
2512 | + start_x = (action_item.globalRect.x + |
2513 | + (action_item.globalRect.width * 0.5)) |
2514 | + start_y = action_item.globalRect.y + (action_item.height * 0.5) |
2515 | + stop_y = start_y - (self.height * 0.7) |
2516 | + self.pointing_device.drag( |
2517 | + start_x, start_y, start_x, stop_y, rate=2) |
2518 | + self.isReady.wait_for(True) |
2519 | + except dbus.StateNotFoundError: |
2520 | + logger.error('ButtomEdge element not found.') |
2521 | + raise |
2522 | |
2523 | === added file 'tests/autopilot/address_book_app/pages/_contact_editor.py' |
2524 | --- tests/autopilot/address_book_app/pages/_contact_editor.py 1970-01-01 00:00:00 +0000 |
2525 | +++ tests/autopilot/address_book_app/pages/_contact_editor.py 2014-06-06 17:32:09 +0000 |
2526 | @@ -0,0 +1,272 @@ |
2527 | +# -*- Mode: Python; coding: utf-8; indent-tabs-mode: nil; tab-width: 4 -*- |
2528 | +# |
2529 | +# Copyright (C) 2014 Canonical Ltd. |
2530 | +# |
2531 | +# This program is free software; you can redistribute it and/or modify |
2532 | +# it under the terms of the GNU General Public License version 3, as published |
2533 | +# by the Free Software Foundation. |
2534 | +# |
2535 | +# This program is distributed in the hope that it will be useful, |
2536 | +# but WITHOUT ANY WARRANTY; without even the implied warranty of |
2537 | +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
2538 | +# GNU General Public License for more details. |
2539 | +# |
2540 | +# You should have received a copy of the GNU General Public License |
2541 | +# along with this program. If not, see <http://www.gnu.org/licenses/>. |
2542 | + |
2543 | +import collections |
2544 | +import logging |
2545 | +import time |
2546 | + |
2547 | +import autopilot.logging |
2548 | +import ubuntuuitoolkit |
2549 | +from address_book_app import data, _errors |
2550 | +from address_book_app.pages import _common |
2551 | + |
2552 | + |
2553 | +logger = logging.getLogger(__name__) |
2554 | + |
2555 | + |
2556 | +_TEXT_FIELD_OBJECT_NAMES = { |
2557 | + 'first_name': 'firstName', |
2558 | + 'last_name': 'lastName', |
2559 | + 'street': 'streetAddress_{}', |
2560 | + 'locality': 'localityAddress_{}', |
2561 | + 'region': 'regionAddress_{}', |
2562 | + 'postal_code': 'postcodeAddress_{}', |
2563 | + 'country': 'countryAddress_{}' |
2564 | +} |
2565 | + |
2566 | + |
2567 | +def _get_text_field(parent, field, index=None): |
2568 | + if field not in _TEXT_FIELD_OBJECT_NAMES: |
2569 | + raise _errors.AddressBookAppError('Unknown field: {}.'.format(field)) |
2570 | + |
2571 | + object_name = _TEXT_FIELD_OBJECT_NAMES[field] |
2572 | + if index is not None: |
2573 | + object_name = object_name.format(index) |
2574 | + return parent.select_single(TextInputDetail, objectName=object_name) |
2575 | + |
2576 | + |
2577 | +class ContactEditor(_common.PageWithHeader): |
2578 | + """Custom proxy object for the Contact Editor.""" |
2579 | + |
2580 | + @autopilot.logging.log_action(logger.info) |
2581 | + def fill_form(self, contact_information): |
2582 | + """Fill the edit contact form. |
2583 | + |
2584 | + :param contact_information: Values of the contact to fill the form. |
2585 | + :type contact_information: data object with the attributes first_name, |
2586 | + last_name, phones, emails, social_aliases, addresses and |
2587 | + professional_details |
2588 | + |
2589 | + """ |
2590 | + if contact_information.first_name is not None: |
2591 | + self._fill_first_name(contact_information.first_name) |
2592 | + if contact_information.last_name is not None: |
2593 | + self._fill_last_name(contact_information.last_name) |
2594 | + |
2595 | + groups = collections.OrderedDict() |
2596 | + groups['phones'] = contact_information.phones |
2597 | + groups['emails'] = contact_information.emails |
2598 | + groups['ims'] = contact_information.social_aliases |
2599 | + groups['addresses'] = contact_information.addresses |
2600 | + groups['professionalDetails'] = ( |
2601 | + contact_information.professional_details) |
2602 | + |
2603 | + for key, information in groups.items(): |
2604 | + if information: |
2605 | + self._fill_detail_group( |
2606 | + object_name=key, details=information) |
2607 | + |
2608 | + def _fill_first_name(self, first_name): |
2609 | + text_field = _get_text_field(self, 'first_name') |
2610 | + text_field.write(first_name) |
2611 | + |
2612 | + def _fill_last_name(self, last_name): |
2613 | + text_field = _get_text_field(self, 'last_name') |
2614 | + text_field.write(last_name) |
2615 | + |
2616 | + def _fill_detail_group(self, object_name, details): |
2617 | + editor = self.select_single( |
2618 | + ContactDetailGroupWithTypeEditor, objectName=object_name) |
2619 | + editor.fill(details) |
2620 | + |
2621 | + def _get_form_values(self): |
2622 | + first_name = _get_text_field(self, 'first_name').text |
2623 | + last_name = _get_text_field(self, 'last_name').text |
2624 | + phones = self._get_values_from_detail_group(object_name='phones') |
2625 | + emails = self._get_values_from_detail_group(object_name='emails') |
2626 | + social_aliases = self._get_values_from_detail_group(object_name='ims') |
2627 | + addresses = self._get_values_from_detail_group(object_name='addresses') |
2628 | + professional_details = self._get_values_from_detail_group( |
2629 | + object_name='professionalDetails') |
2630 | + |
2631 | + return data.Contact( |
2632 | + first_name=first_name, last_name=last_name, phones=phones, |
2633 | + emails=emails, social_aliases=social_aliases, addresses=addresses, |
2634 | + professional_details=professional_details) |
2635 | + |
2636 | + def _get_values_from_detail_group(self, object_name): |
2637 | + editor = self.select_single( |
2638 | + ContactDetailGroupWithTypeEditor, objectName=object_name) |
2639 | + return editor.get_values(object_name) |
2640 | + |
2641 | + def wait_to_stop_moving(self): |
2642 | + flickable = self.select_single( |
2643 | + 'QQuickFlickable', objectName='scrollArea') |
2644 | + flickable.flicking.wait_for(False) |
2645 | + |
2646 | + |
2647 | +class TextInputDetail(ubuntuuitoolkit.TextField): |
2648 | + """Custom proxy object for the Text Input Detail field.""" |
2649 | + |
2650 | + |
2651 | +class ContactDetailGroupWithTypeEditor( |
2652 | + ubuntuuitoolkit.UbuntuUIToolkitCustomProxyObjectBase): |
2653 | + """Custom proxy object for the ContactDetailGroupWithTypeEditor widget.""" |
2654 | + |
2655 | + _DETAIL_EDITORS = { |
2656 | + 'phones': 'base_phoneNumber_{}', |
2657 | + 'emails': 'base_email_{}', |
2658 | + 'ims': 'base_onlineAccount_{}', |
2659 | + 'addresses': 'base_address_{}', |
2660 | + # FIXME fix the unknown. --elopio - 2014-03-01 |
2661 | + 'professionalDetails': 'base_unknown_{}' |
2662 | + } |
2663 | + |
2664 | + def fill(self, details): |
2665 | + """Fill a contact detail group.""" |
2666 | + for index, detail in enumerate(details[:-1]): |
2667 | + self._fill_detail(index, detail) |
2668 | + self._add_detail() |
2669 | + self._fill_detail(len(details) - 1, details[-1]) |
2670 | + |
2671 | + def _fill_detail(self, index, detail): |
2672 | + detail_editor = self._get_detail_editor_by_index(index) |
2673 | + detail_editor.fill(field=self.objectName, index=index, detail=detail) |
2674 | + |
2675 | + def _get_detail_editor_by_index(self, index): |
2676 | + object_name = self._get_contact_detail_editor_object_name(index) |
2677 | + return self.select_single( |
2678 | + ContactDetailWithTypeEditor, objectName=object_name) |
2679 | + |
2680 | + def _get_contact_detail_editor_object_name(self, index): |
2681 | + return self._DETAIL_EDITORS[self.objectName].format(index) |
2682 | + |
2683 | + def _add_detail(self): |
2684 | + # TODO --elopio - 2014-03-01 |
2685 | + raise NotImplementedError('Add extra details not yet implemented.') |
2686 | + |
2687 | + def get_values(self, object_name): |
2688 | + """Return the values of a contact detail group.""" |
2689 | + values = [] |
2690 | + for index in range(self.detailsCount): |
2691 | + detail_editor = self._get_detail_editor_by_index(index) |
2692 | + value = detail_editor.get_values(field=object_name, index=index) |
2693 | + if (value): |
2694 | + values.append(value) |
2695 | + |
2696 | + return values |
2697 | + |
2698 | + |
2699 | +class ContactDetailWithTypeEditor( |
2700 | + ubuntuuitoolkit.UbuntuUIToolkitCustomProxyObjectBase): |
2701 | + """Custom proxy object for the ContactDetailWithTypeEditor widget.""" |
2702 | + |
2703 | + def fill(self, field, index, detail): |
2704 | + self._select_type(detail) |
2705 | + self._fill_value(field, index, detail) |
2706 | + |
2707 | + def _select_type(self, detail): |
2708 | + type_index = detail.TYPES.index(detail.type) |
2709 | + selected_type_index = self._get_selected_type_index() |
2710 | + if type_index != selected_type_index: |
2711 | + # TODO --elopio - 2014-03-01 |
2712 | + raise NotImplementedError('Type selection not yet implemented.') |
2713 | + |
2714 | + def _get_selected_type_index(self): |
2715 | + value_selector = self.select_single('ValueSelector') |
2716 | + return value_selector.currentIndex |
2717 | + |
2718 | + def _fill_value(self, field, index, detail): |
2719 | + single_values = { |
2720 | + 'phones': 'number', |
2721 | + 'emails': 'address', |
2722 | + 'ims': 'alias' |
2723 | + } |
2724 | + if field in single_values: |
2725 | + self._fill_single_field(getattr(detail, single_values[field])) |
2726 | + elif field == 'addresses': |
2727 | + self._fill_address(index, detail) |
2728 | + elif field == 'professionalDetails': |
2729 | + self._fill_professional_details(index, detail) |
2730 | + else: |
2731 | + raise _errors.AddressBookAppError( |
2732 | + 'Unknown field: {}.'.format(field)) |
2733 | + |
2734 | + def _fill_single_field(self, value): |
2735 | + text_field = self.select_single(TextInputDetail) |
2736 | + self._make_field_visible_and_write(text_field, value) |
2737 | + |
2738 | + def _make_field_visible_and_write(self, text_field, value): |
2739 | + while not text_field.activeFocus: |
2740 | + # XXX We should just swipe the text field into view. |
2741 | + # Update this once bug http://pad.lv/1286479 is implemented. |
2742 | + # --elopio - 2014-03-01 |
2743 | + text_field.keyboard.press_and_release('Tab') |
2744 | + time.sleep(0.1) |
2745 | + contact_editor = self.get_root_instance().select_single( |
2746 | + ContactEditor, objectName='contactEditorPage', active=True) |
2747 | + contact_editor.wait_to_stop_moving() |
2748 | + |
2749 | + text_field.write(value) |
2750 | + |
2751 | + def _fill_address(self, index, address): |
2752 | + fields = collections.OrderedDict() |
2753 | + fields['street'] = address.street |
2754 | + fields['locality'] = address.locality |
2755 | + fields['region'] = address.region |
2756 | + fields['postal_code'] = address.postal_code |
2757 | + fields['country'] = address.country |
2758 | + for key, value in fields.items(): |
2759 | + text_field = _get_text_field(self, key, index) |
2760 | + self._make_field_visible_and_write(text_field, value) |
2761 | + |
2762 | + def _fill_professional_details(self, index, address): |
2763 | + # TODO --elopio - 2014-03-01 |
2764 | + raise NotImplementedError('Not yet implemented.') |
2765 | + |
2766 | + def get_values(self, field, index): |
2767 | + if field == 'phones': |
2768 | + return data.Phone( |
2769 | + type_=data.Phone.TYPES[self._get_selected_type_index()], |
2770 | + number=self._get_single_field_value()) |
2771 | + if field == 'emails': |
2772 | + return data.Email( |
2773 | + type_=data.Email.TYPES[self._get_selected_type_index()], |
2774 | + address=self._get_single_field_value()) |
2775 | + if field == 'ims': |
2776 | + return data.SocialAlias( |
2777 | + type_=data.SocialAlias.TYPES[self._get_selected_type_index()], |
2778 | + alias=self._get_single_field_value()) |
2779 | + if field == 'addresses': |
2780 | + return self._get_address_value(index) |
2781 | + if field == 'professionalDetails': |
2782 | + # TODO --elopio - 2014-03-01 |
2783 | + return None |
2784 | + raise _errors.AddressBookAppError('Unknown field: {}.'.format(field)) |
2785 | + |
2786 | + def _get_single_field_value(self): |
2787 | + return self.select_single(TextInputDetail).text |
2788 | + |
2789 | + def _get_address_value(self, index): |
2790 | + street = _get_text_field(self, 'street', index).text |
2791 | + locality = _get_text_field(self, 'locality', index).text |
2792 | + region = _get_text_field(self, 'region', index).text |
2793 | + postal_code = _get_text_field(self, 'postal_code', index).text |
2794 | + country = _get_text_field(self, 'country', index).text |
2795 | + return data.Address( |
2796 | + type_=data.Address.TYPES[self._get_selected_type_index()], |
2797 | + street=street, locality=locality, region=region, |
2798 | + postal_code=postal_code, country=country) |
2799 | |
2800 | === renamed file 'tests/autopilot/address_book_app/emulators/contact_list_page.py' => 'tests/autopilot/address_book_app/pages/_contact_list_page.py' |
2801 | --- tests/autopilot/address_book_app/emulators/contact_list_page.py 2014-02-13 16:44:12 +0000 |
2802 | +++ tests/autopilot/address_book_app/pages/_contact_list_page.py 2014-06-06 17:32:09 +0000 |
2803 | @@ -1,23 +1,33 @@ |
2804 | # -*- Mode: Python; coding: utf-8; indent-tabs-mode: nil; tab-width: 4 -*- |
2805 | +# |
2806 | +# Copyright (C) 2014 Canonical Ltd. |
2807 | +# |
2808 | +# This program is free software; you can redistribute it and/or modify |
2809 | +# it under the terms of the GNU General Public License version 3, as published |
2810 | +# by the Free Software Foundation. |
2811 | +# |
2812 | +# This program is distributed in the hope that it will be useful, |
2813 | +# but WITHOUT ANY WARRANTY; without even the implied warranty of |
2814 | +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
2815 | +# GNU General Public License for more details. |
2816 | +# |
2817 | +# You should have received a copy of the GNU General Public License |
2818 | +# along with this program. If not, see <http://www.gnu.org/licenses/>. |
2819 | |
2820 | """ ContactListPage emulator for Addressbook App tests """ |
2821 | |
2822 | -# Copyright 2014 Canonical |
2823 | -# |
2824 | -# This program is free software: you can redistribute it and/or modify it |
2825 | -# under the terms of the GNU General Public License version 3, as published |
2826 | -# by the Free Software Foundation. |
2827 | - |
2828 | import logging |
2829 | +import time |
2830 | |
2831 | from autopilot.introspection.dbus import StateNotFoundError |
2832 | -from ubuntuuitoolkit import emulators as uitk |
2833 | + |
2834 | +from address_book_app.pages import _common, _contact_view |
2835 | + |
2836 | |
2837 | LOGGER = logging.getLogger(__name__) |
2838 | -from time import sleep |
2839 | - |
2840 | - |
2841 | -class ContactListPage(uitk.UbuntuUIToolkitEmulatorBase): |
2842 | + |
2843 | + |
2844 | +class ContactListPage(_common.PageWithHeader, _common.PageWithBottomEdge): |
2845 | """ ContactListPage emulator class """ |
2846 | |
2847 | def __init__(self, *args): |
2848 | @@ -26,12 +36,28 @@ |
2849 | self.selected_marks = [] |
2850 | super(ContactListPage, self).__init__(*args) |
2851 | |
2852 | + def open_contact(self, index): |
2853 | + """Open the page with the contact information. |
2854 | + |
2855 | + :param index: The index of the contact to open. |
2856 | + :return: The page with the contact information. |
2857 | + |
2858 | + """ |
2859 | + contacts = self.get_contacts() |
2860 | + self.pointing_device.click_object(contacts[index]) |
2861 | + return self.get_root_instance().select_single( |
2862 | + _contact_view.ContactView, objectName='contactViewPage') |
2863 | + |
2864 | + def _get_list_view(self): |
2865 | + return self.wait_select_single("ContactListView", |
2866 | + objectName="contactListView") |
2867 | + |
2868 | def get_contacts(self): |
2869 | """ |
2870 | Returns a list of ContactDelegate objects and populate |
2871 | self.selection_marks |
2872 | """ |
2873 | - sleep(1) |
2874 | + time.sleep(1) |
2875 | self.contacts = self.select_many("ContactDelegate") |
2876 | self.selection_marks = [] |
2877 | for contact in self.contacts: |
2878 | @@ -41,17 +67,34 @@ |
2879 | self.selection_marks.append(mark) |
2880 | return self.contacts |
2881 | |
2882 | + def start_selection(self, idx): |
2883 | + view = self._get_list_view() |
2884 | + if not view.isInSelectionMode: |
2885 | + self.get_contacts() |
2886 | + self.selected_marks.append(self.selection_marks[idx]) |
2887 | + self.pointing_device.move_to_object(self.contacts[idx]) |
2888 | + self.pointing_device.press() |
2889 | + time.sleep(2.0) |
2890 | + self.pointing_device.release() |
2891 | + view.isInSelectionMode.wait_for(True) |
2892 | + else: |
2893 | + self.selected_marks.append(self.selection_marks[idx]) |
2894 | + self.pointing_device.click_object(self.selection_marks[idx]) |
2895 | + |
2896 | + |
2897 | def select_contacts_by_index(self, indices): |
2898 | """ Select contacts corresponding to the list of index in indices |
2899 | |
2900 | :param indices: List of integers |
2901 | """ |
2902 | self.deselect_all() |
2903 | + if len(indices) > 0: |
2904 | + self.start_selection(indices[0]) |
2905 | |
2906 | - # Select matching indices |
2907 | - for idx in indices: |
2908 | - self.selected_marks.append(self.selection_marks[idx]) |
2909 | - self.pointing_device.click_object(self.selection_marks[idx]) |
2910 | + # Select matching indices |
2911 | + for idx in indices[1:]: |
2912 | + self.selected_marks.append(self.selection_marks[idx]) |
2913 | + self.pointing_device.click_object(self.selection_marks[idx]) |
2914 | |
2915 | def deselect_all(self): |
2916 | """Deselect every contacts""" |
2917 | @@ -63,13 +106,17 @@ |
2918 | objectName="selectionMark") |
2919 | self.pointing_device.click_object(mark) |
2920 | |
2921 | - def click_button(self, objectname): |
2922 | + def click_button(self, parent, objectname): |
2923 | """Press a button that matches objectname |
2924 | |
2925 | :param objectname: Name of the object |
2926 | """ |
2927 | + if parent: |
2928 | + obj = parent |
2929 | + else: |
2930 | + obj = self |
2931 | try: |
2932 | - buttons = self.select_many("Button", |
2933 | + buttons = obj.select_many("Button", |
2934 | objectName=objectname) |
2935 | for button in buttons: |
2936 | if button.visible: |
2937 | @@ -80,11 +127,8 @@ |
2938 | ) |
2939 | raise |
2940 | |
2941 | - def cancel(self): |
2942 | - """Press the cancel button displayed when pick mode is enabled""" |
2943 | - self.click_button("DialogButtons.rejectButton") |
2944 | - |
2945 | - def delete(self): |
2946 | - """Press the delete button displayed when pick mode is enabled""" |
2947 | - self.click_button("DialogButtons.acceptButton") |
2948 | - self.get_contacts() |
2949 | + def delete(self, main_window): |
2950 | + main_window.done_selection() |
2951 | + dialog = main_window.wait_select_single("RemoveContactsDialog", |
2952 | + objectName="removeContactsDialog") |
2953 | + self.click_button(main_window, "removeContactsDialog.Yes") |
2954 | |
2955 | === added file 'tests/autopilot/address_book_app/pages/_contact_view.py' |
2956 | --- tests/autopilot/address_book_app/pages/_contact_view.py 1970-01-01 00:00:00 +0000 |
2957 | +++ tests/autopilot/address_book_app/pages/_contact_view.py 2014-06-06 17:32:09 +0000 |
2958 | @@ -0,0 +1,28 @@ |
2959 | +# -*- Mode: Python; coding: utf-8; indent-tabs-mode: nil; tab-width: 4 -*- |
2960 | +# |
2961 | +# Copyright (C) 2014 Canonical Ltd. |
2962 | +# |
2963 | +# This program is free software; you can redistribute it and/or modify |
2964 | +# it under the terms of the GNU General Public License version 3, as published |
2965 | +# by the Free Software Foundation. |
2966 | +# |
2967 | +# This program is distributed in the hope that it will be useful, |
2968 | +# but WITHOUT ANY WARRANTY; without even the implied warranty of |
2969 | +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
2970 | +# GNU General Public License for more details. |
2971 | +# |
2972 | +# You should have received a copy of the GNU General Public License |
2973 | +# along with this program. If not, see <http://www.gnu.org/licenses/>. |
2974 | + |
2975 | +from address_book_app.pages import _common, _contact_editor |
2976 | + |
2977 | + |
2978 | +class ContactView(_common.PageWithHeader): |
2979 | + """Autopilot helper for the ContactView page.""" |
2980 | + |
2981 | + def go_to_edit_contact(self): |
2982 | + self.get_header().click_action_button('edit') |
2983 | + return self.get_root_instance().select_single( |
2984 | + _contact_editor.ContactEditor, |
2985 | + objectName='contactEditorPage', |
2986 | + active=True) |
2987 | |
2988 | === modified file 'tests/autopilot/address_book_app/tests/__init__.py' |
2989 | --- tests/autopilot/address_book_app/tests/__init__.py 2014-04-29 21:05:10 +0000 |
2990 | +++ tests/autopilot/address_book_app/tests/__init__.py 2014-06-06 17:32:09 +0000 |
2991 | @@ -16,7 +16,8 @@ |
2992 | from autopilot.platform import model |
2993 | from testtools.matchers import Equals |
2994 | |
2995 | -from address_book_app.emulators.main_window import MainWindow |
2996 | +import address_book_app |
2997 | +from address_book_app import MainWindow |
2998 | from ubuntuuitoolkit import emulators as toolkit_emulators |
2999 | |
3000 | |
3001 | @@ -45,6 +46,7 @@ |
3002 | self.app_bin = AddressBookAppTestCase.DEFAULT_DEV_LOCATION |
3003 | |
3004 | os.environ['QTCONTACTS_MANAGER_OVERRIDE'] = 'memory' |
3005 | + os.environ['ADDRESS_BOOK_APP_ICON_THEME'] = 'ubuntu-mobile' |
3006 | vcard_data = "" |
3007 | if AddressBookAppTestCase.PRELOAD_VCARD: |
3008 | # Use vcard from source tree and fallback on installed vcard (from |
3009 | @@ -70,6 +72,7 @@ |
3010 | AddressBookAppTestCase.ARGS = [] |
3011 | AddressBookAppTestCase.PRELOAD_VCARD = False |
3012 | self.main_window.visible.wait_for(True) |
3013 | + self.app = address_book_app.AddressBookApp(self.app_proxy) |
3014 | |
3015 | def tearDown(self): |
3016 | super(AddressBookAppTestCase, self).tearDown() |
3017 | @@ -79,7 +82,7 @@ |
3018 | subprocess.check_call(["/sbin/initctl", "start", "maliit-server"]) |
3019 | |
3020 | def launch_test_local(self): |
3021 | - self.app = self.launch_test_application( |
3022 | + self.app_proxy = self.launch_test_application( |
3023 | self.app_bin, |
3024 | *AddressBookAppTestCase.ARGS, |
3025 | app_type='qt', |
3026 | @@ -88,19 +91,19 @@ |
3027 | def launch_test_installed(self): |
3028 | df = "/usr/share/applications/address-book-app.desktop" |
3029 | self.ARGS.append("--desktop_file_hint=" + df) |
3030 | - self.app = self.launch_test_application( |
3031 | + self.app_proxy = self.launch_test_application( |
3032 | "address-book-app", |
3033 | *AddressBookAppTestCase.ARGS, |
3034 | app_type='qt', |
3035 | emulator_base=toolkit_emulators.UbuntuUIToolkitEmulatorBase) |
3036 | |
3037 | def launch_click_installed(self): |
3038 | - self.app = self.launch_click_package( |
3039 | + self.app_proxy = self.launch_click_package( |
3040 | "com.ubuntu.address-book") |
3041 | |
3042 | @property |
3043 | def main_window(self): |
3044 | - return self.app.select_single(MainWindow) |
3045 | + return self.app_proxy.select_single(MainWindow) |
3046 | |
3047 | def select_a_value(self, field, value_selector, value): |
3048 | # Make sure the field has focus |
3049 | @@ -159,7 +162,7 @@ |
3050 | self.assertThat(view_page.visible, Eventually(Equals(True))) |
3051 | |
3052 | # Edit contact |
3053 | - self.main_window.open_toolbar().click_button("edit") |
3054 | + self.main_window.get_header().click_action_button("edit") |
3055 | self.assertThat(view_page.visible, Eventually(Equals(False))) |
3056 | |
3057 | edit_page = self.main_window.get_contact_edit_page() |
3058 | @@ -223,7 +226,7 @@ |
3059 | postcode_address=None, |
3060 | country_address=None): |
3061 | # execute add new contact |
3062 | - self.main_window.open_toolbar().click_button("Add") |
3063 | + self.main_window.go_to_add_contact() |
3064 | |
3065 | first_name_field = self.main_window.select_single( |
3066 | "TextInputDetail", |
3067 | @@ -291,12 +294,4 @@ |
3068 | objectName="countryAddress_0") |
3069 | self.type_on_field(country_0, country_address) |
3070 | |
3071 | - edit_page = self.main_window.get_contact_edit_page() |
3072 | - accept_button = edit_page.select_single( |
3073 | - "Button", |
3074 | - objectName="accept") |
3075 | - self.pointing_device.click_object(accept_button) |
3076 | - |
3077 | - # wait for contact list to be visible again |
3078 | - list_page = self.main_window.get_contact_list_page() |
3079 | - self.assertThat(list_page.visible, Eventually(Equals(True))) |
3080 | + self.main_window.save() |
3081 | |
3082 | === modified file 'tests/autopilot/address_book_app/tests/test_add_contact.py' |
3083 | --- tests/autopilot/address_book_app/tests/test_add_contact.py 2014-05-16 20:34:08 +0000 |
3084 | +++ tests/autopilot/address_book_app/tests/test_add_contact.py 2014-06-06 17:32:09 +0000 |
3085 | @@ -11,9 +11,9 @@ |
3086 | from autopilot.matchers import Eventually |
3087 | from autopilot.introspection import dbus |
3088 | |
3089 | +import address_book_app |
3090 | from address_book_app import data |
3091 | from address_book_app.tests import AddressBookAppTestCase |
3092 | -from address_book_app.emulators import main_window |
3093 | |
3094 | |
3095 | class TestAddContact(AddressBookAppTestCase): |
3096 | @@ -22,37 +22,39 @@ |
3097 | def test_go_to_add_contact(self): |
3098 | """Test to launch the add contact screen using emulator method""" |
3099 | self.assertRaises( |
3100 | - dbus.StateNotFoundError, self.main_window.get_contact_edit_page) |
3101 | - contact_editor = self.main_window.go_to_add_contact() |
3102 | + dbus.StateNotFoundError, |
3103 | + self.app.main_window.get_contact_edit_page) |
3104 | + contact_editor = self.app.main_window.go_to_add_contact() |
3105 | self.assertTrue(contact_editor.visible) |
3106 | - self.assertIsInstance(contact_editor, main_window.ContactEditor) |
3107 | + self.assertIsInstance( |
3108 | + contact_editor, address_book_app.pages.ContactEditor) |
3109 | |
3110 | def test_add_and_cancel_contact(self): |
3111 | - list_page = self.main_window.get_contact_list_page() |
3112 | + list_page = self.app.main_window.get_contact_list_page() |
3113 | |
3114 | # execute add new contact |
3115 | - contact_editor = self.main_window.go_to_add_contact() |
3116 | + contact_editor = self.app.main_window.go_to_add_contact() |
3117 | |
3118 | # Check if the contact list disapear and contact editor appears |
3119 | self.assertThat(list_page.visible, Eventually(Equals(False))) |
3120 | self.assertThat(contact_editor.visible, Eventually(Equals(True))) |
3121 | |
3122 | # cancel new contact without save |
3123 | - self.main_window.cancel() |
3124 | + self.app.main_window.cancel() |
3125 | |
3126 | # Check if the contact list is visible again |
3127 | self.assertThat(list_page.visible, Eventually(Equals(True))) |
3128 | |
3129 | # Check if the contact list still empty |
3130 | - list_view = self.main_window.get_contact_list_view() |
3131 | + list_view = self.app.main_window.get_contact_list_view() |
3132 | self.assertThat(list_view.count, Eventually(Equals(0))) |
3133 | |
3134 | def test_add_contact_without_name(self): |
3135 | # execute add new contact |
3136 | - contact_editor = self.main_window.go_to_add_contact() |
3137 | + contact_editor = self.app.main_window.go_to_add_contact() |
3138 | |
3139 | # Try to save a empty contact |
3140 | - acceptButton = self.main_window.get_button("accept") |
3141 | + acceptButton = self.app.main_window.get_button("save") |
3142 | |
3143 | # Save button must be disabled |
3144 | self.assertThat(acceptButton.enabled, Eventually(Equals(False))) |
3145 | @@ -82,59 +84,59 @@ |
3146 | self.pointing_device.click_object(acceptButton) |
3147 | |
3148 | # Check if the contact editor still visbile |
3149 | - list_page = self.main_window.get_contact_list_page() |
3150 | + list_page = self.app.main_window.get_contact_list_page() |
3151 | |
3152 | self.assertThat(list_page.visible, Eventually(Equals(False))) |
3153 | self.assertThat(contact_editor.visible, Eventually(Equals(True))) |
3154 | |
3155 | # Check if the contact list still empty |
3156 | - list_view = self.main_window.get_contact_list_view() |
3157 | + list_view = self.app.main_window.get_contact_list_view() |
3158 | self.assertThat(list_view.count, Eventually(Equals(0))) |
3159 | |
3160 | def test_add_contact_with_full_name(self): |
3161 | test_contact = data.Contact(first_name='Fulano', last_name='de Tal') |
3162 | |
3163 | # execute add new contact |
3164 | - contact_editor = self.main_window.go_to_add_contact() |
3165 | + contact_editor = self.app.main_window.go_to_add_contact() |
3166 | contact_editor.fill_form(test_contact) |
3167 | |
3168 | # Save contact |
3169 | - self.main_window.save() |
3170 | + self.app.main_window.save() |
3171 | |
3172 | # Check if the contact list is visible again |
3173 | - list_page = self.main_window.get_contact_list_page() |
3174 | + list_page = self.app.main_window.get_contact_list_page() |
3175 | self.assertThat(list_page.visible, Eventually(Equals(True))) |
3176 | |
3177 | # Check if contact was added |
3178 | - list_view = self.main_window.get_contact_list_view() |
3179 | + list_view = self.app.main_window.get_contact_list_view() |
3180 | self.assertThat(list_view.count, Eventually(Equals(1))) |
3181 | |
3182 | def test_add_contact_with_first_name(self): |
3183 | test_contact = data.Contact(first_name='Fulano') |
3184 | |
3185 | # execute add new contact |
3186 | - contact_editor = self.main_window.go_to_add_contact() |
3187 | + contact_editor = self.app.main_window.go_to_add_contact() |
3188 | contact_editor.fill_form(test_contact) |
3189 | |
3190 | # Save contact |
3191 | - self.main_window.save() |
3192 | + self.app.main_window.save() |
3193 | |
3194 | # Check if contact was added |
3195 | - list_view = self.main_window.get_contact_list_view() |
3196 | + list_view = self.app.main_window.get_contact_list_view() |
3197 | self.assertThat(list_view.count, Eventually(Equals(1))) |
3198 | |
3199 | def test_add_contact_with_last_name(self): |
3200 | test_contact = data.Contact(last_name='de Tal') |
3201 | |
3202 | # execute add new contact |
3203 | - contact_editor = self.main_window.go_to_add_contact() |
3204 | + contact_editor = self.app.main_window.go_to_add_contact() |
3205 | contact_editor.fill_form(test_contact) |
3206 | |
3207 | # Save contact |
3208 | - self.main_window.save() |
3209 | + self.app.main_window.save() |
3210 | |
3211 | # Check if contact was added |
3212 | - list_view = self.main_window.get_contact_list_view() |
3213 | + list_view = self.app.main_window.get_contact_list_view() |
3214 | self.assertThat(list_view.count, Eventually(Equals(1))) |
3215 | |
3216 | def test_add_contact_with_name_and_phone(self): |
3217 | @@ -143,14 +145,14 @@ |
3218 | phones=[data.Phone.make()]) |
3219 | |
3220 | # execute add new contact |
3221 | - contact_editor = self.main_window.go_to_add_contact() |
3222 | + contact_editor = self.app.main_window.go_to_add_contact() |
3223 | contact_editor.fill_form(test_contact) |
3224 | |
3225 | # Save contact |
3226 | - self.main_window.save() |
3227 | + self.app.main_window.save() |
3228 | |
3229 | # Check if contact was added |
3230 | - list_view = self.main_window.get_contact_list_view() |
3231 | + list_view = self.app.main_window.get_contact_list_view() |
3232 | self.assertThat(list_view.count, Eventually(Equals(1))) |
3233 | |
3234 | def test_add_full_contact(self): |
3235 | @@ -160,19 +162,19 @@ |
3236 | test_contact.professional_details = [] |
3237 | |
3238 | # execute add new contact |
3239 | - contact_editor = self.main_window.go_to_add_contact() |
3240 | + contact_editor = self.app.main_window.go_to_add_contact() |
3241 | contact_editor.fill_form(test_contact) |
3242 | |
3243 | # Save contact |
3244 | - self.main_window.save() |
3245 | + self.app.main_window.save() |
3246 | |
3247 | # Check if contact was added |
3248 | - list_view = self.main_window.get_contact_list_view() |
3249 | + list_view = self.app.main_window.get_contact_list_view() |
3250 | self.assertThat(list_view.count, Eventually(Equals(1))) |
3251 | |
3252 | def test_email_label_save(self): |
3253 | # execute add new contact |
3254 | - contact_editor = self.main_window.go_to_add_contact() |
3255 | + contact_editor = self.app.main_window.go_to_add_contact() |
3256 | |
3257 | # fill name |
3258 | contact_editor.fill_form( |
3259 | @@ -186,13 +188,13 @@ |
3260 | self.set_email_address(2, "other@email.com", 2) |
3261 | |
3262 | # Save contact |
3263 | - self.main_window.save() |
3264 | + self.app.main_window.save() |
3265 | |
3266 | - contacts = self.main_window.select_many("ContactDelegate") |
3267 | + contacts = self.app.main_window.select_many("ContactDelegate") |
3268 | self.pointing_device.click_object(contacts[0]) |
3269 | |
3270 | # check if contacts was saved with the correct labels |
3271 | - view_page = self.main_window.get_contact_view_page() |
3272 | + view_page = self.app.main_window.get_contact_view_page() |
3273 | self.assertThat(view_page.visible, Eventually(Equals(True))) |
3274 | |
3275 | # check if we have 3 emails""" |
3276 | @@ -222,7 +224,7 @@ |
3277 | |
3278 | def test_phone_label_save(self): |
3279 | # execute add new contact |
3280 | - contact_editor = self.main_window.go_to_add_contact() |
3281 | + contact_editor = self.app.main_window.go_to_add_contact() |
3282 | |
3283 | # fill name |
3284 | contact_editor.fill_form( |
3285 | @@ -240,13 +242,13 @@ |
3286 | self.set_phone_number(4, "(000) 000-0004", 4) |
3287 | |
3288 | # Save contact |
3289 | - self.main_window.save() |
3290 | + self.app.main_window.save() |
3291 | |
3292 | - contacts = self.main_window.select_many("ContactDelegate") |
3293 | + contacts = self.app.main_window.select_many("ContactDelegate") |
3294 | self.pointing_device.click_object(contacts[0]) |
3295 | |
3296 | # check if contacts was saved with the correct labels |
3297 | - view_page = self.main_window.get_contact_view_page() |
3298 | + view_page = self.app.main_window.get_contact_view_page() |
3299 | self.assertThat(view_page.visible, Eventually(Equals(True))) |
3300 | |
3301 | # check if we have five phones""" |
3302 | |
3303 | === modified file 'tests/autopilot/address_book_app/tests/test_contactlist.py' |
3304 | --- tests/autopilot/address_book_app/tests/test_contactlist.py 2014-03-12 20:35:16 +0000 |
3305 | +++ tests/autopilot/address_book_app/tests/test_contactlist.py 2014-06-06 17:32:09 +0000 |
3306 | @@ -17,6 +17,6 @@ |
3307 | """Tests the contact list features""" |
3308 | |
3309 | def test_contact_list(self): |
3310 | - contact_list = self.main_window.get_contact_list_page() |
3311 | + contact_list = self.app.main_window.get_contact_list_page() |
3312 | self.assertThat(contact_list.visible, Eventually(Equals(True))) |
3313 | pass |
3314 | |
3315 | === modified file 'tests/autopilot/address_book_app/tests/test_create_new_from_uri.py' |
3316 | --- tests/autopilot/address_book_app/tests/test_create_new_from_uri.py 2014-03-24 22:42:05 +0000 |
3317 | +++ tests/autopilot/address_book_app/tests/test_create_new_from_uri.py 2014-06-06 17:32:09 +0000 |
3318 | @@ -23,14 +23,17 @@ |
3319 | super(TestCreateNewContactFromURI, self).setUp() |
3320 | |
3321 | def test_save_new_contact(self): |
3322 | - edit_page = self.main_window.get_contact_edit_page() |
3323 | + list_page = self.app.main_window.get_contact_list_page() |
3324 | + list_page.isReady.wait_for(True) |
3325 | + |
3326 | + edit_page = self.app.main_window.get_contact_edit_page() |
3327 | self.assertThat(edit_page.visible, Eventually(Equals(True))) |
3328 | |
3329 | # add name to the contact |
3330 | - firstNameField = self.main_window.wait_select_single( |
3331 | + firstNameField = self.app.main_window.wait_select_single( |
3332 | "TextInputDetail", |
3333 | objectName="firstName") |
3334 | - lastNameField = self.main_window.wait_select_single( |
3335 | + lastNameField = self.app.main_window.wait_select_single( |
3336 | "TextInputDetail", |
3337 | objectName="lastName") |
3338 | |
3339 | @@ -38,15 +41,14 @@ |
3340 | self.type_on_field(lastNameField, "de Tal") |
3341 | |
3342 | # save the contact |
3343 | - acceptButton = self.main_window.get_button("accept") |
3344 | - self.pointing_device.click_object(acceptButton) |
3345 | + self.app.main_window.save() |
3346 | |
3347 | # open contact view |
3348 | - contacts = self.main_window.select_many("ContactDelegate") |
3349 | + contacts = self.app.main_window.select_many("ContactDelegate") |
3350 | self.pointing_device.click_object(contacts[0]) |
3351 | - view_page = self.main_window.get_contact_view_page() |
3352 | + view_page = self.app.main_window.get_contact_view_page() |
3353 | self.assertThat(view_page.visible, Eventually(Equals(True))) |
3354 | - |
3355 | + |
3356 | |
3357 | # check if we have the new phone""" |
3358 | phone_group = view_page.select_single( |
3359 | |
3360 | === renamed file 'tests/autopilot/address_book_app/tests/test_emulators.py' => 'tests/autopilot/address_book_app/tests/test_custom_proxy_objects.py' |
3361 | --- tests/autopilot/address_book_app/tests/test_emulators.py 2014-03-01 11:03:45 +0000 |
3362 | +++ tests/autopilot/address_book_app/tests/test_custom_proxy_objects.py 2014-06-06 17:32:09 +0000 |
3363 | @@ -17,8 +17,6 @@ |
3364 | # along with this program. If not, see <http://www.gnu.org/licenses/>. |
3365 | |
3366 | from address_book_app import data, tests |
3367 | -from address_book_app.emulators import main_window |
3368 | - |
3369 | |
3370 | class ContactEditorTestCase(tests.AddressBookAppTestCase): |
3371 | |
3372 | @@ -29,7 +27,7 @@ |
3373 | # --elopio - 2014-03-01 |
3374 | test_contact.professional_details = [] |
3375 | |
3376 | - contact_editor = self.main_window.go_to_add_contact() |
3377 | + contact_editor = self.app.main_window.go_to_add_contact() |
3378 | |
3379 | contact_editor.fill_form(test_contact) |
3380 | |
3381 | |
3382 | === modified file 'tests/autopilot/address_book_app/tests/test_delete_contact.py' |
3383 | --- tests/autopilot/address_book_app/tests/test_delete_contact.py 2014-03-13 01:40:39 +0000 |
3384 | +++ tests/autopilot/address_book_app/tests/test_delete_contact.py 2014-06-06 17:32:09 +0000 |
3385 | @@ -9,7 +9,6 @@ |
3386 | # by the Free Software Foundation. |
3387 | |
3388 | from testtools.matchers import Equals |
3389 | - |
3390 | from address_book_app.tests import AddressBookAppTestCase |
3391 | |
3392 | |
3393 | @@ -25,9 +24,6 @@ |
3394 | ("multiple_cancel", { |
3395 | "select": [1, 2], |
3396 | "action": "cancel"}), |
3397 | - ("none_delete", { |
3398 | - "select": [], |
3399 | - "action": "delete"}), |
3400 | ("single_delete", { |
3401 | "select": [1], |
3402 | "action": "delete"}), |
3403 | @@ -49,16 +45,15 @@ |
3404 | contact in the list before and after the action. |
3405 | Note that it doesn't check which contact has been deleted. |
3406 | """ |
3407 | - self.main_window.open_toolbar().click_select() |
3408 | - listpage = self.main_window.get_contact_list_page() |
3409 | + listpage = self.app.main_window.get_contact_list_page() |
3410 | contacts_before = listpage.get_contacts() |
3411 | |
3412 | listpage.select_contacts_by_index(self.select) |
3413 | deleted = [] |
3414 | if self.action == "cancel": |
3415 | - listpage.cancel() |
3416 | + self.app.main_window.cancel() |
3417 | elif self.action == "delete": |
3418 | - listpage.delete() |
3419 | + listpage.delete(self.app.main_window) |
3420 | deleted = self.select |
3421 | |
3422 | contacts_after = listpage.get_contacts() |
3423 | |
3424 | === modified file 'tests/autopilot/address_book_app/tests/test_edit_contact.py' |
3425 | --- tests/autopilot/address_book_app/tests/test_edit_contact.py 2014-05-16 20:34:08 +0000 |
3426 | +++ tests/autopilot/address_book_app/tests/test_edit_contact.py 2014-06-06 17:32:09 +0000 |
3427 | @@ -10,13 +10,28 @@ |
3428 | from testtools.matchers import Equals |
3429 | from autopilot.matchers import Eventually |
3430 | |
3431 | +from address_book_app import data |
3432 | from address_book_app.tests import AddressBookAppTestCase |
3433 | |
3434 | |
3435 | class TestEditContact(AddressBookAppTestCase): |
3436 | """Tests edit a contact""" |
3437 | + |
3438 | PHONE_NUMBERS = ['(333) 123-4567', '(333) 123-4568', '(222) 222-2222'] |
3439 | |
3440 | + def add_test_contact(self): |
3441 | + test_contact = data.Contact('test', 'test') |
3442 | + # TODO implement the filling of professional details. |
3443 | + # --elopio - 2014-03-01 |
3444 | + test_contact.professional_details = [] |
3445 | + |
3446 | + # execute add new contact |
3447 | + contact_editor = self.main_window.go_to_add_contact() |
3448 | + contact_editor.fill_form(test_contact) |
3449 | + |
3450 | + # Save contact |
3451 | + self.main_window.save() |
3452 | + |
3453 | def test_add_new_phone(self): |
3454 | self.add_contact("Fulano", "de Tal", [self.PHONE_NUMBERS[0]]) |
3455 | edit_page = self.edit_contact(0) |
3456 | @@ -28,15 +43,15 @@ |
3457 | self.create_new_detail(phoneGroup) |
3458 | |
3459 | # fill phone number |
3460 | - phone_number_1 = self.main_window.select_single( |
3461 | + phone_number_1 = self.app.main_window.select_single( |
3462 | "TextInputDetail", |
3463 | objectName="phoneNumber_1") |
3464 | self.type_on_field(phone_number_1, self.PHONE_NUMBERS[1]) |
3465 | |
3466 | - self.main_window.save() |
3467 | + self.app.main_window.save() |
3468 | |
3469 | # go back to view page |
3470 | - view_page = self.main_window.get_contact_view_page() |
3471 | + view_page = self.app.main_window.get_contact_view_page() |
3472 | self.assertThat(view_page.visible, Eventually(Equals(True))) |
3473 | |
3474 | # check if we have two phones""" |
3475 | @@ -56,16 +71,16 @@ |
3476 | edit_page = self.edit_contact(0) |
3477 | |
3478 | # clear phone 1 |
3479 | - phone_number_1 = self.main_window.select_single( |
3480 | + phone_number_1 = edit_page.select_single( |
3481 | "TextInputDetail", |
3482 | objectName="phoneNumber_1") |
3483 | self.clear_text_on_field(phone_number_1) |
3484 | |
3485 | # Save contact |
3486 | - self.main_window.save() |
3487 | + self.app.main_window.save() |
3488 | |
3489 | # check if we have onlye one phone |
3490 | - view_page = self.main_window.get_contact_view_page() |
3491 | + view_page = self.app.main_window.get_contact_view_page() |
3492 | phone_group = view_page.select_single( |
3493 | "ContactDetailGroupWithTypeView", |
3494 | objectName="phones") |
3495 | @@ -87,15 +102,15 @@ |
3496 | self.create_new_detail(emailGroup) |
3497 | |
3498 | # fill email address |
3499 | - email_field = self.main_window.select_single( |
3500 | + email_field = edit_page.select_single( |
3501 | "TextInputDetail", |
3502 | objectName="emailAddress_0") |
3503 | self.type_on_field(email_field, "fulano@internet.com.br") |
3504 | |
3505 | - self.main_window.save() |
3506 | + self.app.main_window.save() |
3507 | |
3508 | # go back to view page |
3509 | - view_page = self.main_window.get_contact_view_page() |
3510 | + view_page = self.app.main_window.get_contact_view_page() |
3511 | self.assertThat(view_page.visible, Eventually(Equals(True))) |
3512 | |
3513 | # check if we have a new email |
3514 | @@ -113,19 +128,19 @@ |
3515 | |
3516 | def test_remove_email(self): |
3517 | self.add_contact("Fulano", "de Tal", None, ["fulano@email.com"]) |
3518 | - self.edit_contact(0) |
3519 | + edit_page = self.edit_contact(0) |
3520 | |
3521 | # clear email |
3522 | - email_address_0 = self.main_window.select_single( |
3523 | + email_address_0 = edit_page.select_single( |
3524 | "TextInputDetail", |
3525 | objectName="emailAddress_0") |
3526 | self.clear_text_on_field(email_address_0) |
3527 | |
3528 | # Save contact |
3529 | - self.main_window.save() |
3530 | + self.app.main_window.save() |
3531 | |
3532 | # check if the email list is empty |
3533 | - view_page = self.main_window.get_contact_view_page() |
3534 | + view_page = self.app.main_window.get_contact_view_page() |
3535 | emails_group = view_page.select_single( |
3536 | "ContactDetailGroupWithTypeView", |
3537 | objectName="emails") |
3538 | @@ -133,12 +148,12 @@ |
3539 | |
3540 | def test_clear_names(self): |
3541 | self.add_contact("Fulano", "de Tal") |
3542 | - self.edit_contact(0) |
3543 | + edit_page = self.edit_contact(0) |
3544 | |
3545 | - first_name_field = self.main_window.select_single( |
3546 | + first_name_field = edit_page.select_single( |
3547 | "TextInputDetail", |
3548 | objectName="firstName") |
3549 | - last_name_field = self.main_window.select_single( |
3550 | + last_name_field = edit_page.select_single( |
3551 | "TextInputDetail", |
3552 | objectName="lastName") |
3553 | |
3554 | @@ -147,14 +162,15 @@ |
3555 | self.clear_text_on_field(last_name_field) |
3556 | |
3557 | # check if is possible to save a contact without name |
3558 | - accept_button = self.main_window.get_button("accept") |
3559 | + self.app.main_window.save() |
3560 | + accept_button = self.app.main_window.get_button("save") |
3561 | self.assertThat(accept_button.enabled, Eventually(Equals(False))) |
3562 | |
3563 | # Cancel edit |
3564 | - self.main_window.cancel() |
3565 | + self.app.main_window.cancel() |
3566 | |
3567 | # Check if the names still there |
3568 | - view_page = self.main_window.get_contact_view_page() |
3569 | + view_page = self.app.main_window.get_contact_view_page() |
3570 | self.assertThat(view_page.title, Eventually(Equals("Fulano de Tal"))) |
3571 | |
3572 | def test_im_type(self): |
3573 | @@ -162,13 +178,13 @@ |
3574 | edit_page = self.edit_contact(0) |
3575 | |
3576 | # Change Im type |
3577 | - im_value_selector = self.main_window.select_single( |
3578 | + im_value_selector = edit_page.select_single( |
3579 | "ValueSelector", |
3580 | objectName="type_onlineAccount_0") |
3581 | self.pointing_device.click_object(im_value_selector) |
3582 | self.assertThat(im_value_selector.expanded, Eventually(Equals(True))) |
3583 | |
3584 | - im_address_0 = self.main_window.select_single( |
3585 | + im_address_0 = edit_page.select_single( |
3586 | "TextInputDetail", |
3587 | objectName="imUri_0") |
3588 | |
3589 | @@ -176,9 +192,9 @@ |
3590 | self.select_a_value(im_address_0, im_value_selector, 0) |
3591 | |
3592 | # save contact |
3593 | - self.main_window.save() |
3594 | + self.app.main_window.save() |
3595 | |
3596 | - view_page = self.main_window.get_contact_view_page() |
3597 | + view_page = self.app.main_window.get_contact_view_page() |
3598 | |
3599 | # check if the type was saved correct |
3600 | im_type = view_page.select_single( |
3601 | |
3602 | === modified file 'tests/autopilot/address_book_app/tests/test_multiple_pick_mode.py' |
3603 | --- tests/autopilot/address_book_app/tests/test_multiple_pick_mode.py 2014-03-12 20:35:16 +0000 |
3604 | +++ tests/autopilot/address_book_app/tests/test_multiple_pick_mode.py 2014-06-06 17:32:09 +0000 |
3605 | @@ -22,7 +22,7 @@ |
3606 | super(TestMultiplePickerMode, self).setUp() |
3607 | |
3608 | def test_select_contacts(self): |
3609 | - pick_page = self.main_window.get_contact_list_pick_page() |
3610 | + pick_page = self.app.main_window.get_contact_list_pick_page() |
3611 | contacts = pick_page.select_many("ContactDelegate") |
3612 | # all selection marks should be visible |
3613 | selection_marks = [] |
3614 | |
3615 | === modified file 'tests/autopilot/address_book_app/tests/test_single_pick_mode.py' |
3616 | --- tests/autopilot/address_book_app/tests/test_single_pick_mode.py 2014-03-12 20:35:16 +0000 |
3617 | +++ tests/autopilot/address_book_app/tests/test_single_pick_mode.py 2014-06-06 17:32:09 +0000 |
3618 | @@ -22,7 +22,7 @@ |
3619 | super(TestSinglePickerMode, self).setUp() |
3620 | |
3621 | def test_select_single_contact(self): |
3622 | - pick_page = self.main_window.get_contact_list_pick_page() |
3623 | + pick_page = self.app.main_window.get_contact_list_pick_page() |
3624 | contacts = pick_page.select_many("ContactDelegate") |
3625 | # all selection marks should be visible |
3626 | selection_marks = [] |
3627 | |
3628 | === renamed file 'tests/autopilot/address_book_app/emulators/toolbar.py' => 'tests/autopilot/address_book_app/toolbar.py' |
PASSED: Continuous integration, rev:180 jenkins. qa.ubuntu. com/job/ address- book-app- ci/586/ jenkins. qa.ubuntu. com/job/ address- book-app- utopic- amd64-ci/ 39 jenkins. qa.ubuntu. com/job/ address- book-app- utopic- armhf-ci/ 39 jenkins. qa.ubuntu. com/job/ address- book-app- utopic- armhf-ci/ 39/artifact/ work/output/ *zip*/output. zip jenkins. qa.ubuntu. com/job/ address- book-app- utopic- i386-ci/ 39 jenkins. qa.ubuntu. com/job/ generic- deb-autopilot- utopic- touch/735 jenkins. qa.ubuntu. com/job/ generic- mediumtests- utopic/ 679 jenkins. qa.ubuntu. com/job/ generic- deb-autopilot- runner- mako/1161 jenkins. qa.ubuntu. com/job/ generic- mediumtests- builder- utopic- armhf/1379 jenkins. qa.ubuntu. com/job/ generic- mediumtests- builder- utopic- armhf/1379/ artifact/ work/output/ *zip*/output. zip s-jenkins. ubuntu- ci:8080/ job/touch- flash-device/ 8191 jenkins. qa.ubuntu. com/job/ autopilot- testrunner- otto-utopic/ 606 jenkins. qa.ubuntu. com/job/ generic- mediumtests- builder- utopic- amd64/818 jenkins. qa.ubuntu. com/job/ generic- mediumtests- builder- utopic- amd64/818/ artifact/ work/output/ *zip*/output. zip
http://
Executed test runs:
SUCCESS: http://
SUCCESS: http://
deb: http://
SUCCESS: http://
SUCCESS: http://
SUCCESS: http://
SUCCESS: http://
SUCCESS: http://
deb: http://
SUCCESS: http://
SUCCESS: http://
SUCCESS: http://
deb: http://
Click here to trigger a rebuild: s-jenkins. ubuntu- ci:8080/ job/address- book-app- ci/586/ rebuild
http://