Merge lp:~nataliabidart/ubuntuone-control-panel/cloud-to-computer-page into lp:ubuntuone-control-panel

Proposed by Natalia Bidart
Status: Merged
Approved by: Natalia Bidart
Approved revision: 296
Merged at revision: 289
Proposed branch: lp:~nataliabidart/ubuntuone-control-panel/cloud-to-computer-page
Merge into: lp:ubuntuone-control-panel
Diff against target: 1455 lines (+810/-180)
9 files modified
data/qt/are_you_sure.ui (+153/-0)
data/qt/folders.ui (+10/-3)
ubuntuone/controlpanel/gui/__init__.py (+15/-0)
ubuntuone/controlpanel/gui/qt/controlpanel.py (+3/-1)
ubuntuone/controlpanel/gui/qt/folders.py (+46/-19)
ubuntuone/controlpanel/gui/qt/tests/test_controlpanel.py (+5/-0)
ubuntuone/controlpanel/gui/qt/tests/test_folders.py (+164/-91)
ubuntuone/controlpanel/gui/qt/tests/test_wizard.py (+240/-49)
ubuntuone/controlpanel/gui/qt/wizard.py (+174/-17)
To merge this branch: bzr merge lp:~nataliabidart/ubuntuone-control-panel/cloud-to-computer-page
Reviewer Review Type Date Requested Status
Roberto Alsina (community) Approve
Eric Casteleijn (community) Approve
Review via email: mp+97990@code.launchpad.net

Commit message

- Added 'Cloud to computer' page to the initial wizard (part of LP: #933697).
- Added a confirmation dialog to quit the wizard (part of LP: #933697).

Description of the change

To test IRL, please have nightlies installed and up to date. Then, from this branch, please run:

./setup.py clean build; U1_DEBUG=True PYTHONPATH=. bin/ubuntuone-control-panel-qt

Assuming you already have U1 credentials, go to the devices tab and remove the current device. You will be presented with the initial screen, where you can play with:

- closing from the button in the right bottom corner, you should get a confirmation dialog
- after login/register you should be presented with a screen to choose cloud folders to sync from your cloud to your desktop
- optionally, you can play with settings clicking on the button at the end of the folder listing

To post a comment you must log in.
Revision history for this message
Eric Casteleijn (thisfred) wrote :

Approved, provided that bug #957458 is fixed on the next branch.

review: Approve
Revision history for this message
Roberto Alsina (ralsina) wrote :

In the confirmation dialog, the texts in the buttons appear cutoff (to force it, make the dialog smaller). I can't quite figure out why, let's ask Diego. In any case, not a blocker for the branch.

Revision history for this message
Roberto Alsina (ralsina) wrote :

If the user has subscribed UDFs, it should not be shown the cloud-to-computer page (or the computer-to-cloud page when it's added).

Those pages are only meant for new users, and those who have subscribed UDFs don't fit that description.

Also, it's a UX change from the windows behaviour.

Aditionally, the behviour where this page shows the remote folders, and when you click on one it disappears is a UX change from the windows behaviour, and just strange. The changes should only be applied when the user moves to the next page.

Revision history for this message
Roberto Alsina (ralsina) wrote :

Perhaps we should just:

a) check if the user has any subscribed folders
b) if no -> move onto next page
c) if yes -> show the "real" folders page. That way a tentative user, who subscribes to a UDF and then decides he didn't really want to do it, can undo it.

That should not involve huge changes to the code, right?

Revision history for this message
Roberto Alsina (ralsina) wrote :

+1 great work!

review: Approve
Revision history for this message
Ubuntu One Auto Pilot (otto-pilot) wrote :
Download full text (47.6 KiB)

The attempt to merge lp:~nataliabidart/ubuntuone-control-panel/cloud-to-computer-page into lp:ubuntuone-control-panel failed. Below is the output from the failed tests.

*** Running test suite for ubuntuone/controlpanel ***
ubuntuone.controlpanel.tests.test_replication_client
  ReplicationsTestCase
    test_exclude ... [OK]
    test_exclude_name_in_exclusions ... [OK]
    test_exclude_name_not_in_replications ... [OK]
    test_get_exclusions ... [OK]
    test_get_replications ... [OK]
    test_no_pairing_record ... [OK]
    test_replicate ... [OK]
    test_replicate_name_not_in_exclusions ... [OK]
    test_replicate_name_not_in_replications ... [OK]
ubuntuone.controlpanel.tests
  TestCase
    runTest ... [OK]
ubuntuone.controlpanel.tests.test_backend
  BackendAccountTestCase
    test_account_info ... [OK]
    test_account_info_fails ... [OK]
    test_account_info_fails_with_unauthorized ... [OK]
    test_account_info_with_current_plan ... [OK]
    test_backend_creation ... [OK]
    test_device_is_local ... [OK]
    test_get_token ... [OK]
    test_login_client_is_cached ... [OK]
    test_sd_client_is_cached ... [OK]
    test_shutdown_func ... [OK]
    test_shutdown_func_is_called_on_shutdown ... [OK]
    test_shutdown_func_when_none ... [OK]
  BackendBasicTestCase
    test_backend_creation ... [OK]
    test_device_is_local ... [OK]
    test_get_token ... [OK]
    test_login_client_is_cached ... [OK]
    test_sd_client_is_cached ... [OK]
    test_shutdown_func ... [OK]
    test_shutdown_func_is_called_on_shutdown ... [OK]
    test_shutdown_func_when_none ... [OK]
  BackendCredentialsTestCase
    test_backend_creation ... [OK]
    test_clear_credentials ... [OK]
    test_clear_credentials_invalidates_cached_credentials ... [OK]
    test_credentials_are_cached ... [OK]
    test_device_is_lo...

Revision history for this message
Ubuntu One Auto Pilot (otto-pilot) wrote :
Download full text (47.6 KiB)

The attempt to merge lp:~nataliabidart/ubuntuone-control-panel/cloud-to-computer-page into lp:ubuntuone-control-panel failed. Below is the output from the failed tests.

*** Running test suite for ubuntuone/controlpanel ***
ubuntuone.controlpanel.tests.test_replication_client
  ReplicationsTestCase
    test_exclude ... [OK]
    test_exclude_name_in_exclusions ... [OK]
    test_exclude_name_not_in_replications ... [OK]
    test_get_exclusions ... [OK]
    test_get_replications ... [OK]
    test_no_pairing_record ... [OK]
    test_replicate ... [OK]
    test_replicate_name_not_in_exclusions ... [OK]
    test_replicate_name_not_in_replications ... [OK]
ubuntuone.controlpanel.tests
  TestCase
    runTest ... [OK]
ubuntuone.controlpanel.tests.test_backend
  BackendAccountTestCase
    test_account_info ... [OK]
    test_account_info_fails ... [OK]
    test_account_info_fails_with_unauthorized ... [OK]
    test_account_info_with_current_plan ... [OK]
    test_backend_creation ... [OK]
    test_device_is_local ... [OK]
    test_get_token ... [OK]
    test_login_client_is_cached ... [OK]
    test_sd_client_is_cached ... [OK]
    test_shutdown_func ... [OK]
    test_shutdown_func_is_called_on_shutdown ... [OK]
    test_shutdown_func_when_none ... [OK]
  BackendBasicTestCase
    test_backend_creation ... [OK]
    test_device_is_local ... [OK]
    test_get_token ... [OK]
    test_login_client_is_cached ... [OK]
    test_sd_client_is_cached ... [OK]
    test_shutdown_func ... [OK]
    test_shutdown_func_is_called_on_shutdown ... [OK]
    test_shutdown_func_when_none ... [OK]
  BackendCredentialsTestCase
    test_backend_creation ... [OK]
    test_clear_credentials ... [OK]
    test_clear_credentials_invalidates_cached_credentials ... [OK]
    test_credentials_are_cached ... [OK]
    test_device_is_lo...

Revision history for this message
Ubuntu One Auto Pilot (otto-pilot) wrote :
Download full text (47.6 KiB)

The attempt to merge lp:~nataliabidart/ubuntuone-control-panel/cloud-to-computer-page into lp:ubuntuone-control-panel failed. Below is the output from the failed tests.

*** Running test suite for ubuntuone/controlpanel ***
ubuntuone.controlpanel.tests.test_replication_client
  ReplicationsTestCase
    test_exclude ... [OK]
    test_exclude_name_in_exclusions ... [OK]
    test_exclude_name_not_in_replications ... [OK]
    test_get_exclusions ... [OK]
    test_get_replications ... [OK]
    test_no_pairing_record ... [OK]
    test_replicate ... [OK]
    test_replicate_name_not_in_exclusions ... [OK]
    test_replicate_name_not_in_replications ... [OK]
ubuntuone.controlpanel.tests
  TestCase
    runTest ... [OK]
