Merge lp:~artmello/gallery-app/gallery-app-remove_photo_from_album into lp:gallery-app

Proposed by Arthur Mello
Status: Merged
Approved by: Bill Filler
Approved revision: 1002
Merged at revision: 998
Proposed branch: lp:~artmello/gallery-app/gallery-app-remove_photo_from_album
Merge into: lp:gallery-app
Diff against target: 383 lines (+242/-9)
6 files modified
rc/qml/MediaViewer/MediaViewer.qml (+49/-5)
src/qml/qml-media-collection-model.cpp (+18/-0)
src/qml/qml-media-collection-model.h (+1/-0)
tests/autopilot/gallery_app/emulators/album_view.py (+94/-3)
tests/autopilot/gallery_app/emulators/gallery_utils.py (+3/-1)
tests/autopilot/gallery_app/tests/test_album_view.py (+77/-0)
To merge this branch: bzr merge lp:~artmello/gallery-app/gallery-app-remove_photo_from_album
Reviewer Review Type Date Requested Status
Bill Filler (community) Approve
PS Jenkins bot continuous-integration Approve
Tiago Salem Herrmann (community) Approve
Chris Gagnon (community) autopilot tests review Approve
Review via email: mp+222739@code.launchpad.net

Commit message

Add option to remove from album or delete from gallery when viewing photos from an album

Description of the change

Add option to remove from album or delete from gallery when viewing photos from an album

To post a comment you must log in.
Revision history for this message
PS Jenkins bot (ps-jenkins) wrote :
review: Approve (continuous-integration)
Revision history for this message
Chris Gagnon (chris.gagnon) wrote :

This only tests one button of the 3 new buttons. The other 2 buttons should be tested. (make sure on the remove from album and delete test that you verify the file is removed from the file system.)

The one button test you have is pretty solid and would not be a flaky test, but a couple of small changes can make it easier to maintain and troubleshoot.

def get_remove_* should be made private to _get_remove, then consumed by a click_* method. something like click_remove_from_album_button(). You can check the button becoming visible and being not shown as part of the same function

@autopilot_logging.log_action(logger.info) should be used as a method decorator on the click_* method(s) see example:
method http://bazaar.launchpad.net/~elopio/ubuntu-ui-toolkit/intermediate_flickables/revision/976/tests/autopilot/ubuntuuitoolkit/emulators.py

238 + photo = self.album_view.get_first_photo()
239 + self.main_view.close_toolbar()
240 + self.click_item(photo)

This should also be a method click_first_photo() in album_view with the @autopilot_logging.log_action(logger.info) decorator

so you can use self.album_view.click_first_photo() or better self.album_view.click_photo(path or photo name) instead of using 3 lines. That way if the toolbar goes away we won't have to delete a line from alot of tests, just the 1 line.

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

There are some mixed tab/spaces which cause errors and other pep8 issues

  1 album_view.py|10 col 1| F401 'os' imported but unused
  2 album_view.py|141 col 80| E501 line too long (83 > 79 characters)
  3 album_view.py|149 col 80| E501 line too long (84 > 79 characters)
  4 album_view.py|155 col 80| E501 line too long (84 > 79 characters)
  5 album_view.py|157 col 80| E501 line too long (82 > 79 characters)
  6 album_view.py|180 col 5| E101 indentation contains mixed spaces and tabs
  7 album_view.py|180 col 5| W191 indentation contains tabs
  8 album_view.py|185 col 5| E101 indentation contains mixed spaces and tabs
  9 album_view.py|185 col 5| W191 indentation contains tabs
 10 album_view.py|190 col 5| E101 indentation contains mixed spaces and tabs
 11 album_view.py|190 col 5| W191 indentation contains tabs
 12 album_view.py|195 col 5| E101 indentation contains mixed spaces and tabs
 13 album_view.py|195 col 5| W191 indentation contains tabs
 14 album_view.py|200 col 5| E101 indentation contains mixed spaces and tabs
 15 album_view.py|200 col 5| W191 indentation contains tabs
 16 album_view.py|205 col 5| E101 indentation contains mixed spaces and tabs
 17 album_view.py|205 col 5| W191 indentation contains tabs
 18 album_view.py|215 col 80| E501 line too long (87 > 79 characters)

tests/test_album_view.py:11:1: F401 'Is' imported but unused
tests/test_album_view.py:129:13: E128 continuation line under-indented for visual indent
tests/test_album_view.py:149:13: E128 continuation line under-indented for visual indent
tests/test_album_view.py:171:13: E128 continuation line under-indented for visual indent

