Merge lp:~renatofilho/address-book-app/save-state into lp:address-book-app

Proposed by Bill Filler
Status: Work in progress
Proposed branch: lp:~renatofilho/address-book-app/save-state
Merge into: lp:address-book-app
Diff against target: 962 lines (+457/-54)
20 files modified
debian/control (+1/-0)
src/app/addressbookapp.cpp (+12/-0)
src/app/addressbookapp.h (+1/-0)
src/imports/Common/CMakeLists.txt (+1/-0)
src/imports/Common/ContactPage.qml (+34/-0)
src/imports/ContactEdit/CMakeLists.txt (+1/-1)
src/imports/ContactEdit/ContactDetailGroupWithTypeEditor.qml (+21/-6)
src/imports/ContactEdit/ContactDetailNameEditor.qml (+4/-7)
src/imports/ContactEdit/ContactDetailWithTypeEditor.qml (+7/-16)
src/imports/ContactEdit/ContactEditPage.qml (+91/-5)
src/imports/ContactEdit/TextInputDetail.qml (+9/-0)
src/imports/ContactEdit/ValueSelector.qml (+7/-1)
src/imports/ContactList/ContactListPage.qml (+34/-6)
src/imports/ContactView/CMakeLists.txt (+1/-1)
src/imports/ContactView/ContactViewPage.qml (+22/-5)
src/imports/MainWindow.qml (+3/-1)
src/imports/Ubuntu/Contacts/ContactFetch.qml (+2/-0)
tests/autopilot/address_book_app/emulators/main_window.py (+2/-2)
tests/autopilot/address_book_app/tests/__init__.py (+43/-3)
tests/autopilot/address_book_app/tests/test_save_state.py (+161/-0)
To merge this branch: bzr merge lp:~renatofilho/address-book-app/save-state
Reviewer Review Type Date Requested Status
PS Jenkins bot continuous-integration Needs Fixing
Bill Filler Pending
Review via email: mp+206083@code.launchpad.net

This proposal supersedes a proposal from 2014-01-09.

Commit message

save state

To post a comment you must log in.
Revision history for this message
PS Jenkins bot (ps-jenkins) wrote : Posted in a previous version of this proposal

FAILED: Continuous integration, rev:118
http://jenkins.qa.ubuntu.com/job/address-book-app-ci/339/
Executed test runs:
    SUCCESS: http://jenkins.qa.ubuntu.com/job/address-book-app-trusty-amd64-ci/49
    SUCCESS: http://jenkins.qa.ubuntu.com/job/address-book-app-trusty-armhf-ci/49
        deb: http://jenkins.qa.ubuntu.com/job/address-book-app-trusty-armhf-ci/49/artifact/work/output/*zip*/output.zip
    SUCCESS: http://jenkins.qa.ubuntu.com/job/address-book-app-trusty-i386-ci/49
    SUCCESS: http://jenkins.qa.ubuntu.com/job/generic-mediumtests-trusty/2189
    FAILURE: http://jenkins.qa.ubuntu.com/job/generic-mediumtests-trusty-touch/2081/console
    SUCCESS: http://jenkins.qa.ubuntu.com/job/autopilot-testrunner-otto-trusty/1912
    SUCCESS: http://jenkins.qa.ubuntu.com/job/generic-mediumtests-builder-trusty-amd64/2191
        deb: http://jenkins.qa.ubuntu.com/job/generic-mediumtests-builder-trusty-amd64/2191/artifact/work/output/*zip*/output.zip
    SUCCESS: http://jenkins.qa.ubuntu.com/job/generic-mediumtests-builder-trusty-armhf/2081
        deb: http://jenkins.qa.ubuntu.com/job/generic-mediumtests-builder-trusty-armhf/2081/artifact/work/output/*zip*/output.zip
    FAILURE: http://jenkins.qa.ubuntu.com/job/generic-mediumtests-runner-mako/4544/console
    SUCCESS: http://s-jenkins.ubuntu-ci:8080/job/touch-flash-device/2936

Click here to trigger a rebuild:
http://s-jenkins.ubuntu-ci:8080/job/address-book-app-ci/339/rebuild

review: Needs Fixing (continuous-integration)
Revision history for this message
PS Jenkins bot (ps-jenkins) wrote : Posted in a previous version of this proposal

FAILED: Continuous integration, rev:119
http://jenkins.qa.ubuntu.com/job/address-book-app-ci/349/
Executed test runs:
    SUCCESS: http://jenkins.qa.ubuntu.com/job/address-book-app-trusty-amd64-ci/59
    SUCCESS: http://jenkins.qa.ubuntu.com/job/address-book-app-trusty-armhf-ci/59
        deb: http://jenkins.qa.ubuntu.com/job/address-book-app-trusty-armhf-ci/59/artifact/work/output/*zip*/output.zip
    SUCCESS: http://jenkins.qa.ubuntu.com/job/address-book-app-trusty-i386-ci/59
    SUCCESS: http://jenkins.qa.ubuntu.com/job/generic-mediumtests-trusty/2289
    FAILURE: http://jenkins.qa.ubuntu.com/job/generic-mediumtests-trusty-touch/2170/console
    SUCCESS: http://jenkins.qa.ubuntu.com/job/autopilot-testrunner-otto-trusty/2008
    SUCCESS: http://jenkins.qa.ubuntu.com/job/generic-mediumtests-builder-trusty-amd64/2291
        deb: http://jenkins.qa.ubuntu.com/job/generic-mediumtests-builder-trusty-amd64/2291/artifact/work/output/*zip*/output.zip
    SUCCESS: http://jenkins.qa.ubuntu.com/job/generic-mediumtests-builder-trusty-armhf/2170
        deb: http://jenkins.qa.ubuntu.com/job/generic-mediumtests-builder-trusty-armhf/2170/artifact/work/output/*zip*/output.zip
    FAILURE: http://jenkins.qa.ubuntu.com/job/generic-mediumtests-runner-mako/4628/console
    SUCCESS: http://s-jenkins.ubuntu-ci:8080/job/touch-flash-device/3037

Click here to trigger a rebuild:
http://s-jenkins.ubuntu-ci:8080/job/address-book-app-ci/349/rebuild

review: Needs Fixing (continuous-integration)
Revision history for this message
PS Jenkins bot (ps-jenkins) wrote : Posted in a previous version of this proposal