ubuntuone.controlpanel.tests.test_backend
  BackendAccountTestCase
    test_account_info ... [OK]
    test_account_info_fails ... [OK]
    test_account_info_fails_with_unauthorized ... [OK]
    test_account_info_with_current_plan ... [OK]
    test_backend_creation ... [OK]
    test_device_is_local ... [OK]
    test_get_token ... [OK]
    test_login_client_is_cached ... [OK]
    test_sd_client_is_cached ... [OK]
    test_shutdown_func ... [OK]
    test_shutdown_func_is_called_on_shutdown ... [OK]
    test_shutdown_func_when_none ... [OK]
  BackendBasicTestCase
    test_backend_creation ... [OK]
    test_device_is_local ... [OK]
    test_get_token ... [OK]
    test_login_client_is_cached ... [OK]
    test_sd_client_is_cached ... [OK]
    test_shutdown_func ... [OK]
    test_shutdown_func_is_called_on_shutdown ... [OK]
    test_shutdown_func_when_none ... [OK]
  BackendCredentialsTestCase
    test_backend_creation ... [OK]
    test_clear_credentials ... [OK]
    test_clear_credentials_invalidates_cached_credentials ... [OK]
    test_credentials_are_cached ... [OK]
    test_device_is_lo...

Revision history for this message
Ubuntu One Auto Pilot (otto-pilot) wrote :
Download full text (47.6 KiB)

The attempt to merge lp:~nataliabidart/ubuntuone-control-panel/cloud-to-computer-page into lp:ubuntuone-control-panel failed. Below is the output from the failed tests.

*** Running test suite for ubuntuone/controlpanel ***
ubuntuone.controlpanel.tests.test_replication_client
  ReplicationsTestCase
    test_exclude ... [OK]
    test_exclude_name_in_exclusions ... [OK]
    test_exclude_name_not_in_replications ... [OK]
    test_get_exclusions ... [OK]
    test_get_replications ... [OK]
    test_no_pairing_record ... [OK]
    test_replicate ... [OK]
    test_replicate_name_not_in_exclusions ... [OK]
    test_replicate_name_not_in_replications ... [OK]
ubuntuone.controlpanel.tests
  TestCase
    runTest ... [OK]
ubuntuone.controlpanel.tests.test_backend
  BackendAccountTestCase
    test_account_info ... [OK]
    test_account_info_fails ... [OK]
    test_account_info_fails_with_unauthorized ... [OK]
    test_account_info_with_current_plan ... [OK]
    test_backend_creation ... [OK]
    test_device_is_local ... [OK]
    test_get_token ... [OK]
    test_login_client_is_cached ... [OK]
    test_sd_client_is_cached ... [OK]
    test_shutdown_func ... [OK]
    test_shutdown_func_is_called_on_shutdown ... [OK]
    test_shutdown_func_when_none ... [OK]
  BackendBasicTestCase
    test_backend_creation ... [OK]
    test_device_is_local ... [OK]
    test_get_token ... [OK]
    test_login_client_is_cached ... [OK]
    test_sd_client_is_cached ... [OK]
    test_shutdown_func ... [OK]
    test_shutdown_func_is_called_on_shutdown ... [OK]
    test_shutdown_func_when_none ... [OK]
  BackendCredentialsTestCase
    test_backend_creation ... [OK]
    test_clear_credentials ... [OK]
    test_clear_credentials_invalidates_cached_credentials ... [OK]
    test_credentials_are_cached ... [OK]
    test_device_is_lo...

Revision history for this message
Ubuntu One Auto Pilot (otto-pilot) wrote :

The attempt to merge lp:~nataliabidart/ubuntuone-control-panel/cloud-to-computer-page into lp:ubuntuone-control-panel failed. Below is the output from the failed tests.

*** Running test suite for ubuntuone/controlpanel ***