I've also left an inline code comment for an additional assert

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

Are there any related MPs required for this MP to build/function as expected? Please list: No
Is your branch in sync with latest trunk (e.g. bzr pull lp:trunk -> no changes): Yes
Did you perform an exploratory manual test run of your code change and any related functionality on device or emulator?: Yes
Did you successfully run all tests found in your component's Test Plan (https://wiki.ubuntu.com/Process/Merges/TestPlan/gallery-app) on device or emulator? Yes
If you changed the UI, was the change specified/approved by design? No UI changes.
If you changed the packaging (debian), did you subscribe a core-dev to this MP? No packaging changes.

Revision history for this message
Chris Gagnon (chris.gagnon) wrote :

There is one minor pep8 error introduced by a deleted line.

photo_viewer.py:89:5: E301 expected 1 blank line, found 0
gallery_utils.py|13 col 1| E302 expected 2 blank lines, found 1

Sorry I didn't catch it in the first review

review: Needs Fixing
Revision history for this message
Chris Gagnon (chris.gagnon) wrote :

LGTM thanks for fixing those :D

review: Approve (autopilot tests review)
Revision history for this message
PS Jenkins bot (ps-jenkins) wrote :
review: Needs Fixing (continuous-integration)
Revision history for this message
Tiago Salem Herrmann (tiagosh) wrote :

looks good to me.

-- Checklist --

Did you perform an exploratory manual test run of the code change and any related functionality on device or emulator?
Yes

Did CI run pass? If not, please explain why.
Yes

Have you checked that submitter has accurately filled out the submitter checklist and has taken no shortcut?
Yes

review: Approve
Revision history for this message
PS Jenkins bot (ps-jenkins) wrote :
review: Approve (continuous-integration)
Revision history for this message
Tiago Salem Herrmann (tiagosh) :
review: Approve
Revision history for this message
Bill Filler (bfiller) wrote :

found an issue:
- add two photos to an album
- delete the second photo from the album with "remove from album and delete"
- the first photo is shown after the delete but no toolbar is visible and user gets stuck as no way to get back to the album view

review: Needs Fixing
Revision history for this message
Bill Filler (bfiller) wrote :

Same thing happens if you select "remove from album" (w/o delete). issue seems to be when removing leaves only a single picture left in album

1002. By Arthur Mello

Remove wrong signal since that cause issues when removing last photo of an album

Revision history for this message
PS Jenkins bot (ps-jenkins) wrote :
review: Approve (continuous-integration)
Revision history for this message
Bill Filler (bfiller) wrote :

tested, works
Did you perform an exploratory manual test run of the code change and any related functionality on device or emulator?
yes

Did you successfully run all tests found in your component's Test Plan (https://wiki.ubuntu.com/Process/Merges/TestPlan/<package-name>) on device or emulator?
yes

Did CI run pass? If not, please explain why.
yes

Have you checked that submitter has accurately filled out the submitter checklist and has taken no shortcut?
yes

