Merge lp:~elopio/address-book-app/workaround1327325-warning_to_file into lp:address-book-app

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
Reviewer Review Type Date Requested Status
PS Jenkins bot continuous-integration Approve
Ubuntu Phablet Team Pending
Review via email: mp+222377@code.launchpad.net

This proposal has been superseded by a proposal from 2014-06-06.

Commit message

Log warnings to a file temporary, because of bug 1327325.

To post a comment you must log in.
Revision history for this message
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'

Subscribers

People subscribed via source and target branches