Traceback (most recent call last):
  File "/usr/bin/u1trial", line 325, in <module>
    main()
  File "/usr/bin/u1trial", line 305, in main
    suite = trial_runner.get_suite(config)
  File "/usr/bin/u1trial", line 184, in get_suite
    config['ignore-paths']))
  File "/usr/bin/u1trial", line 168, in _collect_tests
    module_suite = self._load_unittest(filepath)
  File "/usr/bin/u1trial", line 108, in _load_unittest
    module = __import__(modpath, None, None, [""])
  File "/mnt/tarmac/cache/ubuntuone-control-panel/trunk/ubuntuone/controlpanel/tests/__init__.py", line 28, in <module>
    from ubuntuone.controlpanel.backend import (
  File "/mnt/tarmac/cache/ubuntuone-control-panel/trunk/ubuntuone/controlpanel/backend.py", line 31, in <module>
    from ubuntuone.platform import is_link
ImportError: No module named platform

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1=== added file 'data/qt/are_you_sure.ui'
2--- data/qt/are_you_sure.ui 1970-01-01 00:00:00 +0000
3+++ data/qt/are_you_sure.ui 2012-03-19 14:00:47 +0000
4@@ -0,0 +1,153 @@
5+<?xml version="1.0" encoding="UTF-8"?>
6+<ui version="4.0">
7+ <class>Dialog</class>
8+ <widget class="QDialog" name="Dialog">
9+ <property name="geometry">
10+ <rect>
11+ <x>0</x>
12+ <y>0</y>
13+ <width>409</width>
14+ <height>196</height>
15+ </rect>
16+ </property>
17+ <layout class="QVBoxLayout" name="verticalLayout">
18+ <item>
19+ <widget class="QLabel" name="title_label">
20+ <property name="text">
21+ <string notr="true">Are you sure?</string>
22+ </property>
23+ <property name="textFormat">
24+ <enum>Qt::PlainText</enum>
25+ </property>
26+ </widget>
27+ </item>
28+ <item>
29+ <widget class="QLabel" name="message_label">
30+ <property name="text">
31+ <string notr="true">more explanation</string>
32+ </property>
33+ <property name="textFormat">
34+ <enum>Qt::AutoText</enum>
35+ </property>
36+ <property name="wordWrap">
37+ <bool>true</bool>
38+ </property>
39+ <property name="openExternalLinks">
40+ <bool>true</bool>
41+ </property>
42+ </widget>
43+ </item>
44+ <item>
45+ <spacer name="verticalSpacer">
46+ <property name="orientation">
47+ <enum>Qt::Vertical</enum>
48+ </property>
49+ <property name="sizeHint" stdset="0">
50+ <size>
51+ <width>20</width>
52+ <height>40</height>
53+ </size>
54+ </property>
55+ </spacer>
56+ </item>
57+ <item>
58+ <layout class="QHBoxLayout" name="horizontalLayout">
59+ <item>
60+ <spacer name="horizontalSpacer">
61+ <property name="orientation">
62+ <enum>Qt::Horizontal</enum>
63+ </property>
64+ <property name="sizeHint" stdset="0">
65+ <size>
66+ <width>40</width>
67+ <height>20</height>
68+ </size>
69+ </property>
70+ </spacer>
71+ </item>
72+ <item>
73+ <widget class="QPushButton" name="yes_button">
74+ <property name="text">
75+ <string notr="true">Yeah</string>
76+ </property>
77+ <property name="autoDefault">
78+ <bool>false</bool>
79+ </property>
80+ </widget>
81+ </item>
82+ <item>
83+ <spacer name="horizontalSpacer_2">
84+ <property name="orientation">
85+ <enum>Qt::Horizontal</enum>
86+ </property>
87+ <property name="sizeHint" stdset="0">
88+ <size>
89+ <width>40</width>
90+ <height>20</height>
91+ </size>
92+ </property>
93+ </spacer>
94+ </item>
95+ <item>
96+ <widget class="QPushButton" name="no_button">
97+ <property name="text">
98+ <string notr="true">Nopes</string>
99+ </property>
100+ <property name="default">
101+ <bool>true</bool>
102+ </property>
103+ </widget>
104+ </item>
105+ <item>
106+ <spacer name="horizontalSpacer_3">
107+ <property name="orientation">
108+ <enum>Qt::Horizontal</enum>
109+ </property>
110+ <property name="sizeHint" stdset="0">
111+ <size>
112+ <width>40</width>
113+ <height>20</height>
114+ </size>
115+ </property>
116+ </spacer>
117+ </item>
118+ </layout>
119+ </item>
120+ </layout>
121+ </widget>
122+ <resources/>
123+ <connections>
124+ <connection>
125+ <sender>no_button</sender>
126+ <signal>clicked()</signal>
127+ <receiver>Dialog</receiver>
128+ <slot>reject()</slot>
129+ <hints>
130+ <hint type="sourcelabel">
131+ <x>272</x>
132+ <y>174</y>
133+ </hint>
134+ <hint type="destinationlabel">
135+ <x>330</x>
136+ <y>129</y>
137+ </hint>
138+ </hints>
139+ </connection>
140+ <connection>
141+ <sender>yes_button</sender>
142+ <signal>clicked()</signal>
143+ <receiver>Dialog</receiver>
144+ <slot>accept()</slot>
145+ <hints>
146+ <hint type="sourcelabel">
147+ <x>118</x>
148+ <y>167</y>
149+ </hint>
150+ <hint type="destinationlabel">
151+ <x>163</x>
152+ <y>122</y>
153+ </hint>
154+ </hints>
155+ </connection>
156+ </connections>
157+</ui>
158
159=== modified file 'data/qt/folders.ui'
160--- data/qt/folders.ui 2012-03-02 13:53:24 +0000
161+++ data/qt/folders.ui 2012-03-19 14:00:47 +0000
162@@ -6,7 +6,7 @@
163 <rect>
164 <x>0</x>
165 <y>0</y>
166- <width>345</width>
167+ <width>393</width>
168 <height>279</height>
169 </rect>
170 </property>
171@@ -97,7 +97,7 @@
172 <bool>false</bool>
173 </attribute>
174 <attribute name="headerStretchLastSection">
175- <bool>true</bool>
176+ <bool>false</bool>
177 </attribute>
178 <column>
179 <property name="text">
180@@ -153,7 +153,7 @@
181 </sizepolicy>
182 </property>
183 <property name="text">
184- <string notr="true">Add a folder from this computer</string>
185+ <string notr="true">Add a folder</string>
186 </property>
187 <property name="default">
188 <bool>true</bool>
189@@ -161,6 +161,13 @@
190 </widget>
191 </item>
192 <item>
193+ <widget class="QPushButton" name="check_settings_button">
194+ <property name="text">
195+ <string notr="true">Check settings</string>
196+ </property>
197+ </widget>
198+ </item>
199+ <item>
200 <spacer name="horizontalSpacer_2">
201 <property name="orientation">
202 <enum>Qt::Horizontal</enum>
203
204=== modified file 'ubuntuone/controlpanel/gui/__init__.py'
205--- ubuntuone/controlpanel/gui/__init__.py 2012-02-29 21:33:02 +0000
206+++ ubuntuone/controlpanel/gui/__init__.py 2012-03-19 14:00:47 +0000
207@@ -18,6 +18,10 @@
208
209 import gettext
210
211+# pylint: disable=W0611
212+from ubuntuone.clientdefs import APP_NAME
213+# pylint: enable=W0611
214+
215 from ubuntuone.controlpanel import TRANSLATION_DOMAIN
216 from ubuntuone.controlpanel.backend import UBUNTUONE_LINK
217
218@@ -78,6 +82,17 @@
219
220 ACCOUNT_LABEL = _('Your services')
221 ALWAYS_SUBSCRIBED = _('Always in sync')
222+ARE_YOU_SURE_HELP = _('If you need to know more about Ubuntu One, then '
223+ 'please go to {support_url}')
224+ARE_YOU_SURE_NO = _('No, continue setting up')
225+ARE_YOU_SURE_SUBTITLE = _('You can restart the setup process at any time '
226+ 'by clicking on Ubuntu One in your menu.')
227+ARE_YOU_SURE_TITLE = _('Are you sure you want to cancel setting up '
228+ 'Ubuntu One?')
229+ARE_YOU_SURE_YES = _('Yes, I want to cancel')
230+CLOUD_TO_COMPUTER_SUBTITLE = _('These are the folders in your cloud. '
231+ 'Select the ones you want to sync with this computer.')
232+CLOUD_TO_COMPUTER_TITLE = _('Syncing the cloud to your computer')
233 CONNECT_BUTTON_LABEL = _('Connect to Ubuntu One')
234 CONTACTS = _('Thunderbird plug-in')
235 CREDENTIALS_ERROR = _('There was a problem while retrieving the credentials.')
236
237=== modified file 'ubuntuone/controlpanel/gui/qt/controlpanel.py'
238--- ubuntuone/controlpanel/gui/qt/controlpanel.py 2012-03-14 20:02:25 +0000
239+++ ubuntuone/controlpanel/gui/qt/controlpanel.py 2012-03-19 14:00:47 +0000
240@@ -159,10 +159,12 @@
241 """The facebook button was clicked."""
242 qt.uri_hook(FACEBOOK_LINK)
243
244+ @log_call(logger.warning)
245 def on_wizard_rejected(self):
246 """Let clients know that we're done."""
247 self.finished.emit()
248
249+ @log_call(logger.info)
250 def on_wizard_finished(self, status):
251- """Move to controlpanel if wizar ended successfully."""
252+ """Move to controlpanel if wizard ended successfully."""
253 self.on_credentials_found()
254
255=== modified file 'ubuntuone/controlpanel/gui/qt/folders.py'
256--- ubuntuone/controlpanel/gui/qt/folders.py 2012-03-12 20:31:06 +0000
257+++ ubuntuone/controlpanel/gui/qt/folders.py 2012-03-19 14:00:47 +0000
258@@ -42,7 +42,11 @@
259 NAME_NOT_SET,
260 SHARE_ICON_NAME,
261 )
262-from ubuntuone.controlpanel.gui.qt import uri_hook, icon_from_name
263+from ubuntuone.controlpanel.gui.qt import (
264+ handle_errors,
265+ icon_from_name,
266+ uri_hook,
267+)
268 from ubuntuone.controlpanel.gui.qt.ubuntuonebin import UbuntuOneBin
269 from ubuntuone.controlpanel.gui.qt.ui import folders_ui
270
271@@ -61,6 +65,15 @@
272 YES = QtGui.QMessageBox.Yes
273
274
275+def _process_name(name):
276+ """Tweak 'name' with a translatable music folder name."""
277+ if name == MUSIC_REAL_PATH:
278+ result = MUSIC_DISPLAY_NAME
279+ else:
280+ result = name
281+ return result
282+
283+
284 class ExploreFolderButton(QtGui.QPushButton):
285 """A specialized button for the folder listing."""
286
287@@ -77,10 +90,11 @@
288
289
290 class FoldersPanel(UbuntuOneBin):
291- """The Folders Tab Panel widget"""
292+ """The Folders Tab Panel widget."""
293
294+ logger = logger
295+ remote_folders = False
296 ui_class = folders_ui
297- logger = logger
298 widget_items = {}
299
300 def _setup(self):
301@@ -89,14 +103,20 @@
302 self.ui.add_folder_button.folderCreated.connect(self.on_folder_created)
303 self.ui.add_folder_button.setText(FOLDERS_BUTTON_ADD_FOLDER)
304
305- self.ui.folders.headerItem().setText(0, FOLDERS_COLUMN_NAME)
306- self.ui.folders.headerItem().setText(1, FOLDERS_COLUMN_SYNC_LOCALLY)
307- self.ui.folders.headerItem().setText(2, FOLDERS_COLUMN_EXPLORE)
308+ self.ui.share_publish_button.setVisible(not self.remote_folders)
309+ self.ui.add_folder_button.setVisible(not self.remote_folders)
310+ self.ui.check_settings_button.setVisible(self.remote_folders)
311+
312+ self.ui.folders.headerItem().setText(FOLDER_NAME_COL,
313+ FOLDERS_COLUMN_NAME)
314+ self.ui.folders.headerItem().setText(SUBSCRIPTION_COL,
315+ FOLDERS_COLUMN_SYNC_LOCALLY)
316+ self.ui.folders.headerItem().setText(EXPLORE_COL,
317+ FOLDERS_COLUMN_EXPLORE)
318 headers = self.ui.folders.header()
319 headers.setResizeMode(FOLDER_NAME_COL, headers.Stretch)
320 headers.setResizeMode(SUBSCRIPTION_COL, headers.ResizeToContents)
321 headers.setResizeMode(EXPLORE_COL, headers.ResizeToContents)
322- headers.setStretchLastSection(False)
323
324 self.ui.share_publish_button.setText(FOLDERS_MANAGE_LABEL)
325 self.ui.share_publish_button.uri = MANAGE_FILES_LINK
326@@ -125,14 +145,7 @@
327 info = yield self.backend.volumes_info(with_storage_info=False)
328 self.process_info(info)
329
330- def _process_name(self, name):
331- """Tweak 'name' with a translatable music folder name."""
332- if name == MUSIC_REAL_PATH:
333- result = MUSIC_DISPLAY_NAME
334- else:
335- result = name
336- return result
337-
338+ @handle_errors(logger=logger)
339 @log_call(logger.debug)
340 def process_info(self, info):
341 """Load folders info into the tree view."""
342@@ -164,6 +177,8 @@
343 self.ui.folders.addTopLevelItem(item)
344
345 for volume in volumes:
346+ subscribed = bool(volume[u'subscribed'])
347+
348 is_root = volume[u'type'] == self.backend.ROOT_TYPE
349 is_share = volume[u'type'] == self.backend.SHARE_TYPE
350
351@@ -175,7 +190,7 @@
352 child.volume_path = volume['path']
353 child.volume_id = volume['volume_id']
354
355- name = self._process_name(volume[u'display_name'])
356+ name = _process_name(volume[u'display_name'])
357 child.setText(FOLDER_NAME_COL, name)
358 child.setToolTip(FOLDER_NAME_COL, name)
359 child.setToolTip(EXPLORE_COL, FOLDERS_COLUMN_EXPLORE)
360@@ -199,7 +214,7 @@
361 # issues.
362 checkbox = QtGui.QCheckBox(parent=self.ui.folders)
363 self.widget_items[checkbox] = child
364- if bool(volume[u'subscribed']):
365+ if subscribed:
366 checkbox.setCheckState(CHECKED)
367 else:
368 checkbox.setCheckState(UNCHECKED)
369@@ -217,11 +232,15 @@
370
371 checkbox.stateChanged.connect(cb)
372
373+ if self.remote_folders:
374+ # no explore button when showing only remote folders
375+ continue
376+
377 # attach a third item with a button to explore the folder
378 button = ExploreFolderButton(folder_path=child.volume_path,
379 parent=self.ui.folders)
380 self.widget_items[button] = child
381- button.setEnabled(bool(volume[u'subscribed']))
382+ button.setEnabled(subscribed)
383 self.ui.folders.setItemWidget(child, EXPLORE_COL, button)
384
385 self.ui.folders.expandAll()
386@@ -247,6 +266,7 @@
387 # Invalid name "on_folders_itemActivated", "on_folders_itemChanged"
388 # pylint: disable=C0103
389
390+ @handle_errors(logger=logger)
391 def on_folders_itemActivated(self, item, column=None):
392 """User activated a given row, open the path in a file browser."""
393 volume_path = getattr(item, 'volume_path', None)
394@@ -260,6 +280,7 @@
395 uri = unicode(QtCore.QUrl.fromLocalFile(volume_path).toString())
396 uri_hook(uri)
397
398+ @handle_errors(logger=logger)
399 @defer.inlineCallbacks
400 def on_folders_itemChanged(self, item, column=None):
401 """User changed the subscription for a given folder."""
402@@ -298,6 +319,12 @@
403 else:
404 # restore old value
405 old = UNCHECKED if subscribed else CHECKED
406- item.setCheckState(SUBSCRIPTION_COL, old)
407+ checkbox.setCheckState(old)
408
409 self.is_processing = False
410+
411+
412+class RemoteFoldersPanel(FoldersPanel):
413+ """The Folders Panel that only shows remote cloud folders."""
414+
415+ remote_folders = True
416
417=== modified file 'ubuntuone/controlpanel/gui/qt/tests/test_controlpanel.py'
418--- ubuntuone/controlpanel/gui/qt/tests/test_controlpanel.py 2012-03-12 16:53:02 +0000
419+++ ubuntuone/controlpanel/gui/qt/tests/test_controlpanel.py 2012-03-19 14:00:47 +0000
420@@ -181,6 +181,11 @@
421
422 self.assertNotIn('connect_files', self.ui.backend._called)
423
424+ def test_folder_panel_shows_all_folders(self):
425+ """The FolderPanel shows all folders (not remote only)."""
426+ remote = self.ui.ui.folders_tab.remote_folders
427+ self.assertFalse(remote)
428+
429
430 class ExternalLinkButtonsTestCase(ControlPanelTestCase):
431 """The link in the go-to-web buttons are correct."""
432
433=== modified file 'ubuntuone/controlpanel/gui/qt/tests/test_folders.py'
434--- ubuntuone/controlpanel/gui/qt/tests/test_folders.py 2012-03-12 20:31:06 +0000
435+++ ubuntuone/controlpanel/gui/qt/tests/test_folders.py 2012-03-19 14:00:47 +0000
436@@ -16,6 +16,7 @@
437
438 """Tests for the Control Panel."""
439
440+import copy
441 import logging
442 import operator
443 import os
444@@ -44,6 +45,13 @@
445 # pylint: disable=W0212, E1103
446
447
448+def volumes_with_music_unsubscribed():
449+ """Return a copy of FAKE_VOLUMES_MINIMAL_INFO with music unsubscribed."""
450+ volumes = copy.deepcopy(FAKE_VOLUMES_MINIMAL_INFO)
451+ volumes[0][2][1][u'subscribed'] = u''
452+ return volumes
453+
454+
455 def _build_name(name):
456 """Helper to build the name expected when showing folder info."""
457 if name:
458@@ -91,9 +99,15 @@
459 self.memento.setLevel(logging.DEBUG)
460 gui.logger.addHandler(self.memento)
461
462- old_home = os.environ['HOME']
463- os.environ['HOME'] = USER_HOME
464- self.addCleanup(lambda: os.environ.__setitem__('HOME', old_home))
465+ def set_item_checked(self, item, checked=True):
466+ """Make item to be checked."""
467+ checkbox = self.ui.ui.folders.itemWidget(item, gui.SUBSCRIPTION_COL)
468+ checkbox.setCheckState(gui.CHECKED if checked else gui.UNCHECKED)
469+
470+ def get_item_checked(self, item):
471+ """Get if item is checked."""
472+ checkbox = self.ui.ui.folders.itemWidget(item, gui.SUBSCRIPTION_COL)
473+ return (checkbox.checkState() == gui.CHECKED)
474
475
476 class FoldersPanelVolumesInfoTestCase(FoldersPanelTestCase):
477@@ -109,7 +123,45 @@
478 self.assertEqual(unicode(item.text(gui.FOLDER_NAME_COL)), name)
479 self.assertEqual(unicode(item.text(gui.SUBSCRIPTION_COL)),
480 gui.FOLDERS_COLUMN_SYNC_LOCALLY)
481- self.assertEqual(unicode(item.text(gui.EXPLORE_COL)), '')
482+ if not self.ui.remote_folders:
483+ self.assertEqual(unicode(item.text(gui.EXPLORE_COL)), '')
484+
485+ def assert_folder_row_correct(self, item, label, icon_name, volume,
486+ tweaked_path=None):
487+ """Check that the folder row 'item' is correct."""
488+ folders = self.ui.ui.folders
489+
490+ actual_label = unicode(item.text(gui.FOLDER_NAME_COL))
491+ self.assertEqual(label, actual_label)
492+
493+ if volume['type'] == self.ui.backend.ROOT_TYPE:
494+ # no check box but the ALWAYS_SUBSCRIBED legend
495+ self.assertEqual(unicode(item.text(gui.SUBSCRIPTION_COL)),
496+ gui.ALWAYS_SUBSCRIBED)
497+ else:
498+ subscribed = self.get_item_checked(item)
499+ self.assertEqual(subscribed, bool(volume['subscribed']))
500+
501+ actual_icon_name = item.icon_obj.icon_name
502+ self.assertEqual(icon_name, actual_icon_name)
503+
504+ self.assertEqual(item.volume_id, volume['volume_id'])
505+
506+ expected_path = volume['path']
507+ if tweaked_path is not None:
508+ expected_path = tweaked_path
509+ self.assertEqual(item.volume_path, expected_path)
510+
511+ # tooltips are correct
512+ self.assertEqual(item.toolTip(gui.FOLDER_NAME_COL), label)
513+ self.assertEqual(item.toolTip(gui.EXPLORE_COL),
514+ gui.FOLDERS_COLUMN_EXPLORE)
515+
516+ if not self.ui.remote_folders:
517+ # explore button is in place
518+ model_index = folders.indexFromItem(item, gui.EXPLORE_COL)
519+ button = folders.indexWidget(model_index)
520+ self.assertEqual(button.isEnabled(), bool(volume['subscribed']))
521
522 @defer.inlineCallbacks
523 def test_is_processing_while_asking_info(self):
524@@ -151,7 +203,6 @@
525
526 self.assert_folder_group_header_correct(item, name)
527
528- # check children
529 self.assertEqual(len(volumes), item.childCount())
530 sorted_vols = sorted(volumes, key=operator.itemgetter('path'))
531 for volume in sorted_vols:
532@@ -164,38 +215,14 @@
533 if volume['type'] == self.ui.backend.SHARE_TYPE:
534 name = volume['name']
535 expected_path = volume['realpath']
536- label = unicode(item.text(gui.FOLDER_NAME_COL))
537- self.assertEqual(label, name)
538-
539- if volume['type'] == self.ui.backend.ROOT_TYPE:
540- # no check box but the ALWAYS_SUBSCRIBED legend
541- self.assertEqual(unicode(item.text(gui.SUBSCRIPTION_COL)),
542- gui.ALWAYS_SUBSCRIBED)
543- else:
544- checkbox = self.ui.ui.folders.itemWidget(
545- item, gui.SUBSCRIPTION_COL)
546- subscribed = checkbox.checkState() == gui.CHECKED
547- self.assertEqual(subscribed, bool(volume['subscribed']))
548-
549- icon_name = item.icon_obj.icon_name
550+
551 if volume['type'] != self.ui.backend.SHARE_TYPE:
552- self.assertEqual(icon_name, gui.FOLDER_ICON_NAME)
553+ icon_name = gui.FOLDER_ICON_NAME
554 else:
555- self.assertEqual(icon_name, gui.SHARE_ICON_NAME)
556-
557- self.assertEqual(item.volume_id, volume['volume_id'])
558- self.assertEqual(item.volume_path, expected_path)
559-
560- # tooltips are correct
561- self.assertEqual(item.toolTip(gui.FOLDER_NAME_COL), name)
562- self.assertEqual(item.toolTip(gui.EXPLORE_COL),
563- gui.FOLDERS_COLUMN_EXPLORE)
564-
565- # explore button is in place
566- model_index = folders.indexFromItem(item, gui.EXPLORE_COL)
567- button = folders.indexWidget(model_index)
568- self.assertEqual(button.isEnabled(),
569- bool(volume['subscribed']))
570+ icon_name = gui.SHARE_ICON_NAME
571+
572+ self.assert_folder_row_correct(item, name, icon_name, volume,
573+ tweaked_path=expected_path)
574
575 treeiter += 1
576 item = treeiter.value()
577@@ -304,9 +331,12 @@
578 self.assertTrue(self.memento.check_warning(path, 'does not exist'))
579 self.assertEqual(self._called, False)
580
581- def test_process_info_with_music_folder(self):
582+ def test_process_info_with_music_folder(self, volumes=None):
583 """The volumes info is processed when ready."""
584- self.ui.process_info(FAKE_VOLUMES_MINIMAL_INFO)
585+ if volumes is None:
586+ volumes = FAKE_VOLUMES_MINIMAL_INFO
587+
588+ self.ui.process_info(volumes)
589 folders = self.ui.ui.folders
590
591 treeiter = gui.QtGui.QTreeWidgetItemIterator(folders)
592@@ -318,46 +348,44 @@
593 treeiter += 1
594 item = treeiter.value()
595
596- volume = MUSIC_FOLDER
597-
598- label = unicode(item.text(gui.FOLDER_NAME_COL))
599- self.assertEqual(label, gui.MUSIC_DISPLAY_NAME)
600-
601- checkbox = self.ui.ui.folders.itemWidget(item, gui.SUBSCRIPTION_COL)
602- subscribed = checkbox.checkState() == gui.CHECKED
603- self.assertEqual(subscribed, bool(volume['subscribed']))
604-
605- icon_name = item.icon_obj.icon_name
606- self.assertEqual(icon_name, gui.MUSIC_ICON_NAME)
607-
608- self.assertEqual(item.volume_id, volume['volume_id'])
609- self.assertEqual(item.volume_path, volume['path'])
610-
611- def test_share_publish_button(self):
612- """When clicking the share/publish button, the proper url is opened."""
613- self.assert_uri_hook_called(self.ui.ui.share_publish_button,
614- gui.MANAGE_FILES_LINK)
615+ volume = volumes[0][2][1]
616+
617+ self.assert_folder_row_correct(item, gui.MUSIC_DISPLAY_NAME,
618+ gui.MUSIC_ICON_NAME, volume)
619
620 def test_focus_order(self):
621 """Ensure that the inner widgets are in the correct tab order."""
622 self.ui.process_info(FAKE_VOLUMES_INFO)
623- # First, assert we are jumping from ui.folders to an
624- # QPushButton
625+ folders = self.ui.ui.folders
626+
627 widget = self.ui.ui.folders.nextInFocusChain()
628- self.assertIsInstance(widget, QtGui.QPushButton)
629- # Then, assert it's the right one
630- self.assertEqual(unicode(
631- self.ui.widget_items[widget].text(0)), u"My Ubuntu")
632- # Next are a checkbox / pushbutton pair
633- # in the 'bar' item
634- widget = widget.nextInFocusChain()
635- self.assertIsInstance(widget, QtGui.QCheckBox)
636- self.assertEqual(unicode(
637- self.ui.widget_items[widget].text(0)), u"bar")
638- widget = widget.nextInFocusChain()
639- self.assertIsInstance(widget, QtGui.QPushButton)
640- self.assertEqual(unicode(
641- self.ui.widget_items[widget].text(0)), u"bar")
642+ treeiter = gui.QtGui.QTreeWidgetItemIterator(folders)
643+ for name, _, volumes in FAKE_VOLUMES_INFO:
644+ item = treeiter.value()
645+ sorted_vols = sorted(volumes, key=operator.itemgetter('path'))
646+ for volume in sorted_vols:
647+ treeiter += 1
648+ item = treeiter.value() # get child folder
649+
650+ name = volume['path'].replace(USER_HOME + os.path.sep, '')
651+ if volume['type'] == self.ui.backend.SHARE_TYPE:
652+ name = volume['name']
653+ self.assertEqual(unicode(item.text(gui.FOLDER_NAME_COL)), name)
654+
655+ if volume['type'] != self.ui.backend.ROOT_TYPE:
656+ self.assertIsInstance(widget, QtGui.QCheckBox)
657+ self.assertEqual(unicode(
658+ self.ui.widget_items[widget].text(0)), name)
659+ widget = widget.nextInFocusChain()
660+
661+ if not self.ui.remote_folders:
662+ self.assertIsInstance(widget, QtGui.QPushButton)
663+ self.assertEqual(unicode(
664+ self.ui.widget_items[widget].text(0)), name)
665+ widget = widget.nextInFocusChain()
666+
667+ treeiter += 1
668+ item = treeiter.value()
669
670 def test_widget_dict(self):
671 """Ensure the widget_items dictionary is full."""
672@@ -377,6 +405,20 @@
673 item)
674 it += 1
675
676+ def test_share_publish_button(self):
677+ """When clicking the share/publish button, the proper url is opened."""
678+ self.assertTrue(self.ui.ui.share_publish_button.isVisible())
679+ self.assert_uri_hook_called(self.ui.ui.share_publish_button,
680+ gui.MANAGE_FILES_LINK)
681+
682+ def test_add_folder_button(self):
683+ """The 'add_folder_button' is visible by default."""
684+ self.assertTrue(self.ui.ui.add_folder_button.isVisible())
685+
686+ def test_check_settings_button(self):
687+ """The 'check_settings_button' is not visible by default."""
688+ self.assertFalse(self.ui.ui.check_settings_button.isVisible())
689+
690
691 class FoldersPanelAddFolderTestCase(FoldersPanelTestCase):
692 """The test suite for the folder creation from a local dir."""
693@@ -411,16 +453,25 @@
694 class FoldersPanelSubscriptionTestCase(FoldersPanelTestCase):
695 """The test suite for the folder subscription."""
696
697+ faked_volumes = FAKE_VOLUMES_MINIMAL_INFO
698+
699 @defer.inlineCallbacks
700 def setUp(self):
701 yield super(FoldersPanelSubscriptionTestCase, self).setUp()
702 self.patch(gui.os.path, 'exists', lambda path: True)
703 FakedDialog.response = gui.YES
704
705- self.ui.process_info(FAKE_VOLUMES_MINIMAL_INFO)
706+ self.ui.process_info(self.faked_volumes)
707 # the music folder
708 self.item = self.ui.ui.folders.topLevelItem(0).child(1)
709
710+ def set_item_checked(self, item=None, checked=True):
711+ """Make item to be checked."""
712+ if item is None:
713+ item = self.item
714+ test = super(FoldersPanelSubscriptionTestCase, self).set_item_checked
715+ test(item=item, checked=checked)
716+
717 @defer.inlineCallbacks
718 def test_on_folders_item_changed(self):
719 """Clicking on 'subscribed' updates the folder subscription."""
720@@ -428,12 +479,9 @@
721 volume = MUSIC_FOLDER
722 fid = volume['volume_id']
723 subscribed = not bool(volume['subscribed'])
724- check_state = gui.CHECKED if subscribed else gui.UNCHECKED
725
726 self.ui.is_processing = True
727- checkbox = self.ui.ui.folders.itemWidget(self.item,
728- gui.SUBSCRIPTION_COL)
729- checkbox.setCheckState(check_state)
730+ self.set_item_checked(self.item, subscribed)
731 self.ui.is_processing = False
732
733 yield self.ui.on_folders_itemChanged(self.item)
734@@ -442,9 +490,7 @@
735 self.assert_backend_called('change_volume_settings',
736 fid, {'subscribed': subscribed})
737
738- checkbox = self.ui.ui.folders.itemWidget(self.item,
739- gui.SUBSCRIPTION_COL)
740- value = checkbox.checkState() == gui.CHECKED
741+ value = self.get_item_checked(self.item)
742 self.assertEqual(value, bool(subscribed))
743
744 # folder list was reloaded
745@@ -494,7 +540,7 @@
746
747 # make sure the item is subscribed
748 self.ui.is_processing = True
749- self.item.setCheckState(gui.SUBSCRIPTION_COL, gui.CHECKED)
750+ self.set_item_checked()
751 self.ui.is_processing = False
752
753 yield self.ui.on_folders_itemChanged(self.item)
754@@ -513,7 +559,7 @@
755
756 # make sure the item is unsubscribed
757 self.ui.is_processing = True
758- self.item.setCheckState(gui.SUBSCRIPTION_COL, gui.UNCHECKED)
759+ self.set_item_checked()
760 self.ui.is_processing = False
761
762 yield self.ui.on_folders_itemChanged(self.item)
763@@ -528,7 +574,7 @@
764
765 # make sure the item is subscribed
766 self.ui.is_processing = True
767- self.item.setCheckState(gui.SUBSCRIPTION_COL, gui.CHECKED)
768+ self.set_item_checked()
769 self.ui.is_processing = False
770
771 yield self.ui.on_folders_itemChanged(self.item)
772@@ -537,8 +583,7 @@
773 self.assertNotIn('change_volume_settings', self.ui.backend._called)
774 self.assertFalse(self.ui.is_processing)
775
776- subscribed = self.item.checkState(gui.SUBSCRIPTION_COL) == gui.CHECKED
777- self.assertEqual(subscribed, False)
778+ self.assertFalse(self.get_item_checked(self.item))
779
780 @defer.inlineCallbacks
781 def test_subscribe_does_not_call_backend_if_answer_is_no(self):
782@@ -547,7 +592,7 @@
783
784 # make sure the item is subscribed
785 self.ui.is_processing = True
786- self.item.setCheckState(gui.SUBSCRIPTION_COL, gui.CHECKED)
787+ self.set_item_checked()
788 self.ui.is_processing = False
789
790 yield self.ui.on_folders_itemChanged(self.item)
791@@ -556,17 +601,14 @@
792 self.assertNotIn('change_volume_settings', self.ui.backend._called)
793 self.assertFalse(self.ui.is_processing)
794
795- subscribed = self.item.checkState(gui.SUBSCRIPTION_COL) == gui.CHECKED
796- self.assertEqual(subscribed, False)
797+ self.assertFalse(self.get_item_checked(self.item))
798
799 @defer.inlineCallbacks
800 def test_no_confirmation_if_unsubscribing(self):
801 """The confirmation dialog is not shown if unsubscribing."""
802 # make sure the item is unsubscribed
803 self.ui.is_processing = True
804- checkbox = self.ui.ui.folders.itemWidget(
805- self.item, gui.SUBSCRIPTION_COL)
806- checkbox.setCheckState(gui.UNCHECKED)
807+ self.set_item_checked(checked=False)
808 self.ui.is_processing = False
809
810 # the confirm dialog was not called so far
811@@ -577,3 +619,34 @@
812
813 self.assertTrue(FakedDialog.args is None, 'dialog was not run')
814 self.assertTrue(FakedDialog.kwargs is None, 'dialog was hid')
815+
816+
817+class RemoteFoldersPanelTestCase(FoldersPanelVolumesInfoTestCase):
818+ """The test case for the RemoteFoldersPanel widget."""
819+
820+ class_ui = gui.RemoteFoldersPanel
821+
822+ def test_process_info_with_music_folder(self, volumes=None):
823+ """The volumes info is processed when ready."""
824+ volumes = volumes_with_music_unsubscribed()
825+ parent = super(RemoteFoldersPanelTestCase, self)
826+ parent.test_process_info_with_music_folder(volumes=volumes)
827+
828+ def test_share_publish_button(self):
829+ """When clicking the share/publish button, the proper url is opened."""
830+ self.assertFalse(self.ui.ui.share_publish_button.isVisible())
831+
832+ def test_add_folder_button(self):
833+ """The 'add_folder_button' is not visible by default."""
834+ self.assertFalse(self.ui.ui.add_folder_button.isVisible())
835+
836+ def test_check_settings_button(self):
837+ """The 'check_settings_button' is visible by default."""
838+ self.assertTrue(self.ui.ui.check_settings_button.isVisible())
839+
840+
841+class RemoteFoldersPanelSubscriptionTestCase(FoldersPanelSubscriptionTestCase):
842+ """The test suite for the remote folder subscription."""
843+
844+ class_ui = gui.RemoteFoldersPanel
845+ faked_volumes = volumes_with_music_unsubscribed()
846
847=== modified file 'ubuntuone/controlpanel/gui/qt/tests/test_wizard.py'
848--- ubuntuone/controlpanel/gui/qt/tests/test_wizard.py 2012-03-12 16:53:02 +0000
849+++ ubuntuone/controlpanel/gui/qt/tests/test_wizard.py 2012-03-19 14:00:47 +0000
850@@ -22,10 +22,121 @@
851 from ubuntuone.controlpanel.gui.qt.tests import BaseTestCase, TOKEN
852
853
854+BUTTONS = [
855+ 'BackButton', 'CancelButton', 'CommitButton',
856+ 'CustomButton1', 'CustomButton2', 'CustomButton3',
857+ 'FinishButton', 'HelpButton', 'NextButton',
858+]
859+
860+
861+class AreYouSureTestCase(BaseTestCase):
862+ """Test suite for the "Are you sure?" dialog."""
863+
864+ class_ui = gui.AreYouSure
865+
866+ def test_title(self):
867+ """Check the window title."""
868+ self.assertEqual(self.ui.windowTitle(), gui.APP_NAME)
869+ self.assertEqual(self.ui.ui.title_label.text(), gui.ARE_YOU_SURE_TITLE)
870+
871+ def test_message_label(self):
872+ """Check the message label text."""
873+ link = gui.LINK_STYLE.format(link_url=gui.UBUNTUONE_LINK,
874+ link_text=gui.UBUNTUONE_LINK)
875+ msg = u'%s<p>%s' % (gui.ARE_YOU_SURE_SUBTITLE,
876+ gui.ARE_YOU_SURE_HELP.format(support_url=link))
877+ self.assertEqual(unicode(self.ui.ui.message_label.text()), msg)
878+
879+ def test_buttons(self):
880+ """The buttons have the correct text."""
881+ self.assertEqual(self.ui.ui.yes_button.text(), gui.ARE_YOU_SURE_YES)
882+ self.assertEqual(self.ui.ui.no_button.text(), gui.ARE_YOU_SURE_NO)
883+
884+
885+class UbuntuOnePageTestCase(BaseTestCase):
886+ """Test the UbuntuOnePage widget."""
887+
888+ class_ui = gui.UbuntuOnePage
889+ main_title = ''
890+ panel_class = gui.QtGui.QFrame
891+ sub_title = ''
892+
893+ def test_panel_class(self):
894+ """The panel_class is correct."""
895+ self.assertIsInstance(self.ui.panel, self.panel_class)
896+
897+ def test_panel_is_created(self):
898+ """The panel is properly added to the layout."""
899+ self.assertEqual(self.ui.layout().itemAt(2).widget(), self.ui.panel)
900+
901+ def test_title(self):
902+ """The title is correct."""
903+ self.assertIn(self.main_title, self.ui.title()) # avoid markup
904+
905+ def test_subtitle(self):
906+ """The subtitle is correct."""
907+ self.assertEqual(self.ui.subTitle(), self.sub_title)
908+
909+ def test_error_label(self):
910+ """The error label is hidden."""
911+ self.assertFalse(self.ui.form_errors_label.isVisible())
912+
913+ def test_is_final(self):
914+ """The page is not final."""
915+ # from the doc:
916+
917+ # After calling setFinalPage(true), isFinalPage() returns true and the
918+ # Finish button is visible (and enabled if isComplete() returns true).
919+
920+ # After calling setFinalPage(false), isFinalPage() returns true if
921+ #nextId() returns -1; otherwise, it returns false.
922+
923+ self.patch(self.ui, 'nextId', lambda *a: 0)
924+ self.assertFalse(self.ui.isFinalPage())
925+
926+
927+class SignInPageTestCase(UbuntuOnePageTestCase):
928+ """Test the SignInPage wizard page."""
929+
930+ class_ui = gui.SignInPage
931+ panel_class = gui.SignInPanel
932+
933+
934+class CloudToComputerPageTestCase(UbuntuOnePageTestCase):
935+ """Test the CloudToComputerPage wizard page."""
936+
937+ class_ui = gui.CloudToComputerPage
938+ main_title = gui.CLOUD_TO_COMPUTER_TITLE
939+ panel_class = gui.RemoteFoldersPanel
940+ sub_title = gui.CLOUD_TO_COMPUTER_SUBTITLE
941+
942+ def test_is_final(self):
943+ """The page is not final."""
944+ self.assertTrue(self.ui.isFinalPage())
945+
946+ def test_folder_panel_shows_remote_folders_only(self):
947+ """The FolderPanel shows only remote folders."""
948+ self.assertTrue(self.ui.panel.remote_folders)
949+
950+
951+class SettingsPageTestCase(UbuntuOnePageTestCase):
952+ """Test the SettingsPage wizard page."""
953+
954+ class_ui = gui.SettingsPage
955+ panel_class = gui.PreferencesPanel
956+
957+
958 class UbuntuOneWizardTestCase(BaseTestCase):
959- """Test the UbuntuOneWizard."""
960+ """Test the UbuntuOneWizard widget."""
961
962 class_ui = gui.UbuntuOneWizard
963+ confirm_response = gui.QtGui.QDialog.Accepted
964+
965+ @defer.inlineCallbacks
966+ def setUp(self):
967+ yield super(UbuntuOneWizardTestCase, self).setUp()
968+ self.patch(self.ui.confirm_dialog, 'exec_',
969+ lambda: self.confirm_response)
970
971 def test_options(self):
972 """Tne wizard options are correct."""
973@@ -40,59 +151,48 @@
974 """Tne wizard style is Modern."""
975 self.assertEqual(self.ui.wizardStyle(), self.ui.ModernStyle)
976
977- def test_cancel_button(self):
978- """Send the rejected signal when the cancel button is clicked."""
979- button = self.ui.button(self.ui.CancelButton)
980- self.assertEqual(unicode(button.text()), gui.CLOSE_AND_SETUP_LATER)
981-
982- self.ui.rejected.connect(self._set_called)
983- button.click()
984-
985- self.assertEqual(self._called, ((), {}))
986-
987- def test_button_layout(self):
988- """The button layout is correct."""
989- buttons = [
990- 'BackButton', 'CommitButton', 'CustomButton1', 'CustomButton2',
991- 'CustomButton3', 'FinishButton', 'HelpButton', 'NextButton',
992- ]
993- for button_name in buttons:
994- button = self.ui.button(getattr(self.ui, button_name))
995- self.assertFalse(button.isVisible(),
996- 'Button %s should not be visible.' % button_name)
997-
998- button = self.ui.button(self.ui.CancelButton)
999- self.assertTrue(button.isVisible(),
1000- 'Cancel button should not be visible.')
1001-
1002- def test_first_page(self):
1003- """The first page is the correct one."""
1004- self.assertEqual(self.ui.startId(),
1005- self.ui.pages[self.ui.signin_page])
1006-
1007- def test_tab_order(self):
1008- """The button tab order is correct."""
1009- # can not be tested due to Qt API limitations
1010-
1011-
1012-class SideWidgetTestCase(UbuntuOneWizardTestCase):
1013- """Test the side widget in the wizard."""
1014-
1015- def test_is_there(self):
1016+ def test_side_widget(self):
1017 """The side widget is correct."""
1018 self.assertIsInstance(self.ui.side_widget, gui.SideWidget)
1019 self.assertIs(self.ui.sideWidget(), self.ui.side_widget)
1020
1021-
1022-class SignInPageTestCase(UbuntuOneWizardTestCase):
1023+ def test_confirm_dialog(self):
1024+ """The confirm dialog is correct."""
1025+ self.assertIsInstance(self.ui.confirm_dialog, gui.AreYouSure)
1026+
1027+ def test_first_page(self):
1028+ """The first page is the correct one."""
1029+ expected = self.ui.pages[self.ui.signin_page]
1030+ self.assertEqual(self.ui.startId(), expected)
1031+
1032+ def test_done_accepted(self):
1033+ """When the wizard reached the end, emit finished."""
1034+ self.ui.finished.connect(self._set_called)
1035+
1036+ self.ui.done(gui.QtGui.QDialog.Accepted)
1037+
1038+ self.assertEqual(self._called, ((gui.QtGui.QDialog.Accepted,), {}))
1039+
1040+
1041+class UbuntuOneWizardSignInTestCase(UbuntuOneWizardTestCase):
1042 """Test the SignInPage wizard page."""
1043
1044+ buttons = {'CancelButton': (gui.CLOSE_AND_SETUP_LATER, 'rejected', ())}
1045 page_name = 'signin'
1046+ stage_name = page_name
1047
1048 @defer.inlineCallbacks
1049 def setUp(self):
1050- yield super(SignInPageTestCase, self).setUp()
1051+ yield super(UbuntuOneWizardSignInTestCase, self).setUp()
1052 self.page = getattr(self.ui, '%s_page' % self.page_name)
1053+ self._move_to_this_page()
1054+
1055+ def _move_to_this_page(self):
1056+ """Fake the wizard is moved to this page."""
1057+ page_id = self.ui.pages[self.page]
1058+ if self.ui.currentId() != page_id:
1059+ self.ui._next_id = page_id
1060+ self.ui.next()
1061
1062 def test_was_added(self):
1063 """The SignInPage is added correctly."""
1064@@ -103,8 +203,98 @@
1065 """The page is enabled at startup."""
1066 self.assertTrue(self.page.isEnabled())
1067
1068-
1069-class LoginTestCase(UbuntuOneWizardTestCase):
1070+ def test_focus_order(self):
1071+ """The focus order is correct."""
1072+ # can not be tested due to Qt API limitations
1073+
1074+ def test_button_layout(self):
1075+ """The button layout is correct."""
1076+ msg = '%s should not be visible.'
1077+ for button_name in BUTTONS:
1078+ button = self.ui.button(getattr(self.ui, button_name))
1079+ if button_name in self.buttons:
1080+ self.assertTrue(button.isVisible(),
1081+ '%s should be visible.' % button_name)
1082+ else:
1083+ self.assertFalse(button.isVisible(), msg % button_name)
1084+
1085+ def test_side_widget_stage(self):
1086+ """When in this page, the side_widget' stage is correct."""
1087+ self.assertEqual(self.ui.side_widget.stage,
1088+ getattr(self.ui.side_widget, '%s_stage' % self.stage_name))
1089+
1090+ def test_buttons_behavior(self):
1091+ """Send the rejected signal when the cancel button is clicked."""
1092+ msg = '%r should emit %r with args %r when clicked.'
1093+
1094+ for name, (text, signal, signal_args) in self.buttons.iteritems():
1095+ button = self.ui.button(getattr(self.ui, name))
1096+
1097+ if text is not None:
1098+ self.assertEqual(unicode(button.text()), text)
1099+
1100+ getattr(self.ui, signal).connect(self._set_called)
1101+ button.click()
1102+
1103+ self.assertEqual(self._called, (signal_args, {}),
1104+ msg % (name, signal, signal_args))
1105+ self._called = False
1106+
1107+ def test_done_rejected(self):
1108+ """When the wizard was cancelled and user confirmed, finish."""
1109+ self.patch(self, 'confirm_response', gui.QtGui.QDialog.Accepted)
1110+ self.ui.rejected.connect(self._set_called)
1111+
1112+ assert not self.ui.page(self.ui.currentId()).isFinalPage()
1113+ self.ui.done(gui.QtGui.QDialog.Rejected)
1114+
1115+ self.assertEqual(self._called, ((), {}))
1116+
1117+ def test_done_rejected_confirmation_rejected(self):
1118+ """When the wizard was cancelled but user unconfimed, do not finish."""
1119+ self.patch(self, 'confirm_response', gui.QtGui.QDialog.Rejected)
1120+ self.ui.accepted.connect(self._set_called)
1121+ self.ui.rejected.connect(self._set_called)
1122+ self.ui.finished.connect(self._set_called)
1123+
1124+ assert not self.ui.page(self.ui.currentId()).isFinalPage()
1125+ self.ui.done(gui.QtGui.QDialog.Rejected)
1126+
1127+ self.assertEqual(self._called, False)
1128+
1129+
1130+class UbuntuOneWizardCloudToComputerTestCase(UbuntuOneWizardSignInTestCase):
1131+ """Test the CloudToComputerPage wizard page."""
1132+
1133+ buttons = {'FinishButton':
1134+ (None, 'finished', (gui.QtGui.QDialog.Accepted,))}
1135+ page_name = 'cloud_folders'
1136+ stage_name = 'folders'
1137+
1138+ def test_done_rejected(self):
1139+ """When the wizard is closed on the final page, emit rejected."""
1140+ self.ui.finished.connect(self._set_called)
1141+
1142+ self.ui._next_id = self.ui.pages[self.ui.cloud_folders_page]
1143+ assert self.ui.page(self.ui.currentId()).isFinalPage()
1144+ self.ui.done(gui.QtGui.QDialog.Rejected)
1145+
1146+ self.assertEqual(self._called, ((gui.QtGui.QDialog.Rejected,), {}))
1147+
1148+ def test_done_rejected_confirmation_rejected(self):
1149+ """When the wizard was cancelled but user unconfimed, do not finish."""
1150+ # does not apply to this page
1151+
1152+
1153+class UbuntuOneWizardSettingsTestCase(UbuntuOneWizardSignInTestCase):
1154+ """Test the CloudToComputerPage wizard page."""
1155+
1156+ buttons = {'BackButton': (None, 'currentIdChanged', (0,))}
1157+ page_name = 'settings'
1158+ stage_name = 'folders'
1159+
1160+
1161+class UbuntuOneWizardLoginTestCase(UbuntuOneWizardTestCase):
1162 """Test the login through the wizard."""
1163
1164 method = 'login'
1165@@ -112,7 +302,7 @@
1166 @defer.inlineCallbacks
1167 def test_with_credentials(self):
1168 """Wizard is done when credentials were retrieved."""
1169- self.ui.finished.connect(self._set_called)
1170+ self.ui.currentIdChanged.connect(self._set_called)
1171 d = defer.succeed(TOKEN)
1172
1173 def check():
1174@@ -128,12 +318,13 @@
1175 yield d
1176
1177 self.assertTrue(self.ui.signin_page.isEnabled())
1178- self.assertEqual(self._called, ((1,), {}))
1179+ expected_next_id = self.ui.pages[self.ui.cloud_folders_page]
1180+ self.assertEqual(self._called, ((expected_next_id,), {}))
1181
1182 @defer.inlineCallbacks
1183 def test_without_credentials(self):
1184 """Wizard is done when credentials were retrieved."""
1185- self.ui.finished.connect(self._set_called)
1186+ self.ui.currentIdChanged.connect(self._set_called)
1187 d = defer.succeed(None)
1188
1189 def check():
1190@@ -152,7 +343,7 @@
1191 self.assertFalse(self._called)
1192
1193
1194-class RegisterTestCase(LoginTestCase):
1195+class UbuntuOneWizardRegisterTestCase(UbuntuOneWizardLoginTestCase):
1196 """Test the register through the wizard."""
1197
1198 method = 'register'
1199
1200=== modified file 'ubuntuone/controlpanel/gui/qt/wizard.py'
1201--- ubuntuone/controlpanel/gui/qt/wizard.py 2012-03-02 17:13:04 +0000
1202+++ ubuntuone/controlpanel/gui/qt/wizard.py 2012-03-19 14:00:47 +0000
1203@@ -19,26 +19,82 @@
1204 from PyQt4 import QtGui, QtCore
1205
1206 from twisted.internet import defer
1207+from ubuntu_sso.qt import LINK_STYLE
1208+from ubuntu_sso.qt.sso_wizard_page import BaseWizardPage
1209 from ubuntu_sso.utils.ui import CLOSE_AND_SETUP_LATER
1210
1211 from ubuntuone.controlpanel import cache
1212+from ubuntuone.controlpanel.logger import log_call, setup_logging
1213+from ubuntuone.controlpanel.gui import (
1214+ APP_NAME,
1215+ ARE_YOU_SURE_HELP,
1216+ ARE_YOU_SURE_NO,
1217+ ARE_YOU_SURE_SUBTITLE,
1218+ ARE_YOU_SURE_TITLE,
1219+ ARE_YOU_SURE_YES,
1220+ CLOUD_TO_COMPUTER_SUBTITLE,
1221+ CLOUD_TO_COMPUTER_TITLE,
1222+ UBUNTUONE_LINK,
1223+)
1224+from ubuntuone.controlpanel.gui.qt.folders import (
1225+ RemoteFoldersPanel,
1226+)
1227+from ubuntuone.controlpanel.gui.qt.preferences import PreferencesPanel
1228 from ubuntuone.controlpanel.gui.qt.signin import SignInPanel
1229 from ubuntuone.controlpanel.gui.qt.side_widget import SideWidget
1230-
1231-
1232-class UbuntuOnePage(QtGui.QWizardPage):
1233+from ubuntuone.controlpanel.gui.qt.ui import are_you_sure_ui
1234+
1235+
1236+logger = setup_logging('qt.wizard')
1237+
1238+
1239+class AreYouSure(QtGui.QDialog):
1240+
1241+ """A 'Are you sure?' dialog."""
1242+
1243+ def __init__(self, *args, **kwargs):
1244+ super(AreYouSure, self).__init__(*args, **kwargs)
1245+ self.ui = are_you_sure_ui.Ui_Dialog()
1246+ self.ui.setupUi(self)
1247+ self.setWindowTitle(APP_NAME)
1248+
1249+ self.ui.title_label.setText(ARE_YOU_SURE_TITLE)
1250+
1251+ link = LINK_STYLE.format(link_url=UBUNTUONE_LINK,
1252+ link_text=UBUNTUONE_LINK)
1253+ msg = u'%s<p>%s' % (ARE_YOU_SURE_SUBTITLE,
1254+ ARE_YOU_SURE_HELP.format(support_url=link))
1255+ self.ui.message_label.setText(msg)
1256+
1257+ self.ui.yes_button.setText(ARE_YOU_SURE_YES)
1258+ self.ui.no_button.setText(ARE_YOU_SURE_NO)
1259+
1260+
1261+class UbuntuOnePage(BaseWizardPage):
1262 """A generic page for the UbuntuOneWizard."""
1263
1264- panel_class = None
1265+ is_final = False
1266+ main_title = None
1267+ max_width = 5000
1268+ panel_class = QtGui.QFrame
1269+ sub_title = None
1270
1271 def __init__(self, *args, **kwargs):
1272 super(UbuntuOnePage, self).__init__(*args, **kwargs)
1273
1274- self.layout = QtGui.QVBoxLayout(self)
1275 self.panel = None
1276 if self.panel_class is not None:
1277- self.panel = SignInPanel()
1278- self.layout.addWidget(self.panel)
1279+ self.panel = self.panel_class()
1280+ self.layout().addWidget(self.panel)
1281+
1282+ if self.main_title is not None:
1283+ self.setTitle(self.main_title)
1284+
1285+ if self.sub_title is not None:
1286+ self.setSubTitle(self.sub_title)
1287+
1288+ self.form_errors_label.hide()
1289+ self.setFinalPage(self.is_final)
1290
1291
1292 class SignInPage(UbuntuOnePage):
1293@@ -47,9 +103,30 @@
1294 panel_class = SignInPanel
1295
1296
1297+class CloudToComputerPage(UbuntuOnePage):
1298+ """The page to choose cloud folders to sync locally."""
1299+
1300+ main_title = CLOUD_TO_COMPUTER_TITLE
1301+ panel_class = RemoteFoldersPanel
1302+ sub_title = CLOUD_TO_COMPUTER_SUBTITLE
1303+ is_final = True
1304+
1305+ def __init__(self, *args, **kwargs):
1306+ super(CloudToComputerPage, self).__init__(*args, **kwargs)
1307+ self.panel.ui.add_folder_button.hide()
1308+
1309+
1310+class SettingsPage(UbuntuOnePage):
1311+ """The page to adjust the service settings."""
1312+
1313+ panel_class = PreferencesPanel
1314+
1315+
1316 class UbuntuOneWizard(cache.Cache, QtGui.QWizard):
1317 """The Ubuntu One wizard."""
1318
1319+ show_license = False # do not change unless you know what you're doing
1320+
1321 def __init__(self, *args, **kwargs):
1322 super(UbuntuOneWizard, self).__init__(*args, **kwargs)
1323 self.pages = {}
1324@@ -58,13 +135,13 @@
1325 self.setOption(self.HaveFinishButtonOnEarlyPages, False)
1326 self.setWizardStyle(self.ModernStyle)
1327
1328- self.setButtonText(self.CancelButton, CLOSE_AND_SETUP_LATER)
1329- self.setButtonLayout([self.Stretch, self.CancelButton])
1330+ self.confirm_dialog = AreYouSure(self)
1331
1332 self.side_widget = SideWidget()
1333 self.side_widget.stage = self.side_widget.signin_stage
1334 self.setSideWidget(self.side_widget)
1335
1336+ # sign in
1337 self.signin_page = SignInPage()
1338 self.addPage(self.signin_page)
1339
1340@@ -72,10 +149,19 @@
1341 self.signin_page.panel.ui.register_button.clicked.connect(
1342 self.register)
1343
1344- self.setTabOrder(self.signin_page.panel.ui.login_button,
1345- self.signin_page.panel.ui.register_button)
1346- self.setTabOrder(self.signin_page.panel.ui.register_button,
1347- self.button(self.CancelButton))
1348+ # cloud to compuer
1349+ self.cloud_folders_page = CloudToComputerPage()
1350+ self.addPage(self.cloud_folders_page)
1351+
1352+ self.cloud_folders_page.panel.ui.check_settings_button.clicked.connect(
1353+ self.check_settings)
1354+
1355+ # settings
1356+ self.settings_page = SettingsPage()
1357+ self.addPage(self.settings_page)
1358+
1359+ self._next_id = self.pages[self.signin_page]
1360+ self.next()
1361
1362 # pylint: disable=C0103
1363
1364@@ -84,8 +170,67 @@
1365 page_id = super(UbuntuOneWizard, self).addPage(page)
1366 self.pages[page] = page_id
1367
1368+ @log_call(logger.info)
1369+ def initializePage(self, page_id):
1370+ """The wizard will show the page 'page_id'."""
1371+ page = self.page(page_id)
1372+ logger.debug('UbuntuOneWizard.initializePage: page is %r.', page)
1373+
1374+ button_layout = button_to = button = stage = None
1375+
1376+ if page is self.signin_page:
1377+ self.setButtonText(self.CancelButton, CLOSE_AND_SETUP_LATER)
1378+
1379+ button_layout = [self.Stretch, self.CancelButton]
1380+ button = self.signin_page.panel.ui.register_button
1381+ button_to = self.button(self.CancelButton)
1382+ stage = self.side_widget.signin_stage
1383+ self._next_id = self.pages[self.cloud_folders_page]
1384+ elif page is self.cloud_folders_page:
1385+ button_layout = [self.Stretch, self.FinishButton]
1386+ button = self.cloud_folders_page.panel.ui.check_settings_button
1387+ button_to = self.button(self.FinishButton)
1388+ stage = self.side_widget.folders_stage
1389+ elif page is self.settings_page:
1390+ button_layout = [self.BackButton, self.Stretch]
1391+ button = self.settings_page.panel.ui.apply_changes_button
1392+ button_to = self.button(self.BackButton)
1393+ stage = self.side_widget.folders_stage
1394+ self._next_id = self.pages[self.cloud_folders_page]
1395+ else:
1396+ logger.error('UbuntuOneWizard.initializePage was called with an'
1397+ 'unknown page: %r (page_id was %r).', page, page_id)
1398+
1399+ logger.info('UbuntuOneWizard.initializePage: new page is %r, '
1400+ 'new button layout is %r, '
1401+ 'new side widget stage is %r.', page, button_layout, stage)
1402+
1403+ if button is not None and button_to is not None:
1404+ self.setTabOrder(button, button_to)
1405+ if button_layout is not None:
1406+ self.setButtonLayout(button_layout)
1407+ if stage is not None:
1408+ self.side_widget.stage = stage
1409+
1410+ @log_call(logger.info)
1411+ def cleanupPage(self, page_id):
1412+ """Called clean up 'page_id' just before the user leaves it."""
1413+ page = self.page(page_id)
1414+ logger.debug('UbuntuOneWizard.cleanupPage: page is %r.', page)
1415+ if page is self.settings_page:
1416+ self.initializePage(self.pages[self.cloud_folders_page])
1417+
1418+ def nextId(self):
1419+ """Return the nextId to show."""
1420+ return self._next_id
1421+
1422 # pylint: enable=C0103
1423
1424+ def _process_credentials(self, credentials=None):
1425+ """Confirm which is the next step after analyzing 'credentials'."""
1426+ if credentials:
1427+ self.next()
1428+
1429 @QtCore.pyqtSlot()
1430 @defer.inlineCallbacks
1431 def login(self):
1432@@ -104,7 +249,19 @@
1433 self._process_credentials(credentials)
1434 self.setEnabled(True)
1435
1436- def _process_credentials(self, credentials=None):
1437- """Confirm which is the next step after analyzing 'credentials'."""
1438- if credentials:
1439- self.accept()
1440+ @QtCore.pyqtSlot()
1441+ def check_settings(self):
1442+ """Show the check settings page."""
1443+ self._next_id = self.pages[self.settings_page]
1444+ self.next()
1445+
1446+ def done(self, result):
1447+ """The main window is being closed, call any custom callback."""
1448+ if (result == QtGui.QDialog.Rejected and
1449+ not self.page(self.currentId()).isFinalPage()):
1450+ response = self.confirm_dialog.exec_()
1451+ if response == QtGui.QDialog.Accepted:
1452+ logger.warning('UbuntuOneWizard: user canceled setup.')
1453+ self.rejected.emit()
1454+ else:
1455+ super(UbuntuOneWizard, self).done(result)

Subscribers

People subscribed via source and target branches