FAILED: Continuous integration, rev:119
http://jenkins.qa.ubuntu.com/job/address-book-app-ci/350/
Executed test runs:
    SUCCESS: http://jenkins.qa.ubuntu.com/job/address-book-app-trusty-amd64-ci/60
    SUCCESS: http://jenkins.qa.ubuntu.com/job/address-book-app-trusty-armhf-ci/60
        deb: http://jenkins.qa.ubuntu.com/job/address-book-app-trusty-armhf-ci/60/artifact/work/output/*zip*/output.zip
    SUCCESS: http://jenkins.qa.ubuntu.com/job/address-book-app-trusty-i386-ci/60
    SUCCESS: http://jenkins.qa.ubuntu.com/job/generic-mediumtests-trusty/2292
    FAILURE: http://jenkins.qa.ubuntu.com/job/generic-mediumtests-trusty-touch/2173/console
    SUCCESS: http://jenkins.qa.ubuntu.com/job/autopilot-testrunner-otto-trusty/2011
    SUCCESS: http://jenkins.qa.ubuntu.com/job/generic-mediumtests-builder-trusty-amd64/2294
        deb: http://jenkins.qa.ubuntu.com/job/generic-mediumtests-builder-trusty-amd64/2294/artifact/work/output/*zip*/output.zip
    SUCCESS: http://jenkins.qa.ubuntu.com/job/generic-mediumtests-builder-trusty-armhf/2173
        deb: http://jenkins.qa.ubuntu.com/job/generic-mediumtests-builder-trusty-armhf/2173/artifact/work/output/*zip*/output.zip
    FAILURE: http://jenkins.qa.ubuntu.com/job/generic-mediumtests-runner-mako/4631/console
    SUCCESS: http://s-jenkins.ubuntu-ci:8080/job/touch-flash-device/3040

Click here to trigger a rebuild:
http://s-jenkins.ubuntu-ci:8080/job/address-book-app-ci/350/rebuild

review: Needs Fixing (continuous-integration)
Revision history for this message
PS Jenkins bot (ps-jenkins) wrote : Posted in a previous version of this proposal

FAILED: Continuous integration, rev:120
http://jenkins.qa.ubuntu.com/job/address-book-app-ci/351/
Executed test runs:
    SUCCESS: http://jenkins.qa.ubuntu.com/job/address-book-app-trusty-amd64-ci/61
    SUCCESS: http://jenkins.qa.ubuntu.com/job/address-book-app-trusty-armhf-ci/61
        deb: http://jenkins.qa.ubuntu.com/job/address-book-app-trusty-armhf-ci/61/artifact/work/output/*zip*/output.zip
    SUCCESS: http://jenkins.qa.ubuntu.com/job/address-book-app-trusty-i386-ci/61
    SUCCESS: http://jenkins.qa.ubuntu.com/job/generic-mediumtests-trusty/2295
    FAILURE: http://jenkins.qa.ubuntu.com/job/generic-mediumtests-trusty-touch/2176/console
    SUCCESS: http://jenkins.qa.ubuntu.com/job/autopilot-testrunner-otto-trusty/2014
    SUCCESS: http://jenkins.qa.ubuntu.com/job/generic-mediumtests-builder-trusty-amd64/2297
        deb: http://jenkins.qa.ubuntu.com/job/generic-mediumtests-builder-trusty-amd64/2297/artifact/work/output/*zip*/output.zip
    SUCCESS: http://jenkins.qa.ubuntu.com/job/generic-mediumtests-builder-trusty-armhf/2176
        deb: http://jenkins.qa.ubuntu.com/job/generic-mediumtests-builder-trusty-armhf/2176/artifact/work/output/*zip*/output.zip
    FAILURE: http://jenkins.qa.ubuntu.com/job/generic-mediumtests-runner-mako/4634/console
    SUCCESS: http://s-jenkins.ubuntu-ci:8080/job/touch-flash-device/3043

Click here to trigger a rebuild:
http://s-jenkins.ubuntu-ci:8080/job/address-book-app-ci/351/rebuild

review: Needs Fixing (continuous-integration)
Revision history for this message
Bill Filler (bfiller) wrote : Posted in a previous version of this proposal

Did some testing, found some issues:

1) Add new contact and then quit app before filling in any fields. When restore app, the focused is given to the Home address field and the view is scrolled down to there. Focus should either be at the first name field at the top or should be with whatever field last had focus

2) If I edit a contact, and put cursor in email field then quit the app I get the main list when restore, not the edited contact. Seems this only happens when a contact doesn't have any phone numbers.

3) edit a contact and restore, focus always put in the phone number field even if that's not what last had focus. If no phone number the contact is not displayed.

4) scroll position in the main list is not maintained after restore. is this a known bug in state saver? if so, what is bug number.

review: Needs Fixing
Revision history for this message
PS Jenkins bot (ps-jenkins) wrote : Posted in a previous version of this proposal

FAILED: Continuous integration, rev:122
http://jenkins.qa.ubuntu.com/job/address-book-app-ci/352/
Executed test runs:
    SUCCESS: http://jenkins.qa.ubuntu.com/job/address-book-app-trusty-amd64-ci/62
    SUCCESS: http://jenkins.qa.ubuntu.com/job/address-book-app-trusty-armhf-ci/62
        deb: http://jenkins.qa.ubuntu.com/job/address-book-app-trusty-armhf-ci/62/artifact/work/output/*zip*/output.zip
    SUCCESS: http://jenkins.qa.ubuntu.com/job/address-book-app-trusty-i386-ci/62
    SUCCESS: http://jenkins.qa.ubuntu.com/job/generic-mediumtests-trusty/2310
    FAILURE: http://jenkins.qa.ubuntu.com/job/generic-mediumtests-trusty-touch/2191/console
    SUCCESS: http://jenkins.qa.ubuntu.com/job/autopilot-testrunner-otto-trusty/2029
    SUCCESS: http://jenkins.qa.ubuntu.com/job/generic-mediumtests-builder-trusty-amd64/2312
        deb: http://jenkins.qa.ubuntu.com/job/generic-mediumtests-builder-trusty-amd64/2312/artifact/work/output/*zip*/output.zip
    SUCCESS: http://jenkins.qa.ubuntu.com/job/generic-mediumtests-builder-trusty-armhf/2191
        deb: http://jenkins.qa.ubuntu.com/job/generic-mediumtests-builder-trusty-armhf/2191/artifact/work/output/*zip*/output.zip
    FAILURE: http://jenkins.qa.ubuntu.com/job/generic-mediumtests-runner-mako/4649/console
    SUCCESS: http://s-jenkins.ubuntu-ci:8080/job/touch-flash-device/3061

