Merge lp:~boiko/messaging-app/rtm-fix_1373479 into lp:messaging-app/rtm-14.09

Proposed by Gustavo Pichorim Boiko
Status: Merged
Approved by: Gustavo Pichorim Boiko
Approved revision: 211
Merged at revision: 213
Proposed branch: lp:~boiko/messaging-app/rtm-fix_1373479
Merge into: lp:messaging-app/rtm-14.09
Prerequisite: lp:~boiko/messaging-app/rtm-fix_search_test
Diff against target: 373 lines (+118/-68)
5 files modified
src/qml/Messages.qml (+20/-6)
tests/CMakeLists.txt (+5/-0)
tests/autopilot/messaging_app/emulators.py (+14/-2)
tests/autopilot/messaging_app/fixture_setup.py (+42/-36)
tests/autopilot/messaging_app/tests/test_messaging.py (+37/-24)
To merge this branch: bzr merge lp:~boiko/messaging-app/rtm-fix_1373479
Reviewer Review Type Date Requested Status
Bill Filler Pending
Review via email: mp+250124@code.launchpad.net

This proposal supersedes a proposal from 2015-01-21.

Commit message

Scroll new messages into view.

Description of the change

Scroll new messages into view.

To post a comment you must log in.
Revision history for this message
Bill Filler (bfiller) wrote : Posted in a previous version of this proposal

if you scroll up the thread view and then recv a new message, it does autoscroll to the new message but the new message is repeated twice. Scrolling it out of view and back then corrects it to only show once.

review: Needs Fixing
211. By Gustavo Pichorim Boiko

