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

Proposed by Natalia Bidart on 2012-03-16
Status: Merged
Approved by: Natalia Bidart on 2012-03-19
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 on 2012-03-19
Eric Casteleijn (community) 2012-03-16 Approve on 2012-03-16
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.
Eric Casteleijn (thisfred) wrote :

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

review: Approve
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.

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.

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?

Roberto Alsina (ralsina) wrote :

+1 great work!

review: Approve
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...

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...

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...

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...

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