Click here to trigger a rebuild:
http://s-jenkins.ubuntu-ci:8080/job/address-book-app-ci/352/rebuild

review: Needs Fixing (continuous-integration)
Revision history for this message
PS Jenkins bot (ps-jenkins) wrote :

FAILED: Continuous integration, rev:122
http://jenkins.qa.ubuntu.com/job/address-book-app-ci/397/
Executed test runs:
    SUCCESS: http://jenkins.qa.ubuntu.com/job/address-book-app-trusty-amd64-ci/107
    SUCCESS: http://jenkins.qa.ubuntu.com/job/address-book-app-trusty-armhf-ci/107
        deb: http://jenkins.qa.ubuntu.com/job/address-book-app-trusty-armhf-ci/107/artifact/work/output/*zip*/output.zip
    SUCCESS: http://jenkins.qa.ubuntu.com/job/address-book-app-trusty-i386-ci/107
    UNSTABLE: http://jenkins.qa.ubuntu.com/job/generic-mediumtests-trusty/3188
    FAILURE: http://jenkins.qa.ubuntu.com/job/generic-mediumtests-trusty-touch/2869/console
    UNSTABLE: http://jenkins.qa.ubuntu.com/job/autopilot-testrunner-otto-trusty/2804
    SUCCESS: http://jenkins.qa.ubuntu.com/job/generic-mediumtests-builder-trusty-amd64/3190
        deb: http://jenkins.qa.ubuntu.com/job/generic-mediumtests-builder-trusty-amd64/3190/artifact/work/output/*zip*/output.zip
    SUCCESS: http://jenkins.qa.ubuntu.com/job/generic-mediumtests-builder-trusty-armhf/2871
        deb: http://jenkins.qa.ubuntu.com/job/generic-mediumtests-builder-trusty-armhf/2871/artifact/work/output/*zip*/output.zip
    FAILURE: http://jenkins.qa.ubuntu.com/job/generic-mediumtests-runner-mako/5279/console
    SUCCESS: http://s-jenkins.ubuntu-ci:8080/job/touch-flash-device/3898

Click here to trigger a rebuild:
http://s-jenkins.ubuntu-ci:8080/job/address-book-app-ci/397/rebuild

review: Needs Fixing (continuous-integration)

Unmerged revisions

122. By Renato Araujo Oliveira Filho

Removed debug messages.

121. By Renato Araujo Oliveira Filho

Move focust to name field after restore the state on edit mode.

120. By Renato Araujo Oliveira Filho

Removed debug messages.

119. By Renato Araujo Oliveira Filho

Fixed name detail save.

118. By Renato Araujo Oliveira Filho

Created test to save state on create contact state.

117. By Renato Araujo Oliveira Filho

Fixed autopilot test with the new component type.

116. By Renato Araujo Oliveira Filho