Use the prepopulated database to the test.

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1=== modified file 'src/qml/Messages.qml'
2--- src/qml/Messages.qml 2015-01-23 20:11:21 +0000
3+++ src/qml/Messages.qml 2015-02-18 12:48:57 +0000
4@@ -50,7 +50,7 @@
5 property alias contactWatcher: contactWatcherInternal
6 property string lastFilter: ""
7 property string text: ""
8- property bool pendingMessage: false
9+ property string latestEventId: ""
10 property string scrollToEventId: ""
11 property bool isSearching: scrollToEventId !== ""
12
13@@ -542,6 +542,13 @@
14 }
15 ]
16
17+ Timer {
18+ id: scrollTimer
19+ repeat: false
20+ interval: 1
21+ onTriggered: messageList.positionViewAtBeginning()
22+ }
23+
24 HistoryEventModel {
25 id: eventModel
26 type: HistoryThreadModel.EventTypeText
27@@ -551,9 +558,18 @@
28 sortOrder: HistorySort.DescendingOrder
29 }
30 onCountChanged: {
31- if (pendingMessage) {
32- pendingMessage = false
33- messageList.positionViewAtBeginning()
34+ if (count == 0) {
35+ latestEventId = ""
36+ return
37+ }
38+ if (latestEventId == "") {
39+ latestEventId = eventModel.get(0).eventId
40+ } else if (latestEventId != eventModel.get(0).eventId) {
41+ latestEventId = eventModel.get(0).eventId
42+ // FIXME: if we scroll the ListView here, the view will end up
43+ // with duplicate delegates, so we postpone the scrolling to give the model
44+ // some time to finish moving items around
45+ scrollTimer.start()
46 }
47 if (isSearching) {
48 // if we ask for more items manually listview will stop working,
49@@ -951,14 +967,12 @@
50 attachment.push(item.filePath)
51 newAttachments.push(attachment)
52 }
53- pendingMessage = true
54 chatManager.sendMMS(participants, textEntry.text, newAttachments, messages.account.accountId)
55 textEntry.text = ""
56 attachments.clear()
57 return
58 }
59
60- pendingMessage = true
61 chatManager.sendMessage(participants, textEntry.text, messages.account.accountId)
62 textEntry.text = ""
63 }
64
65=== modified file 'tests/CMakeLists.txt'
66--- tests/CMakeLists.txt 2014-07-22 02:01:38 +0000
67+++ tests/CMakeLists.txt 2015-02-18 12:48:57 +0000
68@@ -2,6 +2,11 @@
69
70 set(AUTOPILOT_DIR autopilot/messaging_app)
71
72+file(GLOB AUTOPILOT_PY_FILES ${AUTOPILOT_DIR}/*.py ${AUTOPILOT_DIR}/tests/*.py)
73+
74+# custom target to get autopilot files visible on QtCreator
75+add_custom_target(messaging_app_AUTOPILOT_PY_FILES ALL SOURCES ${AUTOPILOT_PY_FILES})
76+
77 execute_process(COMMAND python3 -c "from distutils.sysconfig import get_python_lib; print(get_python_lib())"
78 OUTPUT_VARIABLE PYTHON_PACKAGE_DIR OUTPUT_STRIP_TRAILING_WHITESPACE)
79
80
81=== modified file 'tests/autopilot/messaging_app/emulators.py'
82--- tests/autopilot/messaging_app/emulators.py 2014-12-16 02:13:31 +0000
83+++ tests/autopilot/messaging_app/emulators.py 2015-02-18 12:48:57 +0000
84@@ -433,8 +433,7 @@
85
86 def get_messages_count(self):
87 """Return the number of meesages."""
88- return self.wait_select_single(
89- 'MessagesListView', objectName='messageList').count
90+ return self.get_list_view().count
91
92 @autopilot_logging.log_action(logger.info)
93 def select_messages(self, *indexes):
94@@ -449,6 +448,14 @@
95 message_delegate = self._get_message_delegate(index)
96 self.pointing_device.click_object(message_delegate)
97
98+ def scroll_list(self):
99+ list_view = self.get_list_view()
100+ x, y, width, height = list_view.globalRect
101+ start_x = stop_x = x + (width / 2)
102+ start_y = y + 10
103+ stop_y = y + height - 10
104+ self.pointing_device.drag(start_x, start_y, stop_x, stop_y)
105+
106 def _get_message_delegate(self, index):
107 return self.wait_select_single(
108 'MessageDelegateFactory', objectName='message{}'.format(index))
109@@ -486,6 +493,11 @@
110 return self.wait_select_single(
111 'TextArea', objectName='messageTextArea').text
112
113+ def get_list_view(self):
114+ """Returns the messages list view"""
115+ return self.wait_select_single(
116+ 'MessagesListView', objectName='messageList')
117+
118
119 class ListItemWithActions(_common.UbuntuUIToolkitCustomProxyObjectBase):
120
121
122=== modified file 'tests/autopilot/messaging_app/fixture_setup.py'
123--- tests/autopilot/messaging_app/fixture_setup.py 2015-02-18 12:48:57 +0000
124+++ tests/autopilot/messaging_app/fixture_setup.py 2015-02-18 12:48:57 +0000
125@@ -23,61 +23,65 @@
126 def setUp(self):
127 super(MessagingTestEnvironment, self).setUp()
128 self.useFixture(OfonoPhoneSIM())
129- self.useFixture(BackupHistory())
130 if self.use_testdata_db:
131 self.useFixture(FillCustomSmsHistory())
132+ else:
133+ self.useFixture(UseEmptySmsHistory())
134 self.useFixture(RespawnService())
135
136
137-class BackupHistory(fixtures.Fixture):
138-
139- def setUp(self):
140- super(BackupHistory, self).setUp()
141- self.addCleanup(self._restore_history)
142- self._backup_history()
143-
144- def _backup_history(self):
145- self.history = os.path.expanduser(
146- '~/.local/share/history-service/history.sqlite')
147- if os.path.exists(self.history):
148- os.rename(self.history, self.history + '.orig')
149-
150- def _restore_history(self):
151- try:
152- os.unlink(self.history)
153- except OSError:
154- pass
155- if os.path.exists(self.history + '.orig'):
156- os.rename(self.history + '.orig', self.history)
157-
158-
159 class FillCustomSmsHistory(fixtures.Fixture):
160
161 history_service_dir = os.path.expanduser("~/.local/share/history-service/")
162 history_db = "history.sqlite"
163 testdata_sys = "/usr/lib/python3/dist-packages/messaging_app/testdata/"
164 testdata_local = "messaging_app/testdata/"
165+ database_path = '/tmp/' + history_db
166
167 prefilled_history_local = os.path.join(testdata_local, history_db)
168 prefilled_history_system = os.path.join(testdata_sys, history_db)
169
170 def setUp(self):
171 super(FillCustomSmsHistory, self).setUp()
172- self.addCleanup(self._clear_test_data())
173- self._copy_test_data_history()
174+ self.addCleanup(self._clear_test_data)
175+ self.addCleanup(self._kill_service_to_respawn)
176+ self._clear_test_data()
177+ self._prepare_history_data()
178+ self._kill_service_to_respawn()
179+ self._start_service_with_custom_data()
180
181- def _copy_test_data_history(self):
182+ def _prepare_history_data(self):
183 if os.path.exists(self.prefilled_history_local):
184- shutil.copy(
185- self.prefilled_history_local, self.history_service_dir)
186+ shutil.copy(self.prefilled_history_local, self.database_path)
187 else:
188- shutil.copy(
189- self.prefilled_history_system, self.history_service_dir)
190-
191- def _clear_test_data(self):
192- test_data = os.path.join(self.history_service_dir, self.history_db)
193- if os.path.exists(test_data):
194- os.remove(test_data)
195+ shutil.copy(self.prefilled_history_system, self.database_path)
196+
197+ def _clear_test_data(self):
198+ if os.path.exists(self.database_path):
199+ os.remove(self.database_path)
200+
201+ def _kill_service_to_respawn(self):
202+ subprocess.call(['pkill', 'history-daemon'])
203+
204+ def _start_service_with_custom_data(self):
205+ os.environ['HISTORY_SQLITE_DBPATH'] = self.database_path
206+ with open(os.devnull, 'w') as devnull:
207+ subprocess.Popen(['history-daemon'], stderr=devnull)
208+
209+
210+class UseEmptySmsHistory(FillCustomSmsHistory):
211+ database_path = ':memory:'
212+
213+ def setUp(self):
214+ super(UseEmptySmsHistory, self).setUp()
215+
216+ def _prepare_history_data(self):
217+ # just avoid doing anything
218+ self.database_path = ':memory:'
219+
220+ def _clear_test_data(self):
221+ # don't do anything
222+ self.database_path = ''
223
224
225 class RespawnService(fixtures.Fixture):
226@@ -88,8 +92,10 @@
227 self._kill_services_to_respawn()
228
229 def _kill_services_to_respawn(self):
230- subprocess.call(['pkill', 'history-daemon'])
231 subprocess.call(['pkill', '-f', 'telephony-service-handler'])
232+ # on desktop, notify-osd may generate persistent popups (like for "SMS
233+ # received"), don't make that stay around for the tests
234+ subprocess.call(['pkill', '-f', 'notify-osd'])
235
236
237 class OfonoPhoneSIM(fixtures.Fixture):
238
239=== modified file 'tests/autopilot/messaging_app/testdata/history.sqlite'
240Binary files tests/autopilot/messaging_app/testdata/history.sqlite 2015-02-18 12:48:57 +0000 and tests/autopilot/messaging_app/testdata/history.sqlite 2015-02-18 12:48:57 +0000 differ
241=== modified file 'tests/autopilot/messaging_app/tests/test_messaging.py'
242--- tests/autopilot/messaging_app/tests/test_messaging.py 2015-02-18 12:48:57 +0000
243+++ tests/autopilot/messaging_app/tests/test_messaging.py 2015-02-18 12:48:57 +0000
244@@ -11,7 +11,6 @@
245
246 from __future__ import absolute_import
247
248-import subprocess
249 import time
250
251 from autopilot.matchers import Eventually
252@@ -39,13 +38,6 @@
253 self.assertThat(self.thread_list.visible, Equals(True))
254 self.assertThat(self.thread_list.count, Equals(0))
255
256- def tearDown(self):
257- super(BaseMessagingTestCase, self).tearDown()
258-
259- # on desktop, notify-osd may generate persistent popups (like for "SMS
260- # received"), don't make that stay around for the tests
261- subprocess.call(['pkill', '-f', 'notify-osd'])
262-
263
264 class TestMessaging(BaseMessagingTestCase):
265 """Tests for the communication panel."""
266@@ -301,19 +293,22 @@
267 self.assertThat(list_view.count, Eventually(Equals(0)))
268
269
270-class MessagingTestCaseWithExistingThread(BaseMessagingTestCase):
271+class MessagingTestCaseWithExistingThread(MessagingAppTestCase):
272
273 def setUp(self):
274+ test_setup = fixture_setup.MessagingTestEnvironment(
275+ use_testdata_db=True)
276+ self.useFixture(test_setup)
277+
278 super(MessagingTestCaseWithExistingThread, self).setUp()
279 self.main_page = self.main_view.select_single(emulators.MainPage)
280- self.number = '5555559876'
281- self.messages = self.receive_messages()
282+ self.number = '08154'
283
284- def receive_messages(self):
285- # send 3 messages. Reversed because on the QML, the one with the
286- # 0 index is the latest received.
287+ def receive_messages(self, count=3):
288+ # send the required number of messages. Reversed because on the QML,
289+ # the one with the 0 index is the latest received.
290 messages = []
291- message_indexes = list(reversed(range(3)))
292+ message_indexes = list(reversed(range(count)))
293 for index in message_indexes:
294 message_text = 'test message {}'.format(index)
295 helpers.receive_sms(
296@@ -322,28 +317,40 @@
297 # Prepend to make sure that the indexes match.
298 messages.insert(0, message_text)
299 # Wait for the thread.
300- self.assertThat(
301- self.main_page.get_thread_count, Eventually(Equals(1)))
302+ self.main_page.get_thread_from_number(self.number)
303 return messages
304
305 def test_delete_multiple_messages(self):
306 """Verify we can delete multiple messages"""
307 messages_page = self.main_page.open_thread(self.number)
308+ messages = messages_page.get_messages()
309
310 self.main_view.enable_messages_selection_mode()
311 messages_page.select_messages(1, 2)
312
313- # Wait a few seconds before clicking the header
314- # to make sure the OSD is already gone
315- # FIXME: check if there is a better way to detect OSD visibility
316- time.sleep(10)
317 self.main_view.click_messages_header_delete()
318
319 remaining_messages = messages_page.get_messages()
320 self.assertThat(remaining_messages, HasLength(1))
321- _, remaining_message_text = remaining_messages[0]
322+ remaining_message = remaining_messages[0]
323 self.assertEqual(
324- remaining_message_text, self.messages[0])
325+ remaining_message, messages[0])
326+
327+ def test_scroll_to_new_message(self):
328+ """Verify that the view is scrolled to display a new message"""
329+ # use the number of an existing thread to avoid OSD problems
330+ self.number = '08155'
331+ messages_page = self.main_page.open_thread(self.number)
332+
333+ # scroll the list to display older messages
334+ messages_page.scroll_list()
335+
336+ # now receive a new message
337+ self.receive_messages(1)
338+
339+ # and make sure that the list gets scrolled to the new message again
340+ list_view = messages_page.get_list_view()
341+ self.assertThat(list_view.atYEnd, Eventually(Equals(True)))
342
343
344 class MessagingTestSearch(MessagingAppTestCase):
345@@ -352,6 +359,7 @@
346 test_setup = fixture_setup.MessagingTestEnvironment(
347 use_testdata_db=True)
348 self.useFixture(test_setup)
349+
350 super(MessagingTestSearch, self).setUp()
351 self.thread_list = self.app.select_single(objectName='threadList')
352
353@@ -380,7 +388,7 @@
354 text_field.clear()
355 text_field.write('Ubuntu1')
356 self.assertThat(
357- lambda: count_visible_threads(threads), Eventually(Equals(1)))
358+ lambda: count_visible_threads(threads), Eventually(Equals(2)))
359
360 text_field.clear()
361 text_field.write('Ubuntu')
362@@ -388,6 +396,11 @@
363 lambda: count_visible_threads(threads), Eventually(Equals(5)))
364
365 text_field.clear()
366+ text_field.write('08154')
367+ self.assertThat(
368+ lambda: count_visible_threads(threads), Eventually(Equals(1)))
369+
370+ text_field.clear()
371 text_field.write('%')
372 # as we are testing for items NOT to appear, there is no other way
373 # other than sleeping for awhile before checking if the threads are

Subscribers

People subscribed via source and target branches