review: Approve

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1=== modified file 'rc/qml/MediaViewer/MediaViewer.qml'
2--- rc/qml/MediaViewer/MediaViewer.qml 2014-05-20 15:32:39 +0000
3+++ rc/qml/MediaViewer/MediaViewer.qml 2014-06-17 03:24:34 +0000
4@@ -65,8 +65,8 @@
5
6 // tooolbar actions for the full view
7 property Item tools: media ? (media.type === MediaSource.Photo ?
8- d.photoToolbar : d.videoToolbar)
9- : null
10+ d.photoToolbar : d.videoToolbar)
11+ : null
12
13 /*!
14 */
15@@ -257,7 +257,7 @@
16 sharePanel.userAccountId = facebook.id;
17 sharePanel.visible = true;
18 viewerWrapper.tools.opened = false;
19- _lastPopover = null;
20+ _lastPopover = null;
21 }
22 }
23 }
24@@ -311,6 +311,48 @@
25 }
26 }
27
28+ Component {
29+ id: removeFromAlbumDialog
30+ Dialog {
31+ id: dialogue
32+ objectName: "removePhotoFromAlbumDialog"
33+ title: i18n.tr("Remove a photo from album")
34+
35+ function finishRemove() {
36+ if (model.count === 0)
37+ galleryPhotoViewer.closeRequested();
38+ }
39+
40+ Button {
41+ objectName: "removeFromAlbumButton"
42+ text: i18n.tr("Remove from Album")
43+ color: Gallery.HIGHLIGHT_BUTTON_COLOR
44+ onClicked: {
45+ PopupUtils.close(dialogue)
46+ viewerWrapper.model.removeMediaFromAlbum(album, galleryPhotoViewer.media);
47+ dialogue.finishRemove();
48+ }
49+ }
50+
51+ Button {
52+ objectName: "removeFromAlbumAndDeleteButton"
53+ text: i18n.tr("Remove from Album and Delete")
54+ onClicked: {
55+ PopupUtils.close(dialogue)
56+ viewerWrapper.model.destroyMedia(galleryPhotoViewer.media, true);
57+ dialogue.finishRemove();
58+ }
59+ }
60+
61+ Button {
62+ objectName: "removeFromAlbumCancelButton"
63+ text: i18n.tr("Cancel")
64+ onClicked: PopupUtils.close(dialogue)
65+ }
66+ }
67+
68+ }
69+
70 onCloseRequested: viewerWrapper.closeRequested()
71 onEditRequested: viewerWrapper.editRequested(media)
72 }
73@@ -488,7 +530,10 @@
74 text: i18n.tr("Delete")
75 iconSource: "../../img/delete.png"
76 onTriggered: {
77- PopupUtils.open(deleteDialog, null);
78+ if (album)
79+ PopupUtils.open(removeFromAlbumDialog, null);
80+ else
81+ PopupUtils.open(deleteDialog, null);
82 }
83 }
84 text: i18n.tr("Delete")
85@@ -592,5 +637,4 @@
86 }
87 }
88 }
89-
90 }
91
92=== modified file 'src/qml/qml-media-collection-model.cpp'
93--- src/qml/qml-media-collection-model.cpp 2014-03-21 18:13:28 +0000
94+++ src/qml/qml-media-collection-model.cpp 2014-06-17 03:24:34 +0000
95@@ -112,6 +112,24 @@
96 }
97
98 /*!
99+ * \brief QmlMediaCollectionModel::removeMediaFromAlbum
100+ * \param valbum
101+ * \param vmedia
102+ */
103+void QmlMediaCollectionModel::removeMediaFromAlbum(QVariant valbum, QVariant vmedia)
104+{
105+ Album* album = VariantToObject<Album*>(valbum);
106+ if (album == NULL)
107+ return;
108+
109+ MediaSource* media = VariantToObject<MediaSource*>(vmedia);
110+ if (media == NULL)
111+ return;
112+
113+ album->detach(media);
114+}
115+
116+/*!
117 * \brief QmlMediaCollectionModel::monitored
118 * \return
119 */
120
121=== modified file 'src/qml/qml-media-collection-model.h'
122--- src/qml/qml-media-collection-model.h 2014-03-21 18:13:28 +0000
123+++ src/qml/qml-media-collection-model.h 2014-06-17 03:24:34 +0000
124@@ -48,6 +48,7 @@
125 Q_INVOKABLE QVariant createAlbumFromSelected();
126 Q_INVOKABLE void destroySelectedMedia();
127 Q_INVOKABLE void destroyMedia(QVariant vmedia, bool destroy_backing);
128+ Q_INVOKABLE void removeMediaFromAlbum(QVariant valbum, QVariant vmedia);
129
130 bool monitored() const;
131 void setMonitored(bool monitor);
132
133=== modified file 'tests/autopilot/gallery_app/emulators/album_view.py'
134--- tests/autopilot/gallery_app/emulators/album_view.py 2014-06-10 19:00:23 +0000
135+++ tests/autopilot/gallery_app/emulators/album_view.py 2014-06-17 03:24:34 +0000
136@@ -5,13 +5,21 @@
137 # under the terms of the GNU General Public License version 3, as published
138 # by the Free Software Foundation.
139
140-from testtools.matchers import GreaterThan, LessThan
141+import logging
142+import re
143+
144+from testtools.matchers import GreaterThan, LessThan, Equals, Is
145+from autopilot.matchers import Eventually
146+
147+from autopilot import logging as autopilot_logging
148
149 from gallery_app.emulators.gallery_utils import(
150 GalleryUtils,
151 GalleryAppException,
152 )
153
154+logger = logging.getLogger(__name__)
155+
156
157 class AlbumView(GalleryUtils):
158 """An emulator class that makes it easy to interact with the gallery app"""
159@@ -41,8 +49,7 @@
160
161 def get_album_photo_view(self):
162 """Returns the photo view of the album viewer"""
163- view = self.get_album_view()
164- return view.select_single("PopupPhotoViewer")
165+ return self.app.select_single("PopupPhotoViewer")
166
167 def get_spread_view(self):
168 """Returns the inner spread view to access the pages"""
169@@ -126,3 +133,87 @@
170 :param page_number: The starting page number you are swiping from
171 '''
172 self._swipe_setup(page_number, 'right')
173+
174+ def _get_remove_from_album_dialog(self):
175+ """Returns the photo viewer remove from album dialog."""
176+ return self.app.wait_select_single("Dialog",
177+ objectName=
178+ "removePhotoFromAlbumDialog")
179+
180+ def _remove_from_album_dialog_shown(self):
181+ dialog = self.app.select_many("Dialog",
182+ objectName="removePhotoFromAlbumDialog")
183+ return len(dialog) >= 1
184+
185+ def _get_remove_from_album_popover_remove_item(self):
186+ """Returns remove button of the remove from album popover."""
187+ return self.app.select_single("Button",
188+ objectName="removeFromAlbumButton",
189+ visible=True)
190+
191+ def _get_remove_from_album_popover_delete_item(self):
192+ """Returns delete button of the remove from album popover."""
193+ return self.app.select_single("Button",
194+ objectName=
195+ "removeFromAlbumAndDeleteButton",
196+ visible=True)
197+
198+ def _get_remove_from_album_popover_cancel_item(self):
199+ """Returns cancel button of the remove from album popover."""
200+ return self.app.select_single("Button",
201+ objectName="removeFromAlbumCancelButton",
202+ visible=True)
203+
204+ def _ensure_remove_from_album_dialog_is_open(self):
205+ """Ensure that the remove from album dialog is fully opened."""
206+ remove_dialog = self._get_remove_from_album_dialog()
207+ self.assertThat(remove_dialog.visible, Eventually(Equals(True)))
208+ self.assertThat(remove_dialog.opacity, Eventually(Equals(1)))
209+
210+ def _ensure_remove_from_album_dialog_is_close(self):
211+ """Ensure that the remove from album dialog is fully closed."""
212+ self.assertThat(self._remove_from_album_dialog_shown,
213+ Eventually(Is(False)))
214+
215+ @autopilot_logging.log_action(logger.info)
216+ def click_remove_from_album_remove_button(self):
217+ """Click on the remove from album button of the remove dialog."""
218+ self._ensure_remove_from_album_dialog_is_open()
219+
220+ remove_item = self._get_remove_from_album_popover_remove_item()
221+ self.pointing_device.click_object(remove_item)
222+
223+ self._ensure_remove_from_album_dialog_is_close()
224+
225+ @autopilot_logging.log_action(logger.info)
226+ def click_remove_from_album_delete_button(self):
227+ """Click on the remove and delete of the remove dialog."""
228+ self._ensure_remove_from_album_dialog_is_open()
229+
230+ delete_item = self._get_remove_from_album_popover_delete_item()
231+ self.pointing_device.click_object(delete_item)
232+
233+ self._ensure_remove_from_album_dialog_is_close()
234+
235+ @autopilot_logging.log_action(logger.info)
236+ def click_remove_from_album_cancel_button(self):
237+ """Click on the cancel of the remove dialog."""
238+ self._ensure_remove_from_album_dialog_is_open()
239+
240+ cancel_item = self._get_remove_from_album_popover_cancel_item()
241+ self.pointing_device.click_object(cancel_item)
242+
243+ self._ensure_remove_from_album_dialog_is_close()
244+
245+ @autopilot_logging.log_action(logger.info)
246+ def click_first_photo(self):
247+ """Click on the first photo of the album and return the path of it."""
248+ photo = self.get_first_photo()
249+ images = photo.select_many('QQuickImage')
250+ path = ''
251+ for i in images:
252+ if str(i.source).startswith('image://gallery-standard/'):
253+ path = re.sub('^image://gallery-standard/', '',
254+ i.source).split('?')[0]
255+ self.pointing_device.click_object(photo)
256+ return path
257
258=== modified file 'tests/autopilot/gallery_app/emulators/gallery_utils.py'
259--- tests/autopilot/gallery_app/emulators/gallery_utils.py 2014-05-01 15:27:13 +0000
260+++ tests/autopilot/gallery_app/emulators/gallery_utils.py 2014-06-17 03:24:34 +0000
261@@ -6,6 +6,8 @@
262 # by the Free Software Foundation.
263 import ubuntuuitoolkit.emulators
264
265+from autopilot.testcase import AutopilotTestCase
266+
267 from time import sleep
268
269
270@@ -13,7 +15,7 @@
271 pass
272
273
274-class GalleryUtils(object):
275+class GalleryUtils(AutopilotTestCase):
276 """An emulator class that makes it easy to interact with
277 general components of the gallery app."""
278
279
280=== modified file 'tests/autopilot/gallery_app/tests/test_album_view.py'
281--- tests/autopilot/gallery_app/tests/test_album_view.py 2014-05-22 17:06:01 +0000
282+++ tests/autopilot/gallery_app/tests/test_album_view.py 2014-06-17 03:24:34 +0000
283@@ -14,9 +14,11 @@
284 from gallery_app.emulators.album_view import AlbumView
285 from gallery_app.emulators.albums_view import AlbumsView
286 from gallery_app.emulators.media_selector import MediaSelector
287+from gallery_app.emulators.photo_viewer import PhotoViewer
288 from gallery_app.emulators import album_editor
289 from gallery_app.tests import GalleryTestCase
290
291+import os
292 from time import sleep
293 from unittest import skip
294
295@@ -36,6 +38,10 @@
296 def media_selector(self):
297 return MediaSelector(self.app)
298
299+ @property
300+ def photo_viewer(self):
301+ return PhotoViewer(self.app)
302+
303 def setUp(self):
304 self.ARGS = []
305 super(TestAlbumView, self).setUp()
306@@ -105,6 +111,77 @@
307 lambda: self.album_view.number_of_photos(),
308 Eventually(Equals(num_photos_start + 1)))
309
310+ def test_remove_photo_from_album(self):
311+ self.main_view.close_toolbar()
312+ self.open_first_album()
313+ num_photos_start = self.album_view.number_of_photos()
314+ self.assertThat(num_photos_start, Equals(1))
315+
316+ path = self.album_view.click_first_photo()
317+
318+ self.assertThat(lambda: os.path.exists(path),
319+ Eventually(Equals(True)))
320+
321+ photo_view = self.album_view.get_album_photo_view()
322+ self.assertThat(photo_view.visible, Eventually(Equals(True)))
323+
324+ self.main_view.open_toolbar().click_button("deleteButton")
325+ self.album_view.click_remove_from_album_remove_button()
326+
327+ self.assertThat(lambda: self.album_view.number_of_photos(),
328+ Eventually(Equals(num_photos_start - 1)))
329+
330+ self.assertThat(lambda: os.path.exists(path),
331+ Eventually(Equals(True)))
332+
333+ def test_remove_photo_from_album_and_delete(self):
334+ self.main_view.close_toolbar()
335+ self.open_first_album()
336+ num_photos_start = self.album_view.number_of_photos()
337+ self.assertThat(num_photos_start, Equals(1))
338+
339+ path = self.album_view.click_first_photo()
340+
341+ self.assertThat(lambda: os.path.exists(path),
342+ Eventually(Equals(True)))
343+
344+ photo_view = self.album_view.get_album_photo_view()
345+ self.assertThat(photo_view.visible, Eventually(Equals(True)))
346+
347+ self.main_view.open_toolbar().click_button("deleteButton")
348+ self.album_view.click_remove_from_album_delete_button()
349+
350+ self.assertThat(lambda: self.album_view.number_of_photos(),
351+ Eventually(Equals(num_photos_start - 1)))
352+
353+ self.assertThat(lambda: os.path.exists(path),
354+ Eventually(Equals(False)))
355+
356+ def test_cancel_remove_photo_from_album(self):
357+ self.main_view.close_toolbar()
358+ self.open_first_album()
359+ num_photos_start = self.album_view.number_of_photos()
360+ self.assertThat(num_photos_start, Equals(1))
361+
362+ path = self.album_view.click_first_photo()
363+
364+ self.assertThat(lambda: os.path.exists(path),
365+ Eventually(Equals(True)))
366+
367+ photo_view = self.album_view.get_album_photo_view()
368+ self.assertThat(photo_view.visible, Eventually(Equals(True)))
369+
370+ self.main_view.open_toolbar().click_button("deleteButton")
371+ self.album_view.click_remove_from_album_cancel_button()
372+
373+ self.main_view.open_toolbar().click_button("backButton")
374+
375+ self.assertThat(lambda: self.album_view.number_of_photos(),
376+ Eventually(Equals(num_photos_start)))
377+
378+ self.assertThat(lambda: os.path.exists(path),
379+ Eventually(Equals(True)))
380+
381 def test_add_photo_to_new_album(self):
382 self.main_view.open_toolbar().click_button("addButton")
383 self.ui_update()

Subscribers

People subscribed via source and target branches