Save state implementation.

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1=== modified file 'debian/control'
2--- debian/control 2013-12-11 15:03:32 +0000
3+++ debian/control 2014-02-13 02:55:34 +0000
4@@ -69,5 +69,6 @@
5 libqt5widgets5,
6 ubuntu-ui-toolkit-autopilot,
7 address-book-app (>= ${binary:Version}),
8+ unity8-autopilot,
9 Description: Test package for address-book-app
10 Autopilot tests for the address-book-app package
11
12=== modified file 'src/app/addressbookapp.cpp'
13--- src/app/addressbookapp.cpp 2014-01-10 15:07:11 +0000
14+++ src/app/addressbookapp.cpp 2014-02-13 02:55:34 +0000
15@@ -225,6 +225,18 @@
16 }
17 }
18
19+QUrl AddressBookApp::tempFile(const QString &templateName)
20+{
21+ // Create the temporary file
22+ QTemporaryFile tmpFile(templateName);
23+ tmpFile.setAutoRemove(false);
24+ if (tmpFile.open()) {
25+ return QUrl::fromLocalFile(tmpFile.fileName());
26+ }
27+ qWarning() << "Fail to create temp file for:" << templateName;
28+ return QUrl();
29+}
30+
31 void AddressBookApp::parseUrl(const QString &arg)
32 {
33 QUrl url = QUrl::fromPercentEncoding(arg.toUtf8());
34
35=== modified file 'src/app/addressbookapp.h'
36--- src/app/addressbookapp.h 2013-12-12 15:55:19 +0000
37+++ src/app/addressbookapp.h 2014-02-13 02:55:34 +0000
38@@ -39,6 +39,7 @@
39 void parseUrl(const QString &arg);
40 void onViewStatusChanged(QQuickView::Status status);
41 void returnVcard(const QUrl &url);
42+ QUrl tempFile(const QString &baseName);
43
44 private:
45 void callQMLMethod(const QString name, QStringList args);
46
47=== modified file 'src/imports/Common/CMakeLists.txt'
48--- src/imports/Common/CMakeLists.txt 2013-07-31 19:36:55 +0000
49+++ src/imports/Common/CMakeLists.txt 2014-02-13 02:55:34 +0000
50@@ -3,6 +3,7 @@
51 ContactDetailItem.qml
52 ContactDetailGroupBase.qml
53 ContactDetailGroupWithTypeBase.qml
54+ ContactPage.qml
55 )
56
57 install(FILES ${CONTACT_COMMON_QMLS}
58
59=== added file 'src/imports/Common/ContactPage.qml'
60--- src/imports/Common/ContactPage.qml 1970-01-01 00:00:00 +0000
61+++ src/imports/Common/ContactPage.qml 2014-02-13 02:55:34 +0000
62@@ -0,0 +1,34 @@
63+/*
64+ * Copyright (C) 2012-2013 Canonical, Ltd.
65+ *
66+ * This program is free software; you can redistribute it and/or modify
67+ * it under the terms of the GNU General Public License as published by
68+ * the Free Software Foundation; version 3.
69+ *
70+ * This program is distributed in the hope that it will be useful,
71+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
72+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
73+ * GNU General Public License for more details.
74+ *
75+ * You should have received a copy of the GNU General Public License
76+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
77+ */
78+
79+import QtQuick 2.0
80+import Ubuntu.Components 0.1
81+
82+Page {
83+ property string pageName
84+
85+ Component.onCompleted: {
86+ if (pageStack && pageName) {
87+ pageStack.currentStack += ";" + pageName
88+ }
89+ }
90+
91+ Component.onDestruction: {
92+ if (pageStack && pageName) {
93+ pageStack.currentStack = pageStack.currentStack.replace(";" + pageName, "")
94+ }
95+ }
96+}
97
98=== modified file 'src/imports/ContactEdit/CMakeLists.txt'
99--- src/imports/ContactEdit/CMakeLists.txt 2013-09-24 15:07:46 +0000
100+++ src/imports/ContactEdit/CMakeLists.txt 2014-02-13 02:55:34 +0000
101@@ -8,7 +8,7 @@
102 ContactDetailOrganizationsEditor.qml
103 ContactDetailPhoneNumbersEditor.qml
104 ContactDetailWithTypeEditor.qml
105- ContactEditor.qml
106+ ContactEditPage.qml
107 ContactFetchError.qml
108 EditToolbar.qml
109 KeyboardRectangle.qml
110
111=== modified file 'src/imports/ContactEdit/ContactDetailGroupWithTypeEditor.qml'
112--- src/imports/ContactEdit/ContactDetailGroupWithTypeEditor.qml 2014-01-14 20:43:59 +0000
113+++ src/imports/ContactEdit/ContactDetailGroupWithTypeEditor.qml 2014-02-13 02:55:34 +0000
114@@ -38,8 +38,22 @@
115 root.newDetails = []
116 }
117
118+ function updateType() {
119+ for(var i=0; i < detailDelegates.length; i++) {
120+ var delegate = detailDelegates[i]
121+ // Get item from Loader
122+ if (delegate.item) {
123+ delegate = delegate.item
124+ }
125+ if (delegate.save) {
126+ return updateDetail(delegate.detail, delegate.selectedTypeIndex)
127+ }
128+ }
129+ return false
130+ }
131+
132 function save() {
133- var changed = false
134+ var changed = updateType()
135 for(var i=0; i < detailDelegates.length; i++) {
136 var delegate = detailDelegates[i]
137
138@@ -49,11 +63,6 @@
139 }
140
141 if (delegate.save) {
142- // save type
143- if (updateDetail(delegate.detail, delegate.selectedTypeIndex)) {
144- changed = true
145- }
146-
147 // save fields
148 if (delegate.save()) {
149 changed = true
150@@ -63,6 +72,12 @@
151 return changed
152 }
153
154+ onActiveFocusChanged: {
155+ if (!activeFocus) {
156+ updateType()
157+ }
158+ }
159+
160 focus: true
161 minimumHeight: units.gu(5)
162 headerDelegate: ListItem.Empty {
163
164=== modified file 'src/imports/ContactEdit/ContactDetailNameEditor.qml'
165--- src/imports/ContactEdit/ContactDetailNameEditor.qml 2014-01-23 01:09:04 +0000
166+++ src/imports/ContactEdit/ContactDetailNameEditor.qml 2014-02-13 02:55:34 +0000
167@@ -27,9 +27,9 @@
168 property variant emptyFields: []
169
170 function save() {
171- var changed = false;
172+ var detailChanged = false
173
174- for(var i=0; i < fieldDelegates.length; i++) {
175+ for (var i=0; i < fieldDelegates.length; i++) {
176 var delegate = fieldDelegates[i]
177
178 // Get item from Loader
179@@ -38,14 +38,11 @@
180 }
181
182 if (delegate.detail && (delegate.field >= 0)) {
183- if (delegate.text != delegate.detail.value(delegate.field)) {
184- delegate.detail.setValue(delegate.field, delegate.text)
185- changed = true;
186- }
187+ detailChanged = detailChanged || delegate.changed
188 }
189 }
190
191- return changed
192+ return detailChanged
193 }
194
195 detail: root.contact ? root.contact.name : null
196
197=== modified file 'src/imports/ContactEdit/ContactDetailWithTypeEditor.qml'
198--- src/imports/ContactEdit/ContactDetailWithTypeEditor.qml 2013-11-22 01:05:52 +0000
199+++ src/imports/ContactEdit/ContactDetailWithTypeEditor.qml 2014-02-13 02:55:34 +0000
200@@ -37,26 +37,18 @@
201 }
202
203 function save() {
204- var detailchanged = false
205-
206- // save field values
207+ var detailChanged = detailTypeSelector.changed
208 var isEmpty = true
209+
210 for (var i=0; i < fieldValues.children.length; i++) {
211 var input = fieldValues.children[i]
212 if (input.detail && (input.field >= 0)) {
213- var originalValue = input.detail.value(input.field)
214- originalValue = originalValue ? String(originalValue) : ""
215- if (input.text !== "") {
216- isEmpty = false
217- }
218-
219- if (originalValue !== input.text) {
220- input.detail.setValue(input.field, input.text)
221- detailchanged = true
222- }
223+ isEmpty = isEmpty && input.isEmpty
224+ detailChanged = detailChanged || input.changed
225 }
226 }
227
228+ // remove empty detail
229 if (isEmpty) {
230 // unfavorite the contact if the favorite number was removed
231 if (contact.isPreferredDetail("TEL", detail)) {
232@@ -64,8 +56,7 @@
233 }
234 contact.removeDetail(input.detail)
235 }
236-
237- return detailchanged
238+ return detailChanged
239 }
240
241 focus: true
242@@ -91,7 +82,7 @@
243
244 height: visible ? (root.active ? units.gu(4) : units.gu(3)) : 0
245 onExpandedChanged: {
246- // Make sure that the inputfield get focus when clicking on type selector
247+ // Make sure that the input field get focus when clicking on type selector
248 if (expanded) {
249 root.forceActiveFocus()
250 }
251
252=== renamed file 'src/imports/ContactEdit/ContactEditor.qml' => 'src/imports/ContactEdit/ContactEditPage.qml'
253--- src/imports/ContactEdit/ContactEditor.qml 2013-11-21 12:14:04 +0000
254+++ src/imports/ContactEdit/ContactEditPage.qml 2014-02-13 02:55:34 +0000
255@@ -19,14 +19,22 @@
256 import Ubuntu.Components 0.1
257 import Ubuntu.Components.ListItems 0.1 as ListItem
258 import Ubuntu.Components.Popups 0.1
259+import "../Common"
260
261-Page {
262+ContactPage {
263 id: contactEditor
264 objectName: "contactEditorPage"
265
266 property QtObject contact: null
267 property QtObject model: null
268
269+ // state saver control
270+ property string persistentContactId: ""
271+ property string persistentContactVCardFile: ""
272+
273+ readonly property bool appActive: Qt.application.active
274+ property QtObject pendingContact: null
275+
276 // this is used to add a phone number to a existing contact
277 property int currentFetchOperation: -1
278 property string contactId: ""
279@@ -34,6 +42,9 @@
280
281 property QtObject activeItem: null
282
283+ pageName: "contactEditPage"
284+ StateSaver.properties: "persistentContactVCardFile, persistentContactId"
285+
286 // we use a custom toolbar in this view
287 tools: ToolbarItems {
288 locked: true
289@@ -110,6 +121,63 @@
290 scrollArea.returnToBounds()
291 }
292
293+ function saveState() {
294+ var fileName = "savedContact_XXXXXX.vcf"
295+ fileName = application.tempFile(fileName);
296+ if (fileName != "") {
297+ // save current contact info
298+ model.exportContacts(fileName, ["Sync", "Backup"], [contact])
299+ persistentContactVCardFile = fileName
300+ }
301+ if (contact.contactId != "qtcontacts:::") {
302+ persistentContactId = contact.contactId
303+ } else {
304+ persistentContactId = ""
305+ }
306+ pendingContact = null
307+ }
308+
309+ function restoreState() {
310+ // if was editing a contact load the contact first
311+ if (persistentContactId != "") {
312+ contactEditor.contactId = persistentContactId
313+ } else if (persistentContactVCardFile != "") {
314+ importerModel.importContacts(persistentContactVCardFile, ["Sync", "Backup"])
315+ }
316+ }
317+
318+ onAppActiveChanged: if (!appActive) saveState();
319+
320+ ContactModel {
321+ id: importerModel
322+
323+ manager: "memory"
324+ onContactsChanged: {
325+ var contact = importerModel.contacts[0]
326+ var newContact = null
327+
328+ if (contactEditor.pendingContact) {
329+ newContact = contactEditor.pendingContact
330+ newContact.clearDetails()
331+ } else {
332+ // is a new contact we need to remove the contactId
333+ newContact = Qt.createQmlObject("import QtContacts 5.0; Contact{}", contactEditor)
334+ }
335+
336+ var details = contact.contactDetails
337+ for(var index in details) {
338+ newContact.addDetail(details[index])
339+ }
340+
341+ contactEditor.contact = newContact
342+ contactEditor.pendingContact = null
343+ contactEditor.persistentContactVCardFile = ""
344+
345+ // move focus to first field until SDK implement a way to save it
346+ nameEditor.forceActiveFocus()
347+ }
348+ }
349+
350 ContactFetchError {
351 id: fetchErrorDialog
352 }
353@@ -119,16 +187,26 @@
354 onContactsFetched: {
355 if (requestId == currentFetchOperation) {
356 currentFetchOperation = -1
357+
358 // this fetch request can only return one contact
359 if(fetchedContacts.length !== 1) {
360 PopupUtils.open(fetchErrorDialog, null)
361 }
362- contact = fetchedContacts[0]
363+
364+ if (persistentContactVCardFile != "") {
365+ // we need to merge the contact information with the saved state vcard
366+ contactEditor.pendingContact = fetchedContacts[0]
367+ persistentContactId = ""
368+ // go to second step of state save restore
369+ contactEditor.restoreState()
370+ } else {
371+ contact = fetchedContacts[0]
372+ }
373 }
374 }
375 }
376
377- onContactIdChanged: {
378+ onContactIdChanged: {
379 if (contactId) {
380 currentFetchOperation = model.fetchContacts(contactId)
381 }
382@@ -286,8 +364,6 @@
383 }
384 }
385
386- Component.onCompleted: nameEditor.forceActiveFocus()
387-
388 ActivityIndicator {
389 id: busyIndicator
390
391@@ -358,4 +434,14 @@
392 }
393 }
394 }
395+
396+ Component.onCompleted: {
397+ nameEditor.forceActiveFocus()
398+ contactEditor.restoreState()
399+ }
400+
401+ Component.onDestruction: {
402+ contactEditor.persistentContactId = ""
403+ contactEditor.persistentContactVCardFile = ""
404+ }
405 }
406
407=== modified file 'src/imports/ContactEdit/TextInputDetail.qml'
408--- src/imports/ContactEdit/TextInputDetail.qml 2013-10-08 12:58:44 +0000
409+++ src/imports/ContactEdit/TextInputDetail.qml 2014-02-13 02:55:34 +0000
410@@ -26,10 +26,13 @@
411 property QtObject detail
412 property int field: -1
413 property variant originalValue: root.detail && (root.field >= 0) ? root.detail.value(root.field) : null
414+ property bool changed: false
415+ readonly property bool isEmpty: (text == "")
416
417 signal removeClicked()
418
419 Component.onCompleted: makeMeVisible(root)
420+ StateSaver.properties: "cursorPosition"
421
422 focus: true
423 text: originalValue ? originalValue : ""
424@@ -41,6 +44,12 @@
425 onActiveFocusChanged: {
426 if (activeFocus) {
427 makeMeVisible(root)
428+ } else if (root.detail) {
429+ var value = root.detail.value(root.field)
430+ if (String(value) != root.text) {
431+ root.changed = true
432+ root.detail.setValue(root.field, root.text)
433+ }
434 }
435 }
436
437
438=== modified file 'src/imports/ContactEdit/ValueSelector.qml'
439--- src/imports/ContactEdit/ValueSelector.qml 2013-11-22 00:47:10 +0000
440+++ src/imports/ContactEdit/ValueSelector.qml 2014-02-13 02:55:34 +0000
441@@ -21,6 +21,7 @@
442 id: root
443
444 property bool active: false
445+ property bool changed: false
446 property alias values: listView.model
447 property alias currentIndex: listView.currentIndex
448 readonly property bool expanded: (state === "expanded") && listView.opacity == 1.0
449@@ -161,7 +162,12 @@
450 width: parent.width + units.gu(0.5)
451 height: parent.height + units.gu(0.5)
452 anchors.centerIn: parent
453- onClicked: currentIndex = index
454+ onClicked: {
455+ if (currentIndex != index) {
456+ root.changed = true
457+ currentIndex = index
458+ }
459+ }
460 }
461 }
462
463
464=== modified file 'src/imports/ContactList/ContactListPage.qml'
465--- src/imports/ContactList/ContactListPage.qml 2013-12-17 23:47:28 +0000
466+++ src/imports/ContactList/ContactListPage.qml 2014-02-13 02:55:34 +0000
467@@ -21,7 +21,9 @@
468 import Ubuntu.Contacts 0.1 as ContactsUI
469 import QtContacts 5.0
470
471-Page {
472+import "../Common"
473+
474+ContactPage {
475 id: mainPage
476 objectName: "contactListPage"
477
478@@ -48,7 +50,24 @@
479 return newContact
480 }
481
482+ function restoreState()
483+ {
484+ // store current page before load contact list because this will overwrite it
485+ var stack = pageStack.currentStack.split(";")
486+ pageStack.currentStack = ""
487+ for (var index in stack) {
488+ var page = stack[index]
489+ // load previous state/page
490+ if (page == "contactEditPage") {
491+ pageStack.push(Qt.resolvedUrl("../ContactEdit/ContactEditPage.qml"),
492+ {model: contactList.listModel})
493+ } else if (page == "contactViewPage") {
494+ pageStack.push(Qt.resolvedUrl("../ContactView/ContactViewPage.qml"),
495+ {model: contactList.listModel})
496+ }
497+ }
498
499+ }
500
501 title: i18n.tr("Contacts")
502 Component {
503@@ -97,7 +116,7 @@
504 }
505
506 onContactClicked: {
507- pageStack.push(Qt.resolvedUrl("../ContactView/ContactView.qml"),
508+ pageStack.push(Qt.resolvedUrl("../ContactView/ContactViewPage.qml"),
509 {model: contactList.listModel, contactId: contact.contactId})
510 }
511
512@@ -118,6 +137,7 @@
513 contactList.listModel.removeContacts(ids)
514 }
515 }
516+
517 onSelectionCanceled: {
518 if (pickMode) {
519 if (contactContentHub) {
520@@ -154,7 +174,7 @@
521 iconSource: "artwork:/add.png"
522 onTriggered: {
523 var newContact = mainPage.createEmptyContact("")
524- pageStack.push(Qt.resolvedUrl("../ContactEdit/ContactEditor.qml"),
525+ pageStack.push(Qt.resolvedUrl("../ContactEdit/ContactEditPage.qml"),
526 {model: contactList.listModel, contact: newContact})
527 }
528 }
529@@ -164,16 +184,16 @@
530 Connections {
531 target: pageStack
532 onContactRequested: {
533- pageStack.push(Qt.resolvedUrl("../ContactView/ContactView.qml"),
534+ pageStack.push(Qt.resolvedUrl("../ContactView/ContactViewPage.qml"),
535 {model: contactList.listModel, contactId: contactId})
536 }
537 onCreateContactRequested: {
538 var newContact = mainPage.createEmptyContact(phoneNumber)
539- pageStack.push(Qt.resolvedUrl("../ContactEdit/ContactEditor.qml"),
540+ pageStack.push(Qt.resolvedUrl("../ContactEdit/ContactEditPage.qml"),
541 {model: contactList.listModel, contact: newContact})
542 }
543 onEditContatRequested: {
544- pageStack.push(Qt.resolvedUrl("../ContactEdit/ContactEditor.qml"),
545+ pageStack.push(Qt.resolvedUrl("../ContactEdit/ContactEditPage.qml"),
546 {model: contactList.listModel, contactId: contactId, newPhoneNumber: phoneNumber })
547 }
548 onContactCreated: {
549@@ -206,4 +226,12 @@
550 contactList.listModel.importContacts("file://" + TEST_DATA)
551 }
552 }
553+
554+ // WORKAROUND: Page stack fails to push page using Component.onCompleted. Check bug #1262224
555+ Timer {
556+ interval: 500
557+ running: true
558+ repeat: false
559+ onTriggered: restoreState()
560+ }
561 }
562
563=== modified file 'src/imports/ContactView/CMakeLists.txt'
564--- src/imports/ContactView/CMakeLists.txt 2013-10-21 19:25:51 +0000
565+++ src/imports/ContactView/CMakeLists.txt 2014-02-13 02:55:34 +0000
566@@ -13,7 +13,7 @@
567 ContactDetailPhoneNumberView.qml
568 ContactDetailWithTypeView.qml
569 ContactHeaderView.qml
570- ContactView.qml
571+ ContactViewPage.qml
572 )
573
574 install(FILES ${CONTACT_VIEW_QMLS}
575
576=== renamed file 'src/imports/ContactView/ContactView.qml' => 'src/imports/ContactView/ContactViewPage.qml'
577--- src/imports/ContactView/ContactView.qml 2013-11-14 17:48:48 +0000
578+++ src/imports/ContactView/ContactViewPage.qml 2014-02-13 02:55:34 +0000
579@@ -19,13 +19,15 @@
580 import Ubuntu.Components 0.1
581 import Ubuntu.Components.ListItems 0.1 as ListItem
582 import Ubuntu.Contacts 0.1 as ContactsUI
583+import "../Common"
584
585-Page {
586+ContactPage {
587 id: root
588 objectName: "contactViewPage"
589
590 readonly property alias contact: contactFetch.contact
591- property variant contactId: null
592+ property string contactId: ""
593+ property string savedContactId: ""
594 property alias model: contactFetch.model
595
596 function formatNameToDisplay(contact) {
597@@ -43,12 +45,21 @@
598 }
599 }
600
601+ onSavedContactIdChanged: {
602+ if (root.contactId == "") {
603+ root.contactId = savedContactId
604+ contactFetch.fetchContact(savedContactId)
605+ }
606+ }
607
608+ pageName: "contactViewPage"
609+ StateSaver.properties: "savedContactId"
610 title: formatNameToDisplay(contact)
611 onActiveChanged: {
612 if (active) {
613- contactFetch.fetchContact(root.contactId)
614-
615+ if (root.contactId != "") {
616+ contactFetch.fetchContact(root.contactId)
617+ }
618 //WORKAROUND: to correct scroll back the page
619 flickable.contentY = -100
620 flickable.returnToBounds()
621@@ -147,6 +158,7 @@
622 id: contactFetch
623
624 onContactRemoved: pageStack.pop()
625+ onContactFetched: root.savedContactId = contact.contactId
626 }
627
628 tools: ToolbarItems {
629@@ -169,10 +181,15 @@
630 text: i18n.tr("Edit")
631 iconSource: "artwork:/edit.png"
632 onTriggered: {
633- pageStack.push(Qt.resolvedUrl("../ContactEdit/ContactEditor.qml"),
634+ pageStack.push(Qt.resolvedUrl("../ContactEdit/ContactEditPage.qml"),
635 { model: root.model, contact: root.contact})
636 }
637 }
638 }
639 }
640+
641+ Component.onDestruction: {
642+ // avoid save contact Id by stateSave
643+ savedContactId = ""
644+ }
645 }
646
647=== modified file 'src/imports/MainWindow.qml'
648--- src/imports/MainWindow.qml 2013-12-17 14:57:28 +0000
649+++ src/imports/MainWindow.qml 2014-02-13 02:55:34 +0000
650@@ -49,6 +49,8 @@
651 id: mainStack
652
653 property string newPhoneNumber: ""
654+ property string currentStack
655+ StateSaver.properties: "currentStack"
656
657 signal contactRequested(string contactId)
658 signal createContactRequested(string phoneNumber)
659@@ -68,7 +70,7 @@
660
661 Component.onCompleted: {
662 Theme.name = "Ubuntu.Components.Themes.SuruGradient"
663- mainStack.push(Qt.createComponent("ContactList/ContactListPage.qml"))
664+ mainStack.push(Qt.resolvedUrl("ContactList/ContactListPage.qml"))
665 mainWindow.applicationReady()
666 }
667
668
669=== modified file 'src/imports/Ubuntu/Contacts/ContactFetch.qml'
670--- src/imports/Ubuntu/Contacts/ContactFetch.qml 2013-11-04 14:56:40 +0000
671+++ src/imports/Ubuntu/Contacts/ContactFetch.qml 2014-02-13 02:55:34 +0000
672@@ -23,6 +23,7 @@
673 property bool running: false
674 property QtObject contact: null
675 property bool contactIsDirty: false
676+ property string contactId: ""
677
678 property string _pendingId: ""
679 property bool _ready: false
680@@ -31,6 +32,7 @@
681 signal contactRemoved()
682
683 function fetchContact(contactId) {
684+ root.contactId = contactId
685 if (root._ready) {
686 root._fetchContact(contactId)
687 } else {
688
689=== modified file 'tests/autopilot/address_book_app/emulators/main_window.py'
690--- tests/autopilot/address_book_app/emulators/main_window.py 2013-12-12 15:55:57 +0000
691+++ tests/autopilot/address_book_app/emulators/main_window.py 2014-02-13 02:55:34 +0000
692@@ -16,11 +16,11 @@
693 objectName="contactListPage")
694
695 def get_contact_edit_page(self):
696- return self.wait_select_single("ContactEditor",
697+ return self.wait_select_single("ContactEditPage",
698 objectName="contactEditorPage")
699
700 def get_contact_view_page(self):
701- return self.wait_select_single("ContactView",
702+ return self.wait_select_single("ContactViewPage",
703 objectName="contactViewPage")
704
705 def get_contact_list_pick_page(self):
706
707=== modified file 'tests/autopilot/address_book_app/tests/__init__.py'
708--- tests/autopilot/address_book_app/tests/__init__.py 2014-01-14 20:44:25 +0000
709+++ tests/autopilot/address_book_app/tests/__init__.py 2014-02-13 02:55:34 +0000
710@@ -11,10 +11,13 @@
711 import os
712 import time
713 import subprocess
714+import signal
715
716 from autopilot.testcase import AutopilotTestCase
717 from autopilot.matchers import Eventually
718 from autopilot.platform import model
719+from autopilot.introspection import get_proxy_object_for_existing_process
720+
721 from testtools.matchers import Equals
722
723 from address_book_app.emulators.main_window import MainWindow
724@@ -30,6 +33,11 @@
725 ARGS = []
726 PRELOAD_VCARD = False
727
728+ def do_reset_config(self):
729+ config = os.path.expanduser(os.path.join("~", ".config", "AddressBookApp.conf"))
730+ if os.path.exists(config):
731+ os.remove(config)
732+
733 def setUp(self):
734 self.pointing_device = toolkit_emulators.get_pointing_device()
735 super(AddressBookAppTestCase, self).setUp()
736@@ -43,7 +51,13 @@
737 else:
738 self.app_bin = AddressBookAppTestCase.DEFAULT_DEV_LOCATION
739
740+ self.do_reset_config()
741+ self.start_app()
742+
743+
744+ def start_app(self):
745 print "Running from: %s" % (self.app_bin)
746+
747 os.environ['QTCONTACTS_MANAGER_OVERRIDE'] = 'memory'
748 if AddressBookAppTestCase.PRELOAD_VCARD:
749 os.environ["ADDRESS_BOOK_TEST_DATA"] = "/usr/share/address-book-app/vcards/vcard.vcf"
750@@ -57,13 +71,13 @@
751 else:
752 self.launch_click_installed()
753
754+ def tearDown(self):
755+ super(AddressBookAppTestCase, self).tearDown()
756+
757 AddressBookAppTestCase.ARGS = []
758 AddressBookAppTestCase.PRELOAD_VCARD = False
759 self.main_window.visible.wait_for(True)
760
761- def tearDown(self):
762- super(AddressBookAppTestCase, self).tearDown()
763-
764 # start the vkb
765 if model() != "Desktop":
766 subprocess.check_call(["/sbin/initctl", "start", "maliit-server"])
767@@ -290,3 +304,29 @@
768 # wait for contact list to be visible again
769 list_page = self.main_window.get_contact_list_page()
770 self.assertThat(list_page.visible, Eventually(Equals(True)))
771+
772+ def ensure_app_has_quit(self):
773+ """Terminate as gracefully as possible the application and ensure
774+ that it has fully quit before returning"""
775+
776+ if model() == "Desktop":
777+ # make the app lost focus (this will save all info)
778+ self.keyboard.press_and_release("Alt+Tab")
779+ # kill the app
780+ self.app.process.send_signal(signal.SIGTERM)
781+ else:
782+ # On unity8 at the moment we have no clean way to close the app.
783+ # So we ask the shell first to show the home, unfocusing our app, which will
784+ # save its state. Then we simply send it a SIGTERM to force it to quit.
785+ # See bug https://bugs.launchpad.net/unity8/+bug/1261720 for more details.
786+ from unity8 import process_helpers
787+ pid = process_helpers._get_unity_pid()
788+ unity8 = get_proxy_object_for_existing_process(pid)
789+ shell = unity8.select_single("Shell")
790+ shell.slots.showHome()
791+ self.assertThat(shell.currentFocusedAppId, Eventually(NotEquals("address-book-app")))
792+ self.app.process.send_signal(signal.SIGTERM)
793+
794+ # Either way, we wait for the underlying process to be fully finished.
795+ self.app.process.wait()
796+ self.assertIsNotNone(self.app.process.returncode)
797
798=== added file 'tests/autopilot/address_book_app/tests/test_save_state.py'
799--- tests/autopilot/address_book_app/tests/test_save_state.py 1970-01-01 00:00:00 +0000
800+++ tests/autopilot/address_book_app/tests/test_save_state.py 2014-02-13 02:55:34 +0000
801@@ -0,0 +1,161 @@
802+# -*- Mode: Python; coding: utf-8; indent-tabs-mode: nil; tab-width: 4 -*-
803+# Copyright 2013 Canonical
804+#
805+# This program is free software: you can redistribute it and/or modify it
806+# under the terms of the GNU General Public License version 3, as published
807+# by the Free Software Foundation.
808+
809+"""Tests for the Addressbook App"""
810+
811+from __future__ import absolute_import
812+
813+from testtools.matchers import Equals
814+from autopilot.matchers import Eventually
815+
816+from address_book_app.tests import AddressBookAppTestCase
817+
818+
819+class TestStateSave(AddressBookAppTestCase):
820+ """ Tests the Save State functionality """
821+
822+ def test_create_contact_state(self):
823+ # execute add new contact
824+ self.main_window.open_toolbar().click_button("Add")
825+
826+ # fill name
827+ firstNameField = self.main_window.wait_select_single(
828+ "TextInputDetail",
829+ objectName="firstName")
830+ lastNameField = self.main_window.wait_select_single(
831+ "TextInputDetail",
832+ objectName="lastName")
833+ self.type_on_field(firstNameField, "Sherlock")
834+ self.type_on_field(lastNameField, "Holmes")
835+
836+ # fill phone number
837+ phone_number_0 = self.main_window.wait_select_single(
838+ "TextInputDetail",
839+ objectName="phoneNumber_0")
840+ self.type_on_field(phone_number_0, "81 8777 7755")
841+
842+ # fill email
843+ email_0 = self.main_window.wait_select_single(
844+ "TextInputDetail",
845+ objectName="emailAddress_0")
846+ self.type_on_field(email_0, "holmes.sherlock.uk")
847+
848+ # fill im
849+ im_0 = self.main_window.wait_select_single(
850+ "TextInputDetail",
851+ objectName="imUri_0")
852+ self.type_on_field(im_0, "sh.im.com.br")
853+
854+ # Change Im type
855+ im_value_selector = self.main_window.select_single(
856+ "ValueSelector",
857+ objectName="type_onlineAccount_0")
858+ self.pointing_device.click_object(im_value_selector)
859+ self.assertThat(im_value_selector.expanded, Eventually(Equals(True)))
860+
861+ im_address_0 = self.main_window.select_single(
862+ "TextInputDetail",
863+ objectName="imUri_0")
864+
865+ # select the type with index = 0
866+ self.select_a_value(im_address_0, im_value_selector, 0)
867+
868+ # fill address
869+ street_0 = self.main_window.wait_select_single(
870+ "TextInputDetail",
871+ objectName="streetAddress_0")
872+ self.type_on_field(street_0, "221B Baker Street")
873+ locality_0 = self.main_window.wait_select_single(
874+ "TextInputDetail",
875+ objectName="localityAddress_0")
876+ self.type_on_field(locality_0, "West End")
877+ region_0 = self.main_window.wait_select_single(
878+ "TextInputDetail",
879+ objectName="regionAddress_0")
880+ self.type_on_field(region_0, "London")
881+ postcode_0 = self.main_window.wait_select_single(
882+ "TextInputDetail",
883+ objectName="postcodeAddress_0")
884+ self.type_on_field(postcode_0, "7777")
885+ country_0 = self.main_window.wait_select_single(
886+ "TextInputDetail",
887+ objectName="countryAddress_0")
888+ self.type_on_field(country_0, "united kingdom")
889+
890+ # kill the app while in the create mode
891+ self.ensure_app_has_quit()
892+
893+ # start app again
894+ self.start_app()
895+
896+ # wait for edit page to appear
897+ edit_page = self.main_window.get_contact_edit_page()
898+ self.assertThat(edit_page.visible, Eventually(Equals(True)))
899+
900+ # check for the contact data displayed correct
901+
902+ # Name
903+ firstNameField = self.main_window.wait_select_single(
904+ "TextInputDetail",
905+ objectName="firstName")
906+ lastNameField = self.main_window.wait_select_single(
907+ "TextInputDetail",
908+ objectName="lastName")
909+
910+ self.assertThat(firstNameField.text, Eventually(Equals("Sherlock")))
911+ self.assertThat(lastNameField.text, Eventually(Equals("Holmes")))
912+
913+ # Phone number
914+ phone_number_0 = self.main_window.wait_select_single(
915+ "TextInputDetail",
916+ objectName="phoneNumber_0")
917+ self.assertThat(phone_number_0.text, Eventually(Equals("81 8777 7755")))
918+
919+ # Email
920+ email_0 = self.main_window.wait_select_single(
921+ "TextInputDetail",
922+ objectName="emailAddress_0")
923+ self.assertThat(email_0.text, Eventually(Equals("holmes.sherlock.uk")))
924+
925+ # Im
926+ im_0 = self.main_window.wait_select_single(
927+ "TextInputDetail",
928+ objectName="imUri_0")
929+ self.assertThat(im_0.text, Eventually(Equals("sh.im.com.br")))
930+
931+ # Im type
932+ im_value_selector = self.main_window.select_single(
933+ "ValueSelector",
934+ objectName="type_onlineAccount_0")
935+ self.assertThat(im_value_selector.currentIndex, Eventually(Equals(0)))
936+
937+ # Address
938+ street_0 = self.main_window.wait_select_single(
939+ "TextInputDetail",
940+ objectName="streetAddress_0")
941+ self.assertThat(street_0.text, Eventually(Equals("221B Baker Street")))
942+
943+ locality_0 = self.main_window.wait_select_single(
944+ "TextInputDetail",
945+ objectName="localityAddress_0")
946+ self.assertThat(locality_0.text, Eventually(Equals("West End")))
947+
948+ region_0 = self.main_window.wait_select_single(
949+ "TextInputDetail",
950+ objectName="regionAddress_0")
951+ self.assertThat(region_0.text, Eventually(Equals("London")))
952+
953+ postcode_0 = self.main_window.wait_select_single(
954+ "TextInputDetail",
955+ objectName="postcodeAddress_0")
956+ self.assertThat(postcode_0.text, Eventually(Equals("7777")))
957+
958+ country_0 = self.main_window.wait_select_single(
959+ "TextInputDetail",
960+ objectName="countryAddress_0")
961+ self.assertThat(country_0.text, Eventually(Equals("united kingdom")))
962+

Subscribers

People subscribed via source and target branches