Qt::Horizontal
=== modified file 'ubuntuone/controlpanel/gui/__init__.py'
--- ubuntuone/controlpanel/gui/__init__.py 2012-02-29 21:33:02 +0000
+++ ubuntuone/controlpanel/gui/__init__.py 2012-03-19 14:00:47 +0000
@@ -18,6 +18,10 @@
import gettext
+# pylint: disable=W0611
+from ubuntuone.clientdefs import APP_NAME
+# pylint: enable=W0611
+
from ubuntuone.controlpanel import TRANSLATION_DOMAIN
from ubuntuone.controlpanel.backend import UBUNTUONE_LINK
@@ -78,6 +82,17 @@
ACCOUNT_LABEL = _('Your services')
ALWAYS_SUBSCRIBED = _('Always in sync')
+ARE_YOU_SURE_HELP = _('If you need to know more about Ubuntu One, then '
+ 'please go to {support_url}')
+ARE_YOU_SURE_NO = _('No, continue setting up')
+ARE_YOU_SURE_SUBTITLE = _('You can restart the setup process at any time '
+ 'by clicking on Ubuntu One in your menu.')
+ARE_YOU_SURE_TITLE = _('Are you sure you want to cancel setting up '
+ 'Ubuntu One?')
+ARE_YOU_SURE_YES = _('Yes, I want to cancel')
+CLOUD_TO_COMPUTER_SUBTITLE = _('These are the folders in your cloud. '
+ 'Select the ones you want to sync with this computer.')
+CLOUD_TO_COMPUTER_TITLE = _('Syncing the cloud to your computer')
CONNECT_BUTTON_LABEL = _('Connect to Ubuntu One')
CONTACTS = _('Thunderbird plug-in')
CREDENTIALS_ERROR = _('There was a problem while retrieving the credentials.')
=== modified file 'ubuntuone/controlpanel/gui/qt/controlpanel.py'
--- ubuntuone/controlpanel/gui/qt/controlpanel.py 2012-03-14 20:02:25 +0000
+++ ubuntuone/controlpanel/gui/qt/controlpanel.py 2012-03-19 14:00:47 +0000
@@ -159,10 +159,12 @@
"""The facebook button was clicked."""
qt.uri_hook(FACEBOOK_LINK)
+ @log_call(logger.warning)
def on_wizard_rejected(self):
"""Let clients know that we're done."""
self.finished.emit()
+ @log_call(logger.info)
def on_wizard_finished(self, status):
- """Move to controlpanel if wizar ended successfully."""
+ """Move to controlpanel if wizard ended successfully."""
self.on_credentials_found()
=== modified file 'ubuntuone/controlpanel/gui/qt/folders.py'
--- ubuntuone/controlpanel/gui/qt/folders.py 2012-03-12 20:31:06 +0000
+++ ubuntuone/controlpanel/gui/qt/folders.py 2012-03-19 14:00:47 +0000
@@ -42,7 +42,11 @@
NAME_NOT_SET,
SHARE_ICON_NAME,
)
-from ubuntuone.controlpanel.gui.qt import uri_hook, icon_from_name
+from ubuntuone.controlpanel.gui.qt import (
+ handle_errors,
+ icon_from_name,
+ uri_hook,
+)
from ubuntuone.controlpanel.gui.qt.ubuntuonebin import UbuntuOneBin
from ubuntuone.controlpanel.gui.qt.ui import folders_ui
@@ -61,6 +65,15 @@
YES = QtGui.QMessageBox.Yes
+def _process_name(name):
+ """Tweak 'name' with a translatable music folder name."""
+ if name == MUSIC_REAL_PATH:
+ result = MUSIC_DISPLAY_NAME
+ else:
+ result = name
+ return result
+
+
class ExploreFolderButton(QtGui.QPushButton):
"""A specialized button for the folder listing."""
@@ -77,10 +90,11 @@
class FoldersPanel(UbuntuOneBin):
- """The Folders Tab Panel widget"""
+ """The Folders Tab Panel widget."""
+ logger = logger
+ remote_folders = False
ui_class = folders_ui
- logger = logger
widget_items = {}
def _setup(self):
@@ -89,14 +103,20 @@
self.ui.add_folder_button.folderCreated.connect(self.on_folder_created)
self.ui.add_folder_button.setText(FOLDERS_BUTTON_ADD_FOLDER)
- self.ui.folders.headerItem().setText(0, FOLDERS_COLUMN_NAME)
- self.ui.folders.headerItem().setText(1, FOLDERS_COLUMN_SYNC_LOCALLY)
- self.ui.folders.headerItem().setText(2, FOLDERS_COLUMN_EXPLORE)
+ self.ui.share_publish_button.setVisible(not self.remote_folders)
+ self.ui.add_folder_button.setVisible(not self.remote_folders)
+ self.ui.check_settings_button.setVisible(self.remote_folders)
+
+ self.ui.folders.headerItem().setText(FOLDER_NAME_COL,
+ FOLDERS_COLUMN_NAME)
+ self.ui.folders.headerItem().setText(SUBSCRIPTION_COL,
+ FOLDERS_COLUMN_SYNC_LOCALLY)
+ self.ui.folders.headerItem().setText(EXPLORE_COL,
+ FOLDERS_COLUMN_EXPLORE)
headers = self.ui.folders.header()
headers.setResizeMode(FOLDER_NAME_COL, headers.Stretch)
headers.setResizeMode(SUBSCRIPTION_COL, headers.ResizeToContents)
headers.setResizeMode(EXPLORE_COL, headers.ResizeToContents)
- headers.setStretchLastSection(False)
self.ui.share_publish_button.setText(FOLDERS_MANAGE_LABEL)
self.ui.share_publish_button.uri = MANAGE_FILES_LINK
@@ -125,14 +145,7 @@
info = yield self.backend.volumes_info(with_storage_info=False)
self.process_info(info)
- def _process_name(self, name):
- """Tweak 'name' with a translatable music folder name."""
- if name == MUSIC_REAL_PATH:
- result = MUSIC_DISPLAY_NAME
- else:
- result = name
- return result
-
+ @handle_errors(logger=logger)
@log_call(logger.debug)
def process_info(self, info):
"""Load folders info into the tree view."""
@@ -164,6 +177,8 @@
self.ui.folders.addTopLevelItem(item)
for volume in volumes:
+ subscribed = bool(volume[u'subscribed'])
+
is_root = volume[u'type'] == self.backend.ROOT_TYPE
is_share = volume[u'type'] == self.backend.SHARE_TYPE
@@ -175,7 +190,7 @@
child.volume_path = volume['path']
child.volume_id = volume['volume_id']
- name = self._process_name(volume[u'display_name'])
+ name = _process_name(volume[u'display_name'])
child.setText(FOLDER_NAME_COL, name)
child.setToolTip(FOLDER_NAME_COL, name)
child.setToolTip(EXPLORE_COL, FOLDERS_COLUMN_EXPLORE)
@@ -199,7 +214,7 @@
# issues.
checkbox = QtGui.QCheckBox(parent=self.ui.folders)
self.widget_items[checkbox] = child
- if bool(volume[u'subscribed']):
+ if subscribed:
checkbox.setCheckState(CHECKED)
else:
checkbox.setCheckState(UNCHECKED)
@@ -217,11 +232,15 @@
checkbox.stateChanged.connect(cb)
+ if self.remote_folders:
+ # no explore button when showing only remote folders
+ continue
+
# attach a third item with a button to explore the folder
button = ExploreFolderButton(folder_path=child.volume_path,
parent=self.ui.folders)
self.widget_items[button] = child
- button.setEnabled(bool(volume[u'subscribed']))
+ button.setEnabled(subscribed)
self.ui.folders.setItemWidget(child, EXPLORE_COL, button)
self.ui.folders.expandAll()
@@ -247,6 +266,7 @@
# Invalid name "on_folders_itemActivated", "on_folders_itemChanged"
# pylint: disable=C0103
+ @handle_errors(logger=logger)
def on_folders_itemActivated(self, item, column=None):
"""User activated a given row, open the path in a file browser."""
volume_path = getattr(item, 'volume_path', None)
@@ -260,6 +280,7 @@
uri = unicode(QtCore.QUrl.fromLocalFile(volume_path).toString())
uri_hook(uri)
+ @handle_errors(logger=logger)
@defer.inlineCallbacks
def on_folders_itemChanged(self, item, column=None):
"""User changed the subscription for a given folder."""
@@ -298,6 +319,12 @@
else:
# restore old value
old = UNCHECKED if subscribed else CHECKED
- item.setCheckState(SUBSCRIPTION_COL, old)
+ checkbox.setCheckState(old)
self.is_processing = False
+
+
+class RemoteFoldersPanel(FoldersPanel):
+ """The Folders Panel that only shows remote cloud folders."""
+
+ remote_folders = True
=== modified file 'ubuntuone/controlpanel/gui/qt/tests/test_controlpanel.py'
--- ubuntuone/controlpanel/gui/qt/tests/test_controlpanel.py 2012-03-12 16:53:02 +0000
+++ ubuntuone/controlpanel/gui/qt/tests/test_controlpanel.py 2012-03-19 14:00:47 +0000
@@ -181,6 +181,11 @@
self.assertNotIn('connect_files', self.ui.backend._called)
+ def test_folder_panel_shows_all_folders(self):
+ """The FolderPanel shows all folders (not remote only)."""
+ remote = self.ui.ui.folders_tab.remote_folders
+ self.assertFalse(remote)
+
class ExternalLinkButtonsTestCase(ControlPanelTestCase):
"""The link in the go-to-web buttons are correct."""
=== modified file 'ubuntuone/controlpanel/gui/qt/tests/test_folders.py'
--- ubuntuone/controlpanel/gui/qt/tests/test_folders.py 2012-03-12 20:31:06 +0000
+++ ubuntuone/controlpanel/gui/qt/tests/test_folders.py 2012-03-19 14:00:47 +0000
@@ -16,6 +16,7 @@
"""Tests for the Control Panel."""
+import copy
import logging
import operator
import os
@@ -44,6 +45,13 @@
# pylint: disable=W0212, E1103
+def volumes_with_music_unsubscribed():
+ """Return a copy of FAKE_VOLUMES_MINIMAL_INFO with music unsubscribed."""
+ volumes = copy.deepcopy(FAKE_VOLUMES_MINIMAL_INFO)
+ volumes[0][2][1][u'subscribed'] = u''
+ return volumes
+
+
def _build_name(name):
"""Helper to build the name expected when showing folder info."""
if name:
@@ -91,9 +99,15 @@
self.memento.setLevel(logging.DEBUG)
gui.logger.addHandler(self.memento)
- old_home = os.environ['HOME']
- os.environ['HOME'] = USER_HOME
- self.addCleanup(lambda: os.environ.__setitem__('HOME', old_home))
+ def set_item_checked(self, item, checked=True):
+ """Make item to be checked."""
+ checkbox = self.ui.ui.folders.itemWidget(item, gui.SUBSCRIPTION_COL)
+ checkbox.setCheckState(gui.CHECKED if checked else gui.UNCHECKED)
+
+ def get_item_checked(self, item):
+ """Get if item is checked."""
+ checkbox = self.ui.ui.folders.itemWidget(item, gui.SUBSCRIPTION_COL)
+ return (checkbox.checkState() == gui.CHECKED)
class FoldersPanelVolumesInfoTestCase(FoldersPanelTestCase):
@@ -109,7 +123,45 @@
self.assertEqual(unicode(item.text(gui.FOLDER_NAME_COL)), name)
self.assertEqual(unicode(item.text(gui.SUBSCRIPTION_COL)),
gui.FOLDERS_COLUMN_SYNC_LOCALLY)
- self.assertEqual(unicode(item.text(gui.EXPLORE_COL)), '')
+ if not self.ui.remote_folders:
+ self.assertEqual(unicode(item.text(gui.EXPLORE_COL)), '')
+
+ def assert_folder_row_correct(self, item, label, icon_name, volume,
+ tweaked_path=None):
+ """Check that the folder row 'item' is correct."""
+ folders = self.ui.ui.folders
+
+ actual_label = unicode(item.text(gui.FOLDER_NAME_COL))
+ self.assertEqual(label, actual_label)
+
+ if volume['type'] == self.ui.backend.ROOT_TYPE:
+ # no check box but the ALWAYS_SUBSCRIBED legend
+ self.assertEqual(unicode(item.text(gui.SUBSCRIPTION_COL)),
+ gui.ALWAYS_SUBSCRIBED)
+ else:
+ subscribed = self.get_item_checked(item)
+ self.assertEqual(subscribed, bool(volume['subscribed']))
+
+ actual_icon_name = item.icon_obj.icon_name
+ self.assertEqual(icon_name, actual_icon_name)
+
+ self.assertEqual(item.volume_id, volume['volume_id'])
+
+ expected_path = volume['path']
+ if tweaked_path is not None:
+ expected_path = tweaked_path
+ self.assertEqual(item.volume_path, expected_path)
+
+ # tooltips are correct
+ self.assertEqual(item.toolTip(gui.FOLDER_NAME_COL), label)
+ self.assertEqual(item.toolTip(gui.EXPLORE_COL),
+ gui.FOLDERS_COLUMN_EXPLORE)
+
+ if not self.ui.remote_folders:
+ # explore button is in place
+ model_index = folders.indexFromItem(item, gui.EXPLORE_COL)
+ button = folders.indexWidget(model_index)
+ self.assertEqual(button.isEnabled(), bool(volume['subscribed']))
@defer.inlineCallbacks
def test_is_processing_while_asking_info(self):
@@ -151,7 +203,6 @@
self.assert_folder_group_header_correct(item, name)
- # check children
self.assertEqual(len(volumes), item.childCount())
sorted_vols = sorted(volumes, key=operator.itemgetter('path'))
for volume in sorted_vols:
@@ -164,38 +215,14 @@
if volume['type'] == self.ui.backend.SHARE_TYPE:
name = volume['name']
expected_path = volume['realpath']
- label = unicode(item.text(gui.FOLDER_NAME_COL))
- self.assertEqual(label, name)
-
- if volume['type'] == self.ui.backend.ROOT_TYPE:
- # no check box but the ALWAYS_SUBSCRIBED legend
- self.assertEqual(unicode(item.text(gui.SUBSCRIPTION_COL)),
- gui.ALWAYS_SUBSCRIBED)
- else:
- checkbox = self.ui.ui.folders.itemWidget(
- item, gui.SUBSCRIPTION_COL)
- subscribed = checkbox.checkState() == gui.CHECKED
- self.assertEqual(subscribed, bool(volume['subscribed']))
-
- icon_name = item.icon_obj.icon_name
+
if volume['type'] != self.ui.backend.SHARE_TYPE:
- self.assertEqual(icon_name, gui.FOLDER_ICON_NAME)
+ icon_name = gui.FOLDER_ICON_NAME
else:
- self.assertEqual(icon_name, gui.SHARE_ICON_NAME)
-
- self.assertEqual(item.volume_id, volume['volume_id'])
- self.assertEqual(item.volume_path, expected_path)
-
- # tooltips are correct
- self.assertEqual(item.toolTip(gui.FOLDER_NAME_COL), name)
- self.assertEqual(item.toolTip(gui.EXPLORE_COL),
- gui.FOLDERS_COLUMN_EXPLORE)
-
- # explore button is in place
- model_index = folders.indexFromItem(item, gui.EXPLORE_COL)
- button = folders.indexWidget(model_index)
- self.assertEqual(button.isEnabled(),
- bool(volume['subscribed']))
+ icon_name = gui.SHARE_ICON_NAME
+
+ self.assert_folder_row_correct(item, name, icon_name, volume,
+ tweaked_path=expected_path)
treeiter += 1
item = treeiter.value()
@@ -304,9 +331,12 @@
self.assertTrue(self.memento.check_warning(path, 'does not exist'))
self.assertEqual(self._called, False)
- def test_process_info_with_music_folder(self):
+ def test_process_info_with_music_folder(self, volumes=None):
"""The volumes info is processed when ready."""
- self.ui.process_info(FAKE_VOLUMES_MINIMAL_INFO)
+ if volumes is None:
+ volumes = FAKE_VOLUMES_MINIMAL_INFO
+
+ self.ui.process_info(volumes)
folders = self.ui.ui.folders
treeiter = gui.QtGui.QTreeWidgetItemIterator(folders)
@@ -318,46 +348,44 @@
treeiter += 1
item = treeiter.value()
- volume = MUSIC_FOLDER
-
- label = unicode(item.text(gui.FOLDER_NAME_COL))
- self.assertEqual(label, gui.MUSIC_DISPLAY_NAME)
-
- checkbox = self.ui.ui.folders.itemWidget(item, gui.SUBSCRIPTION_COL)
- subscribed = checkbox.checkState() == gui.CHECKED
- self.assertEqual(subscribed, bool(volume['subscribed']))
-
- icon_name = item.icon_obj.icon_name
- self.assertEqual(icon_name, gui.MUSIC_ICON_NAME)
-
- self.assertEqual(item.volume_id, volume['volume_id'])
- self.assertEqual(item.volume_path, volume['path'])
-
- def test_share_publish_button(self):
- """When clicking the share/publish button, the proper url is opened."""
- self.assert_uri_hook_called(self.ui.ui.share_publish_button,
- gui.MANAGE_FILES_LINK)
+ volume = volumes[0][2][1]
+
+ self.assert_folder_row_correct(item, gui.MUSIC_DISPLAY_NAME,
+ gui.MUSIC_ICON_NAME, volume)
def test_focus_order(self):
"""Ensure that the inner widgets are in the correct tab order."""
self.ui.process_info(FAKE_VOLUMES_INFO)
- # First, assert we are jumping from ui.folders to an
- # QPushButton
+ folders = self.ui.ui.folders
+
widget = self.ui.ui.folders.nextInFocusChain()
- self.assertIsInstance(widget, QtGui.QPushButton)
- # Then, assert it's the right one
- self.assertEqual(unicode(
- self.ui.widget_items[widget].text(0)), u"My Ubuntu")
- # Next are a checkbox / pushbutton pair
- # in the 'bar' item
- widget = widget.nextInFocusChain()
- self.assertIsInstance(widget, QtGui.QCheckBox)
- self.assertEqual(unicode(
- self.ui.widget_items[widget].text(0)), u"bar")
- widget = widget.nextInFocusChain()
- self.assertIsInstance(widget, QtGui.QPushButton)
- self.assertEqual(unicode(
- self.ui.widget_items[widget].text(0)), u"bar")
+ treeiter = gui.QtGui.QTreeWidgetItemIterator(folders)
+ for name, _, volumes in FAKE_VOLUMES_INFO:
+ item = treeiter.value()
+ sorted_vols = sorted(volumes, key=operator.itemgetter('path'))
+ for volume in sorted_vols:
+ treeiter += 1
+ item = treeiter.value() # get child folder
+
+ name = volume['path'].replace(USER_HOME + os.path.sep, '')
+ if volume['type'] == self.ui.backend.SHARE_TYPE:
+ name = volume['name']
+ self.assertEqual(unicode(item.text(gui.FOLDER_NAME_COL)), name)
+
+ if volume['type'] != self.ui.backend.ROOT_TYPE:
+ self.assertIsInstance(widget, QtGui.QCheckBox)
+ self.assertEqual(unicode(
+ self.ui.widget_items[widget].text(0)), name)
+ widget = widget.nextInFocusChain()
+
+ if not self.ui.remote_folders:
+ self.assertIsInstance(widget, QtGui.QPushButton)
+ self.assertEqual(unicode(
+ self.ui.widget_items[widget].text(0)), name)
+ widget = widget.nextInFocusChain()
+
+ treeiter += 1
+ item = treeiter.value()
def test_widget_dict(self):
"""Ensure the widget_items dictionary is full."""
@@ -377,6 +405,20 @@
item)
it += 1
+ def test_share_publish_button(self):
+ """When clicking the share/publish button, the proper url is opened."""
+ self.assertTrue(self.ui.ui.share_publish_button.isVisible())
+ self.assert_uri_hook_called(self.ui.ui.share_publish_button,
+ gui.MANAGE_FILES_LINK)
+
+ def test_add_folder_button(self):
+ """The 'add_folder_button' is visible by default."""
+ self.assertTrue(self.ui.ui.add_folder_button.isVisible())
+
+ def test_check_settings_button(self):
+ """The 'check_settings_button' is not visible by default."""
+ self.assertFalse(self.ui.ui.check_settings_button.isVisible())
+
class FoldersPanelAddFolderTestCase(FoldersPanelTestCase):
"""The test suite for the folder creation from a local dir."""
@@ -411,16 +453,25 @@
class FoldersPanelSubscriptionTestCase(FoldersPanelTestCase):
"""The test suite for the folder subscription."""
+ faked_volumes = FAKE_VOLUMES_MINIMAL_INFO
+
@defer.inlineCallbacks
def setUp(self):
yield super(FoldersPanelSubscriptionTestCase, self).setUp()
self.patch(gui.os.path, 'exists', lambda path: True)
FakedDialog.response = gui.YES
- self.ui.process_info(FAKE_VOLUMES_MINIMAL_INFO)
+ self.ui.process_info(self.faked_volumes)
# the music folder
self.item = self.ui.ui.folders.topLevelItem(0).child(1)
+ def set_item_checked(self, item=None, checked=True):
+ """Make item to be checked."""
+ if item is None:
+ item = self.item
+ test = super(FoldersPanelSubscriptionTestCase, self).set_item_checked
+ test(item=item, checked=checked)
+
@defer.inlineCallbacks
def test_on_folders_item_changed(self):
"""Clicking on 'subscribed' updates the folder subscription."""
@@ -428,12 +479,9 @@
volume = MUSIC_FOLDER
fid = volume['volume_id']
subscribed = not bool(volume['subscribed'])
- check_state = gui.CHECKED if subscribed else gui.UNCHECKED
self.ui.is_processing = True
- checkbox = self.ui.ui.folders.itemWidget(self.item,
- gui.SUBSCRIPTION_COL)
- checkbox.setCheckState(check_state)
+ self.set_item_checked(self.item, subscribed)
self.ui.is_processing = False
yield self.ui.on_folders_itemChanged(self.item)
@@ -442,9 +490,7 @@
self.assert_backend_called('change_volume_settings',
fid, {'subscribed': subscribed})
- checkbox = self.ui.ui.folders.itemWidget(self.item,
- gui.SUBSCRIPTION_COL)
- value = checkbox.checkState() == gui.CHECKED
+ value = self.get_item_checked(self.item)
self.assertEqual(value, bool(subscribed))
# folder list was reloaded
@@ -494,7 +540,7 @@
# make sure the item is subscribed
self.ui.is_processing = True
- self.item.setCheckState(gui.SUBSCRIPTION_COL, gui.CHECKED)
+ self.set_item_checked()
self.ui.is_processing = False
yield self.ui.on_folders_itemChanged(self.item)
@@ -513,7 +559,7 @@
# make sure the item is unsubscribed
self.ui.is_processing = True
- self.item.setCheckState(gui.SUBSCRIPTION_COL, gui.UNCHECKED)
+ self.set_item_checked()
self.ui.is_processing = False
yield self.ui.on_folders_itemChanged(self.item)
@@ -528,7 +574,7 @@
# make sure the item is subscribed
self.ui.is_processing = True
- self.item.setCheckState(gui.SUBSCRIPTION_COL, gui.CHECKED)
+ self.set_item_checked()
self.ui.is_processing = False
yield self.ui.on_folders_itemChanged(self.item)
@@ -537,8 +583,7 @@
self.assertNotIn('change_volume_settings', self.ui.backend._called)
self.assertFalse(self.ui.is_processing)
- subscribed = self.item.checkState(gui.SUBSCRIPTION_COL) == gui.CHECKED
- self.assertEqual(subscribed, False)
+ self.assertFalse(self.get_item_checked(self.item))
@defer.inlineCallbacks
def test_subscribe_does_not_call_backend_if_answer_is_no(self):
@@ -547,7 +592,7 @@
# make sure the item is subscribed
self.ui.is_processing = True
- self.item.setCheckState(gui.SUBSCRIPTION_COL, gui.CHECKED)
+ self.set_item_checked()
self.ui.is_processing = False
yield self.ui.on_folders_itemChanged(self.item)
@@ -556,17 +601,14 @@
self.assertNotIn('change_volume_settings', self.ui.backend._called)
self.assertFalse(self.ui.is_processing)
- subscribed = self.item.checkState(gui.SUBSCRIPTION_COL) == gui.CHECKED
- self.assertEqual(subscribed, False)
+ self.assertFalse(self.get_item_checked(self.item))
@defer.inlineCallbacks
def test_no_confirmation_if_unsubscribing(self):
"""The confirmation dialog is not shown if unsubscribing."""
# make sure the item is unsubscribed
self.ui.is_processing = True
- checkbox = self.ui.ui.folders.itemWidget(
- self.item, gui.SUBSCRIPTION_COL)
- checkbox.setCheckState(gui.UNCHECKED)
+ self.set_item_checked(checked=False)
self.ui.is_processing = False
# the confirm dialog was not called so far
@@ -577,3 +619,34 @@
self.assertTrue(FakedDialog.args is None, 'dialog was not run')
self.assertTrue(FakedDialog.kwargs is None, 'dialog was hid')
+
+
+class RemoteFoldersPanelTestCase(FoldersPanelVolumesInfoTestCase):
+ """The test case for the RemoteFoldersPanel widget."""
+
+ class_ui = gui.RemoteFoldersPanel
+
+ def test_process_info_with_music_folder(self, volumes=None):
+ """The volumes info is processed when ready."""
+ volumes = volumes_with_music_unsubscribed()
+ parent = super(RemoteFoldersPanelTestCase, self)
+ parent.test_process_info_with_music_folder(volumes=volumes)
+
+ def test_share_publish_button(self):
+ """When clicking the share/publish button, the proper url is opened."""
+ self.assertFalse(self.ui.ui.share_publish_button.isVisible())
+
+ def test_add_folder_button(self):
+ """The 'add_folder_button' is not visible by default."""
+ self.assertFalse(self.ui.ui.add_folder_button.isVisible())
+
+ def test_check_settings_button(self):
+ """The 'check_settings_button' is visible by default."""
+ self.assertTrue(self.ui.ui.check_settings_button.isVisible())
+
+
+class RemoteFoldersPanelSubscriptionTestCase(FoldersPanelSubscriptionTestCase):
+ """The test suite for the remote folder subscription."""
+
+ class_ui = gui.RemoteFoldersPanel
+ faked_volumes = volumes_with_music_unsubscribed()
=== modified file 'ubuntuone/controlpanel/gui/qt/tests/test_wizard.py'
--- ubuntuone/controlpanel/gui/qt/tests/test_wizard.py 2012-03-12 16:53:02 +0000
+++ ubuntuone/controlpanel/gui/qt/tests/test_wizard.py 2012-03-19 14:00:47 +0000
@@ -22,10 +22,121 @@
from ubuntuone.controlpanel.gui.qt.tests import BaseTestCase, TOKEN
+BUTTONS = [
+ 'BackButton', 'CancelButton', 'CommitButton',
+ 'CustomButton1', 'CustomButton2', 'CustomButton3',
+ 'FinishButton', 'HelpButton', 'NextButton',
+]
+
+
+class AreYouSureTestCase(BaseTestCase):
+ """Test suite for the "Are you sure?" dialog."""
+
+ class_ui = gui.AreYouSure
+
+ def test_title(self):
+ """Check the window title."""
+ self.assertEqual(self.ui.windowTitle(), gui.APP_NAME)
+ self.assertEqual(self.ui.ui.title_label.text(), gui.ARE_YOU_SURE_TITLE)
+
+ def test_message_label(self):
+ """Check the message label text."""
+ link = gui.LINK_STYLE.format(link_url=gui.UBUNTUONE_LINK,
+ link_text=gui.UBUNTUONE_LINK)
+ msg = u'%s%s' % (gui.ARE_YOU_SURE_SUBTITLE,
+ gui.ARE_YOU_SURE_HELP.format(support_url=link))
+ self.assertEqual(unicode(self.ui.ui.message_label.text()), msg)
+
+ def test_buttons(self):
+ """The buttons have the correct text."""
+ self.assertEqual(self.ui.ui.yes_button.text(), gui.ARE_YOU_SURE_YES)
+ self.assertEqual(self.ui.ui.no_button.text(), gui.ARE_YOU_SURE_NO)
+
+
+class UbuntuOnePageTestCase(BaseTestCase):
+ """Test the UbuntuOnePage widget."""
+
+ class_ui = gui.UbuntuOnePage
+ main_title = ''
+ panel_class = gui.QtGui.QFrame
+ sub_title = ''
+
+ def test_panel_class(self):
+ """The panel_class is correct."""
+ self.assertIsInstance(self.ui.panel, self.panel_class)
+
+ def test_panel_is_created(self):
+ """The panel is properly added to the layout."""
+ self.assertEqual(self.ui.layout().itemAt(2).widget(), self.ui.panel)
+
+ def test_title(self):
+ """The title is correct."""
+ self.assertIn(self.main_title, self.ui.title()) # avoid markup
+
+ def test_subtitle(self):
+ """The subtitle is correct."""
+ self.assertEqual(self.ui.subTitle(), self.sub_title)
+
+ def test_error_label(self):
+ """The error label is hidden."""
+ self.assertFalse(self.ui.form_errors_label.isVisible())
+
+ def test_is_final(self):
+ """The page is not final."""
+ # from the doc:
+
+ # After calling setFinalPage(true), isFinalPage() returns true and the
+ # Finish button is visible (and enabled if isComplete() returns true).
+
+ # After calling setFinalPage(false), isFinalPage() returns true if
+ #nextId() returns -1; otherwise, it returns false.
+
+ self.patch(self.ui, 'nextId', lambda *a: 0)
+ self.assertFalse(self.ui.isFinalPage())
+
+
+class SignInPageTestCase(UbuntuOnePageTestCase):
+ """Test the SignInPage wizard page."""
+
+ class_ui = gui.SignInPage
+ panel_class = gui.SignInPanel
+
+
+class CloudToComputerPageTestCase(UbuntuOnePageTestCase):
+ """Test the CloudToComputerPage wizard page."""
+
+ class_ui = gui.CloudToComputerPage
+ main_title = gui.CLOUD_TO_COMPUTER_TITLE
+ panel_class = gui.RemoteFoldersPanel
+ sub_title = gui.CLOUD_TO_COMPUTER_SUBTITLE
+
+ def test_is_final(self):
+ """The page is not final."""
+ self.assertTrue(self.ui.isFinalPage())
+
+ def test_folder_panel_shows_remote_folders_only(self):
+ """The FolderPanel shows only remote folders."""
+ self.assertTrue(self.ui.panel.remote_folders)
+
+
+class SettingsPageTestCase(UbuntuOnePageTestCase):
+ """Test the SettingsPage wizard page."""
+
+ class_ui = gui.SettingsPage
+ panel_class = gui.PreferencesPanel
+
+
class UbuntuOneWizardTestCase(BaseTestCase):
- """Test the UbuntuOneWizard."""
+ """Test the UbuntuOneWizard widget."""
class_ui = gui.UbuntuOneWizard
+ confirm_response = gui.QtGui.QDialog.Accepted
+
+ @defer.inlineCallbacks
+ def setUp(self):
+ yield super(UbuntuOneWizardTestCase, self).setUp()
+ self.patch(self.ui.confirm_dialog, 'exec_',
+ lambda: self.confirm_response)
def test_options(self):
"""Tne wizard options are correct."""
@@ -40,59 +151,48 @@
"""Tne wizard style is Modern."""
self.assertEqual(self.ui.wizardStyle(), self.ui.ModernStyle)
- def test_cancel_button(self):
- """Send the rejected signal when the cancel button is clicked."""
- button = self.ui.button(self.ui.CancelButton)
- self.assertEqual(unicode(button.text()), gui.CLOSE_AND_SETUP_LATER)
-
- self.ui.rejected.connect(self._set_called)
- button.click()
-
- self.assertEqual(self._called, ((), {}))
-
- def test_button_layout(self):
- """The button layout is correct."""
- buttons = [
- 'BackButton', 'CommitButton', 'CustomButton1', 'CustomButton2',
- 'CustomButton3', 'FinishButton', 'HelpButton', 'NextButton',
- ]
- for button_name in buttons:
- button = self.ui.button(getattr(self.ui, button_name))
- self.assertFalse(button.isVisible(),
- 'Button %s should not be visible.' % button_name)
-
- button = self.ui.button(self.ui.CancelButton)
- self.assertTrue(button.isVisible(),
- 'Cancel button should not be visible.')
-
- def test_first_page(self):
- """The first page is the correct one."""
- self.assertEqual(self.ui.startId(),
- self.ui.pages[self.ui.signin_page])
-
- def test_tab_order(self):
- """The button tab order is correct."""
- # can not be tested due to Qt API limitations
-
-
-class SideWidgetTestCase(UbuntuOneWizardTestCase):
- """Test the side widget in the wizard."""
-
- def test_is_there(self):
+ def test_side_widget(self):
"""The side widget is correct."""
self.assertIsInstance(self.ui.side_widget, gui.SideWidget)
self.assertIs(self.ui.sideWidget(), self.ui.side_widget)
-
-class SignInPageTestCase(UbuntuOneWizardTestCase):
+ def test_confirm_dialog(self):
+ """The confirm dialog is correct."""
+ self.assertIsInstance(self.ui.confirm_dialog, gui.AreYouSure)
+
+ def test_first_page(self):
+ """The first page is the correct one."""
+ expected = self.ui.pages[self.ui.signin_page]
+ self.assertEqual(self.ui.startId(), expected)
+
+ def test_done_accepted(self):
+ """When the wizard reached the end, emit finished."""
+ self.ui.finished.connect(self._set_called)
+
+ self.ui.done(gui.QtGui.QDialog.Accepted)
+
+ self.assertEqual(self._called, ((gui.QtGui.QDialog.Accepted,), {}))
+
+
+class UbuntuOneWizardSignInTestCase(UbuntuOneWizardTestCase):
"""Test the SignInPage wizard page."""
+ buttons = {'CancelButton': (gui.CLOSE_AND_SETUP_LATER, 'rejected', ())}
page_name = 'signin'
+ stage_name = page_name
@defer.inlineCallbacks
def setUp(self):
- yield super(SignInPageTestCase, self).setUp()
+ yield super(UbuntuOneWizardSignInTestCase, self).setUp()
self.page = getattr(self.ui, '%s_page' % self.page_name)
+ self._move_to_this_page()
+
+ def _move_to_this_page(self):
+ """Fake the wizard is moved to this page."""
+ page_id = self.ui.pages[self.page]
+ if self.ui.currentId() != page_id:
+ self.ui._next_id = page_id
+ self.ui.next()
def test_was_added(self):
"""The SignInPage is added correctly."""
@@ -103,8 +203,98 @@
"""The page is enabled at startup."""
self.assertTrue(self.page.isEnabled())
-
-class LoginTestCase(UbuntuOneWizardTestCase):
+ def test_focus_order(self):
+ """The focus order is correct."""
+ # can not be tested due to Qt API limitations
+
+ def test_button_layout(self):
+ """The button layout is correct."""
+ msg = '%s should not be visible.'
+ for button_name in BUTTONS:
+ button = self.ui.button(getattr(self.ui, button_name))
+ if button_name in self.buttons:
+ self.assertTrue(button.isVisible(),
+ '%s should be visible.' % button_name)
+ else:
+ self.assertFalse(button.isVisible(), msg % button_name)
+
+ def test_side_widget_stage(self):
+ """When in this page, the side_widget' stage is correct."""
+ self.assertEqual(self.ui.side_widget.stage,
+ getattr(self.ui.side_widget, '%s_stage' % self.stage_name))
+
+ def test_buttons_behavior(self):
+ """Send the rejected signal when the cancel button is clicked."""
+ msg = '%r should emit %r with args %r when clicked.'
+
+ for name, (text, signal, signal_args) in self.buttons.iteritems():
+ button = self.ui.button(getattr(self.ui, name))
+
+ if text is not None:
+ self.assertEqual(unicode(button.text()), text)
+
+ getattr(self.ui, signal).connect(self._set_called)
+ button.click()
+
+ self.assertEqual(self._called, (signal_args, {}),
+ msg % (name, signal, signal_args))
+ self._called = False
+
+ def test_done_rejected(self):
+ """When the wizard was cancelled and user confirmed, finish."""
+ self.patch(self, 'confirm_response', gui.QtGui.QDialog.Accepted)
+ self.ui.rejected.connect(self._set_called)
+
+ assert not self.ui.page(self.ui.currentId()).isFinalPage()
+ self.ui.done(gui.QtGui.QDialog.Rejected)
+
+ self.assertEqual(self._called, ((), {}))
+
+ def test_done_rejected_confirmation_rejected(self):
+ """When the wizard was cancelled but user unconfimed, do not finish."""
+ self.patch(self, 'confirm_response', gui.QtGui.QDialog.Rejected)
+ self.ui.accepted.connect(self._set_called)
+ self.ui.rejected.connect(self._set_called)
+ self.ui.finished.connect(self._set_called)
+
+ assert not self.ui.page(self.ui.currentId()).isFinalPage()
+ self.ui.done(gui.QtGui.QDialog.Rejected)
+
+ self.assertEqual(self._called, False)
+
+
+class UbuntuOneWizardCloudToComputerTestCase(UbuntuOneWizardSignInTestCase):
+ """Test the CloudToComputerPage wizard page."""
+
+ buttons = {'FinishButton':
+ (None, 'finished', (gui.QtGui.QDialog.Accepted,))}
+ page_name = 'cloud_folders'
+ stage_name = 'folders'
+
+ def test_done_rejected(self):
+ """When the wizard is closed on the final page, emit rejected."""
+ self.ui.finished.connect(self._set_called)
+
+ self.ui._next_id = self.ui.pages[self.ui.cloud_folders_page]
+ assert self.ui.page(self.ui.currentId()).isFinalPage()
+ self.ui.done(gui.QtGui.QDialog.Rejected)
+
+ self.assertEqual(self._called, ((gui.QtGui.QDialog.Rejected,), {}))
+
+ def test_done_rejected_confirmation_rejected(self):
+ """When the wizard was cancelled but user unconfimed, do not finish."""
+ # does not apply to this page
+
+
+class UbuntuOneWizardSettingsTestCase(UbuntuOneWizardSignInTestCase):
+ """Test the CloudToComputerPage wizard page."""
+
+ buttons = {'BackButton': (None, 'currentIdChanged', (0,))}
+ page_name = 'settings'
+ stage_name = 'folders'
+
+
+class UbuntuOneWizardLoginTestCase(UbuntuOneWizardTestCase):
"""Test the login through the wizard."""
method = 'login'
@@ -112,7 +302,7 @@
@defer.inlineCallbacks
def test_with_credentials(self):
"""Wizard is done when credentials were retrieved."""
- self.ui.finished.connect(self._set_called)
+ self.ui.currentIdChanged.connect(self._set_called)
d = defer.succeed(TOKEN)
def check():
@@ -128,12 +318,13 @@
yield d
self.assertTrue(self.ui.signin_page.isEnabled())
- self.assertEqual(self._called, ((1,), {}))
+ expected_next_id = self.ui.pages[self.ui.cloud_folders_page]
+ self.assertEqual(self._called, ((expected_next_id,), {}))
@defer.inlineCallbacks
def test_without_credentials(self):
"""Wizard is done when credentials were retrieved."""
- self.ui.finished.connect(self._set_called)
+ self.ui.currentIdChanged.connect(self._set_called)
d = defer.succeed(None)
def check():
@@ -152,7 +343,7 @@
self.assertFalse(self._called)
-class RegisterTestCase(LoginTestCase):
+class UbuntuOneWizardRegisterTestCase(UbuntuOneWizardLoginTestCase):
"""Test the register through the wizard."""
method = 'register'
=== modified file 'ubuntuone/controlpanel/gui/qt/wizard.py'
--- ubuntuone/controlpanel/gui/qt/wizard.py 2012-03-02 17:13:04 +0000
+++ ubuntuone/controlpanel/gui/qt/wizard.py 2012-03-19 14:00:47 +0000
@@ -19,26 +19,82 @@
from PyQt4 import QtGui, QtCore
from twisted.internet import defer
+from ubuntu_sso.qt import LINK_STYLE
+from ubuntu_sso.qt.sso_wizard_page import BaseWizardPage
from ubuntu_sso.utils.ui import CLOSE_AND_SETUP_LATER
from ubuntuone.controlpanel import cache
+from ubuntuone.controlpanel.logger import log_call, setup_logging
+from ubuntuone.controlpanel.gui import (
+ APP_NAME,
+ ARE_YOU_SURE_HELP,
+ ARE_YOU_SURE_NO,
+ ARE_YOU_SURE_SUBTITLE,
+ ARE_YOU_SURE_TITLE,
+ ARE_YOU_SURE_YES,
+ CLOUD_TO_COMPUTER_SUBTITLE,
+ CLOUD_TO_COMPUTER_TITLE,
+ UBUNTUONE_LINK,
+)
+from ubuntuone.controlpanel.gui.qt.folders import (
+ RemoteFoldersPanel,
+)
+from ubuntuone.controlpanel.gui.qt.preferences import PreferencesPanel
from ubuntuone.controlpanel.gui.qt.signin import SignInPanel
from ubuntuone.controlpanel.gui.qt.side_widget import SideWidget
-
-
-class UbuntuOnePage(QtGui.QWizardPage):
+from ubuntuone.controlpanel.gui.qt.ui import are_you_sure_ui
+
+
+logger = setup_logging('qt.wizard')
+
+
+class AreYouSure(QtGui.QDialog):
+
+ """A 'Are you sure?' dialog."""
+
+ def __init__(self, *args, **kwargs):
+ super(AreYouSure, self).__init__(*args, **kwargs)
+ self.ui = are_you_sure_ui.Ui_Dialog()
+ self.ui.setupUi(self)
+ self.setWindowTitle(APP_NAME)
+
+ self.ui.title_label.setText(ARE_YOU_SURE_TITLE)
+
+ link = LINK_STYLE.format(link_url=UBUNTUONE_LINK,
+ link_text=UBUNTUONE_LINK)
+ msg = u'%s
%s' % (ARE_YOU_SURE_SUBTITLE,
+ ARE_YOU_SURE_HELP.format(support_url=link))
+ self.ui.message_label.setText(msg)
+
+ self.ui.yes_button.setText(ARE_YOU_SURE_YES)
+ self.ui.no_button.setText(ARE_YOU_SURE_NO)
+
+
+class UbuntuOnePage(BaseWizardPage):
"""A generic page for the UbuntuOneWizard."""
- panel_class = None
+ is_final = False
+ main_title = None
+ max_width = 5000
+ panel_class = QtGui.QFrame
+ sub_title = None
def __init__(self, *args, **kwargs):
super(UbuntuOnePage, self).__init__(*args, **kwargs)
- self.layout = QtGui.QVBoxLayout(self)
self.panel = None
if self.panel_class is not None:
- self.panel = SignInPanel()
- self.layout.addWidget(self.panel)
+ self.panel = self.panel_class()
+ self.layout().addWidget(self.panel)
+
+ if self.main_title is not None:
+ self.setTitle(self.main_title)
+
+ if self.sub_title is not None:
+ self.setSubTitle(self.sub_title)
+
+ self.form_errors_label.hide()
+ self.setFinalPage(self.is_final)
class SignInPage(UbuntuOnePage):
@@ -47,9 +103,30 @@
panel_class = SignInPanel
+class CloudToComputerPage(UbuntuOnePage):
+ """The page to choose cloud folders to sync locally."""
+
+ main_title = CLOUD_TO_COMPUTER_TITLE
+ panel_class = RemoteFoldersPanel
+ sub_title = CLOUD_TO_COMPUTER_SUBTITLE
+ is_final = True
+
+ def __init__(self, *args, **kwargs):
+ super(CloudToComputerPage, self).__init__(*args, **kwargs)
+ self.panel.ui.add_folder_button.hide()
+
+
+class SettingsPage(UbuntuOnePage):
+ """The page to adjust the service settings."""
+
+ panel_class = PreferencesPanel
+
+
class UbuntuOneWizard(cache.Cache, QtGui.QWizard):
"""The Ubuntu One wizard."""
+ show_license = False # do not change unless you know what you're doing
+
def __init__(self, *args, **kwargs):
super(UbuntuOneWizard, self).__init__(*args, **kwargs)
self.pages = {}
@@ -58,13 +135,13 @@
self.setOption(self.HaveFinishButtonOnEarlyPages, False)
self.setWizardStyle(self.ModernStyle)
- self.setButtonText(self.CancelButton, CLOSE_AND_SETUP_LATER)
- self.setButtonLayout([self.Stretch, self.CancelButton])
+ self.confirm_dialog = AreYouSure(self)
self.side_widget = SideWidget()
self.side_widget.stage = self.side_widget.signin_stage
self.setSideWidget(self.side_widget)
+ # sign in
self.signin_page = SignInPage()
self.addPage(self.signin_page)
@@ -72,10 +149,19 @@
self.signin_page.panel.ui.register_button.clicked.connect(
self.register)
- self.setTabOrder(self.signin_page.panel.ui.login_button,
- self.signin_page.panel.ui.register_button)
- self.setTabOrder(self.signin_page.panel.ui.register_button,
- self.button(self.CancelButton))
+ # cloud to compuer
+ self.cloud_folders_page = CloudToComputerPage()
+ self.addPage(self.cloud_folders_page)
+
+ self.cloud_folders_page.panel.ui.check_settings_button.clicked.connect(
+ self.check_settings)
+
+ # settings
+ self.settings_page = SettingsPage()
+ self.addPage(self.settings_page)
+
+ self._next_id = self.pages[self.signin_page]
+ self.next()
# pylint: disable=C0103
@@ -84,8 +170,67 @@
page_id = super(UbuntuOneWizard, self).addPage(page)
self.pages[page] = page_id
+ @log_call(logger.info)
+ def initializePage(self, page_id):
+ """The wizard will show the page 'page_id'."""
+ page = self.page(page_id)
+ logger.debug('UbuntuOneWizard.initializePage: page is %r.', page)
+
+ button_layout = button_to = button = stage = None
+
+ if page is self.signin_page:
+ self.setButtonText(self.CancelButton, CLOSE_AND_SETUP_LATER)
+
+ button_layout = [self.Stretch, self.CancelButton]
+ button = self.signin_page.panel.ui.register_button
+ button_to = self.button(self.CancelButton)
+ stage = self.side_widget.signin_stage
+ self._next_id = self.pages[self.cloud_folders_page]
+ elif page is self.cloud_folders_page:
+ button_layout = [self.Stretch, self.FinishButton]
+ button = self.cloud_folders_page.panel.ui.check_settings_button
+ button_to = self.button(self.FinishButton)
+ stage = self.side_widget.folders_stage
+ elif page is self.settings_page:
+ button_layout = [self.BackButton, self.Stretch]
+ button = self.settings_page.panel.ui.apply_changes_button
+ button_to = self.button(self.BackButton)
+ stage = self.side_widget.folders_stage
+ self._next_id = self.pages[self.cloud_folders_page]
+ else:
+ logger.error('UbuntuOneWizard.initializePage was called with an'
+ 'unknown page: %r (page_id was %r).', page, page_id)
+
+ logger.info('UbuntuOneWizard.initializePage: new page is %r, '
+ 'new button layout is %r, '
+ 'new side widget stage is %r.', page, button_layout, stage)
+
+ if button is not None and button_to is not None:
+ self.setTabOrder(button, button_to)
+ if button_layout is not None:
+ self.setButtonLayout(button_layout)
+ if stage is not None:
+ self.side_widget.stage = stage
+
+ @log_call(logger.info)
+ def cleanupPage(self, page_id):
+ """Called clean up 'page_id' just before the user leaves it."""
+ page = self.page(page_id)
+ logger.debug('UbuntuOneWizard.cleanupPage: page is %r.', page)
+ if page is self.settings_page:
+ self.initializePage(self.pages[self.cloud_folders_page])
+
+ def nextId(self):
+ """Return the nextId to show."""
+ return self._next_id
+
# pylint: enable=C0103
+ def _process_credentials(self, credentials=None):
+ """Confirm which is the next step after analyzing 'credentials'."""
+ if credentials:
+ self.next()
+
@QtCore.pyqtSlot()
@defer.inlineCallbacks
def login(self):
@@ -104,7 +249,19 @@
self._process_credentials(credentials)
self.setEnabled(True)
- def _process_credentials(self, credentials=None):
- """Confirm which is the next step after analyzing 'credentials'."""
- if credentials:
- self.accept()
+ @QtCore.pyqtSlot()
+ def check_settings(self):
+ """Show the check settings page."""
+ self._next_id = self.pages[self.settings_page]
+ self.next()
+
+ def done(self, result):
+ """The main window is being closed, call any custom callback."""
+ if (result == QtGui.QDialog.Rejected and
+ not self.page(self.currentId()).isFinalPage()):
+ response = self.confirm_dialog.exec_()
+ if response == QtGui.QDialog.Accepted:
+ logger.warning('UbuntuOneWizard: user canceled setup.')
+ self.rejected.emit()
+ else:
+ super(UbuntuOneWizard, self).done(result)