Merge lp:~diegosarmentero/ubuntu-sso-client/qt-refactor into lp:ubuntu-sso-client

Proposed by Diego Sarmentero
Status: Merged
Approved by: Roberto Alsina
Approved revision: 853
Merged at revision: 858
Proposed branch: lp:~diegosarmentero/ubuntu-sso-client/qt-refactor
Merge into: lp:ubuntu-sso-client
Diff against target: 6465 lines (+2159/-3667)
25 files modified
data/qt/choose_sign_in.ui (+1/-1)
ubuntu_sso/qt/__init__.py (+29/-0)
ubuntu_sso/qt/common.py (+7/-9)
ubuntu_sso/qt/controllers.py (+0/-977)
ubuntu_sso/qt/current_user_sign_in_page.py (+104/-1)
ubuntu_sso/qt/email_verification_page.py (+132/-0)
ubuntu_sso/qt/error_page.py (+29/-0)
ubuntu_sso/qt/forgotten_password_page.py (+180/-0)
ubuntu_sso/qt/gui.py (+63/-369)
ubuntu_sso/qt/main.py (+3/-1)
ubuntu_sso/qt/reset_password_page.py (+203/-0)
ubuntu_sso/qt/setup_account_page.py (+347/-54)
ubuntu_sso/qt/sign_in_page.py (+41/-3)
ubuntu_sso/qt/success_page.py (+27/-0)
ubuntu_sso/qt/tests/__init__.py (+45/-1)
ubuntu_sso/qt/tests/test_controllers.py (+0/-2113)
ubuntu_sso/qt/tests/test_current_user_sign_in_page.py (+201/-0)
ubuntu_sso/qt/tests/test_email_verification.py (+151/-0)
ubuntu_sso/qt/tests/test_forgotten_password.py (+230/-0)
ubuntu_sso/qt/tests/test_qt_views.py (+10/-88)
ubuntu_sso/qt/tests/test_reset_password.py (+9/-2)
ubuntu_sso/qt/tests/test_setup_account.py (+52/-44)
ubuntu_sso/qt/tests/test_sign_in_page.py (+2/-4)
ubuntu_sso/qt/ubuntu_sso_wizard.py (+269/-0)
ubuntu_sso/utils/ui.py (+24/-0)
To merge this branch: bzr merge lp:~diegosarmentero/ubuntu-sso-client/qt-refactor
Reviewer Review Type Date Requested Status
Roberto Alsina (community) Approve
Natalia Bidart (community) Approve
Review via email: mp+92343@code.launchpad.net

Commit message

- Refactor the pages and controller in sso (LP: #929686).

Description of the change

Now the Qt UI can be started in linux.

To post a comment you must log in.
852. By Diego Sarmentero

String moved to utils.ui

853. By Diego Sarmentero

merge

Revision history for this message
Natalia Bidart (nataliabidart) wrote :

Looks great!

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

I like it.

review: Approve

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1=== modified file 'data/qt/choose_sign_in.ui'
2--- data/qt/choose_sign_in.ui 2012-02-02 13:40:36 +0000
3+++ data/qt/choose_sign_in.ui 2012-02-10 11:32:20 +0000
4@@ -65,7 +65,7 @@
5 </font>
6 </property>
7 <property name="text">
8- <string>Congratulations, Ubuntu One is installed!</string>
9+ <string>Congratulations, app_name is installed!</string>
10 </property>
11 <property name="alignment">
12 <set>Qt::AlignLeading|Qt::AlignLeft|Qt::AlignVCenter</set>
13
14=== modified file 'ubuntu_sso/qt/__init__.py'
15--- ubuntu_sso/qt/__init__.py 2012-02-03 15:11:19 +0000
16+++ ubuntu_sso/qt/__init__.py 2012-02-10 11:32:20 +0000
17@@ -17,10 +17,13 @@
18 """The Qt graphical interface for the Ubuntu Single Sign On Client."""
19
20 import gettext
21+import collections
22
23
24 _ = gettext.gettext
25
26+ERROR_ALL = '__all__'
27+ERROR_MESSAGE = 'message'
28 LOCAL_FOLDERS_TITLE = _("Syncing your computer with the cloud")
29 LOCAL_FOLDERS_SPACE_HEADER = _("Space (%s)")
30 LOCAL_FOLDERS_OFFER_LABEL = _("The folders you have selected to sync "
31@@ -28,3 +31,29 @@
32 "some extra space")
33 LOCAL_FOLDERS_CALCULATING = _("Calculating")
34 NEXT = _("Next")
35+
36+
37+# Based on the gtk implementation
38+def build_general_error_message(errordict):
39+ """Build a user-friendly error message from the errordict."""
40+ result = ''
41+ if isinstance(errordict, collections.Mapping):
42+ msg1 = errordict.get(ERROR_ALL)
43+ msg2 = errordict.get(ERROR_MESSAGE)
44+ if msg2 is None:
45+ # See the errordict in LP: 828417
46+ msg2 = errordict.get('error_message')
47+ if msg1 is not None and msg2 is not None:
48+ result = '\n'.join((msg1, msg2))
49+ elif msg1 is not None:
50+ result = msg1
51+ elif msg2 is not None:
52+ result = msg2
53+ else:
54+ if 'errtype' in errordict:
55+ del errordict['errtype']
56+ result = '\n'.join(
57+ [('%s: %s' % (k, v)) for k, v in errordict.iteritems()])
58+ else:
59+ result = repr(errordict)
60+ return result
61
62=== modified file 'ubuntu_sso/qt/common.py'
63--- ubuntu_sso/qt/common.py 2011-09-01 11:04:23 +0000
64+++ ubuntu_sso/qt/common.py 2012-02-10 11:32:20 +0000
65@@ -18,20 +18,18 @@
66 """Common functionality used by the UI modules."""
67
68 import re
69-import gettext
70-
71-gettext.textdomain('ubuntu-sso-client')
72-_ = gettext.gettext
73+from ubuntu_sso.utils.ui import (
74+ PASSWORD_DIGIT,
75+ PASSWORD_LENGTH,
76+ PASSWORD_MATCH,
77+ PASSWORD_MUST_CONTAIN,
78+ PASSWORD_UPPER,
79+)
80
81 # all the text + styles that are used in the gui
82 BAD = u'<img src=":/password_hint_warning.png" /><font> %s </font>'
83 GOOD = u'<img src=":/password_hint_ok.png" /><font> %s </font>'
84 NORMAL = u'<font> %s </font>'
85-PASSWORD_DIGIT = _("At least one number")
86-PASSWORD_LENGTH = _("At least 8 characters")
87-PASSWORD_MATCH = _("Passwords don't match")
88-PASSWORD_MUST_CONTAIN = _("Your password must contain")
89-PASSWORD_UPPER = _("At least one uppercase letter")
90
91
92 def password_assistance(line_edit, assistance, icon_type=BAD):
93
94=== removed file 'ubuntu_sso/qt/controllers.py'
95--- ubuntu_sso/qt/controllers.py 2012-02-06 16:21:12 +0000
96+++ ubuntu_sso/qt/controllers.py 1970-01-01 00:00:00 +0000
97@@ -1,977 +0,0 @@
98-# -*- coding: utf-8 -*-
99-#
100-# Copyright 2011-2012 Canonical Ltd.
101-#
102-# This program is free software: you can redistribute it and/or modify it
103-# under the terms of the GNU General Public License version 3, as published
104-# by the Free Software Foundation.
105-#
106-# This program is distributed in the hope that it will be useful, but
107-# WITHOUT ANY WARRANTY; without even the implied warranties of
108-# MERCHANTABILITY, SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR
109-# PURPOSE. See the GNU General Public License for more details.
110-#
111-# You should have received a copy of the GNU General Public License along
112-# with this program. If not, see <http://www.gnu.org/licenses/>.
113-"""Controllers with the logic of the UI."""
114-
115-import collections
116-import os
117-import StringIO
118-import tempfile
119-
120-# pylint: disable=F0401
121-try:
122- from PIL import Image
123-except ImportError:
124- import Image
125-# pylint: enable=F0401
126-
127-from PyQt4.QtGui import QMessageBox, QWizard, QPixmap
128-from twisted.internet import defer
129-
130-from ubuntu_sso import main, NO_OP
131-from ubuntu_sso.logger import setup_logging
132-from ubuntu_sso.utils.ui import (
133- CAPTCHA_LOAD_ERROR,
134- CAPTCHA_REQUIRED_ERROR,
135- CAPTCHA_SOLUTION_ENTRY,
136- EMAIL1_ENTRY,
137- EMAIL2_ENTRY,
138- EMAIL_LABEL,
139- EMAIL_INVALID,
140- EMAIL_MISMATCH,
141- ERROR,
142- EXISTING_ACCOUNT_CHOICE_BUTTON,
143- FORGOTTEN_PASSWORD_BUTTON,
144- is_min_required_password,
145- is_correct_email,
146- JOIN_HEADER_LABEL,
147- LOGIN_PASSWORD_LABEL,
148- NAME_ENTRY,
149- NAME_INVALID,
150- PASSWORD1_ENTRY,
151- PASSWORD2_ENTRY,
152- PASSWORD_HELP,
153- PASSWORD_MISMATCH,
154- PASSWORD_TOO_WEAK,
155- REQUEST_PASSWORD_TOKEN_LABEL,
156- RESET_PASSWORD,
157- REQUEST_PASSWORD_TOKEN_WRONG_EMAIL,
158- REQUEST_PASSWORD_TOKEN_TECH_ERROR,
159- SET_UP_ACCOUNT_CHOICE_BUTTON,
160- SIGN_IN_BUTTON,
161- TRY_AGAIN_BUTTON,
162- VERIFY_EMAIL_TITLE,
163- VERIFY_EMAIL_CONTENT,
164-)
165-
166-
167-ERROR_ALL = '__all__'
168-ERROR_EMAIL = 'email'
169-ERROR_EMAIL_TOKEN = 'email_token'
170-ERROR_MESSAGE = 'message'
171-ERROR_PASSWORD = 'password'
172-logger = setup_logging('ubuntu_sso.controllers')
173-
174-
175-# Based on the gtk implementation
176-def _build_general_error_message(errordict):
177- """Build a user-friendly error message from the errordict."""
178- result = ''
179- if isinstance(errordict, collections.Mapping):
180- msg1 = errordict.get(ERROR_ALL)
181- msg2 = errordict.get(ERROR_MESSAGE)
182- if msg2 is None:
183- # See the errordict in LP: 828417
184- msg2 = errordict.get('error_message')
185- if msg1 is not None and msg2 is not None:
186- result = '\n'.join((msg1, msg2))
187- elif msg1 is not None:
188- result = msg1
189- elif msg2 is not None:
190- result = msg2
191- else:
192- if 'errtype' in errordict:
193- del errordict['errtype']
194- result = '\n'.join(
195- [('%s: %s' % (k, v)) for k, v in errordict.iteritems()])
196- else:
197- result = repr(errordict)
198- return result
199-
200-
201-class BackendController(object):
202- """Represent a controller that talks with the sso self.backend."""
203-
204- def __init__(self, title='', subtitle=''):
205- """Create a new instance."""
206- self.view = None
207- self.backend = None
208- self._title = title
209- self._subtitle = subtitle
210-
211- @defer.inlineCallbacks
212- def get_backend(self):
213- """Return the backend used by the controller."""
214- if self.backend is None:
215- client = yield main.get_sso_client()
216- self.backend = client.sso_login
217- defer.returnValue(self.backend)
218-
219- #pylint: disable=C0103
220- def pageInitialized(self):
221- """Call to prepare the page just before it is shown."""
222- #pylint: enable=C0103
223-
224-
225-class ChooseSignInController(BackendController):
226- """Controlled to the ChooseSignIn view/widget."""
227-
228- def __init__(self, title='', subtitle=''):
229- """Create a new instance to manage the view."""
230- super(ChooseSignInController, self).__init__(title, subtitle)
231- self.view = None
232-
233- # use an ugly name just so have a simlar api as found in PyQt
234- # pylint: disable=C0103
235- def setupUi(self, view):
236- """Perform the required actions to set up the ui."""
237- self.view = view
238- self._set_up_translated_strings()
239- self.view.header.set_title(self._title)
240- self.view.header.set_subtitle(self._subtitle)
241- self._connect_buttons()
242- # pylint: enable=C0103
243-
244- def _set_up_translated_strings(self):
245- """Set the correct strings for the UI."""
246- logger.debug('ChooseSignInController._set_up_translated_strings')
247- self.view.ui.existing_account_button.setText(
248- EXISTING_ACCOUNT_CHOICE_BUTTON)
249- self.view.ui.setup_account_button.setText(
250- SET_UP_ACCOUNT_CHOICE_BUTTON)
251-
252- def _connect_buttons(self):
253- """Connect the buttons to the actions to perform."""
254- logger.debug('ChooseSignInController._connect_buttons')
255- self.view.ui.existing_account_button.clicked.connect(
256- self._set_next_existing)
257- self.view.ui.setup_account_button.clicked.connect(self._set_next_new)
258-
259- def _set_next_existing(self):
260- """Set the next id and fire signal."""
261- logger.debug('ChooseSignInController._set_next_existing')
262- self.view.next = self.view.wizard().current_user_page_id
263- self.view.wizard().next()
264-
265- def _set_next_new(self):
266- """Set the next id and fire signal."""
267- logger.debug('ChooseSignInController._set_next_new')
268- self.view.next = self.view.wizard().setup_account_page_id
269- self.view.wizard().next()
270-
271-
272-class CurrentUserController(BackendController):
273- """Controller used in the view that is used to allow the signin."""
274-
275- def __init__(self, backend=None, title='', subtitle='', message_box=None):
276- """Create a new instance."""
277- super(CurrentUserController, self).__init__(title, subtitle)
278- if message_box is None:
279- message_box = QMessageBox
280- self.message_box = message_box
281-
282- def _set_translated_strings(self):
283- """Set the translated strings."""
284- logger.debug('CurrentUserController._set_translated_strings')
285- self.view.ui.email_label.setText(EMAIL_LABEL)
286- self.view.ui.password_label.setText(LOGIN_PASSWORD_LABEL)
287- self.view.ui.forgot_password_label.setText(FORGOTTEN_PASSWORD_BUTTON)
288- self.view.ui.sign_in_button.setText(SIGN_IN_BUTTON)
289-
290- def _connect_ui(self):
291- """Connect the buttons to perform actions."""
292- logger.debug('CurrentUserController._connect_buttons')
293- self.view.ui.sign_in_button.clicked.connect(self.login)
294- # lets add call backs to be execute for the calls we are interested
295- self.backend.on_login_error_cb = lambda app, error:\
296- self.on_login_error(error)
297- self.backend.on_logged_in_cb = self.on_logged_in
298- self.view.ui.forgot_password_label.linkActivated.connect(
299- self.on_forgotten_password)
300- self.view.ui.email_edit.textChanged.connect(self._validate)
301- self.view.ui.password_edit.textChanged.connect(self._validate)
302-
303- def _validate(self):
304- """Perform input validation."""
305- valid = True
306- if not is_correct_email(unicode(self.view.ui.email_edit.text())) or \
307- not unicode(self.view.ui.password_edit.text()):
308- valid = False
309- self.view.ui.sign_in_button.setEnabled(valid)
310- self.view.ui.sign_in_button.setProperty("DisabledState",
311- not self.view.ui.sign_in_button.isEnabled())
312- self.view.ui.sign_in_button.style().unpolish(
313- self.view.ui.sign_in_button)
314- self.view.ui.sign_in_button.style().polish(
315- self.view.ui.sign_in_button)
316-
317- def login(self):
318- """Perform the login using the self.backend."""
319- logger.debug('CurrentUserController.login')
320- # grab the data from the view and call the backend
321- email = unicode(self.view.ui.email_edit.text())
322- password = unicode(self.view.ui.password_edit.text())
323- d = self.backend.login(self.view.wizard().app_name, email, password)
324- d.addErrback(self.on_login_error)
325-
326- def on_login_error(self, error):
327- """There was an error when login in."""
328- # let the user know
329- logger.error('Got error when login %s, error: %s',
330- self.view.wizard().app_name, error)
331- if isinstance(error, collections.Mapping) and \
332- error.get('errtype', None) == 'UserNotValidated':
333- self.view.setField('email_address', self.view.ui.email_edit.text())
334- self.view.setField('password', self.view.ui.password_edit.text())
335- app_name = self.view.wizard().app_name
336- self.view.wizard().registrationIncomplete.emit(
337- app_name, error['message'])
338- else:
339- self.message_box.critical(_build_general_error_message(error),
340- self.view)
341-
342- def on_logged_in(self, app_name, result):
343- """We managed to log in."""
344- logger.info('Logged in for %s', app_name)
345- email = unicode(self.view.ui.email_edit.text())
346- self.view.wizard().loginSuccess.emit(app_name, email)
347- logger.debug('Wizard.loginSuccess emitted.')
348-
349- def on_forgotten_password(self):
350- """Show the user the forgotten password page."""
351- logger.info('Forgotten password')
352- email = unicode(self.view.ui.email_edit.text())
353- self.view.wizard().forgotten.ui.email_line_edit.setText(email)
354- self.view.next = self.view.wizard().forgotten_password_page_id
355- self.view.wizard().next()
356-
357- # use an ugly name just so have a simlar api as found in PyQt
358- #pylint: disable=C0103
359- @defer.inlineCallbacks
360- def setupUi(self, view):
361- """Setup the view."""
362- self.view = view
363- self.backend = yield self.get_backend()
364- self.view.header.set_title(self._title)
365- self.view.header.set_subtitle(self._subtitle)
366- self._set_translated_strings()
367- self._connect_ui()
368- #pylint: enable=C0103
369-
370-
371-class SetUpAccountController(BackendController):
372- """Controller for the setup account view."""
373-
374- def __init__(self, message_box=None, title='', subtitle=''):
375- """Create a new instance."""
376- super(SetUpAccountController, self).__init__(title, subtitle)
377- if message_box is None:
378- message_box = QMessageBox
379- self.message_box = message_box
380-
381- def _set_translated_strings(self):
382- """Set the different gettext translated strings."""
383- logger.debug('SetUpAccountController._set_translated_strings')
384- # set the translated string
385- self.view.ui.name_label.setText(NAME_ENTRY)
386- self.view.ui.email_label.setText(EMAIL1_ENTRY)
387- self.view.ui.confirm_email_label.setText(EMAIL2_ENTRY)
388- self.view.ui.password_label.setText(PASSWORD1_ENTRY)
389- self.view.ui.confirm_password_label.setText(PASSWORD2_ENTRY)
390- self.view.ui.password_info_label.setText(PASSWORD_HELP)
391- self.view.ui.captcha_solution_edit.setPlaceholderText(
392- CAPTCHA_SOLUTION_ENTRY)
393-
394- def _set_line_edits_validations(self):
395- """Set the validations to be performed on the edits."""
396- logger.debug('SetUpAccountController._set_line_edits_validations')
397- self.view.set_line_edit_validation_rule(self.view.ui.email_edit,
398- is_correct_email)
399- # set the validation rule for the email confirmation
400- self.view.set_line_edit_validation_rule(
401- self.view.ui.confirm_email_edit,
402- self.is_correct_email_confirmation)
403- # connect the changed text of the password to trigger a changed text
404- # in the confirm so that the validation is redone
405- self.view.ui.email_edit.textChanged.connect(
406- self.view.ui.confirm_email_edit.textChanged.emit)
407- self.view.set_line_edit_validation_rule(self.view.ui.password_edit,
408- is_min_required_password)
409- self.view.set_line_edit_validation_rule(
410- self.view.ui.confirm_password_edit,
411- self.is_correct_password_confirmation)
412- # same as the above case, lets connect a signal to a signal
413- self.view.ui.password_edit.textChanged.connect(
414- self.view.ui.confirm_password_edit.textChanged.emit)
415-
416- def _connect_ui_elements(self):
417- """Set the connection of signals."""
418- logger.debug('SetUpAccountController._connect_ui_elements')
419- self.view.ui.refresh_label.linkActivated.connect(lambda url: \
420- self._refresh_captcha())
421- # set the callbacks for the captcha generation
422- self.backend.on_captcha_generated_cb = self.on_captcha_generated
423- self.backend.on_captcha_generation_error_cb = lambda app, error: \
424- self.on_captcha_generation_error(error)
425- self.backend.on_user_registered_cb = self.on_user_registered
426- self.backend.on_user_registration_error_cb = \
427- self.on_user_registration_error
428- # We need to check if we enable the button on many signals
429- self.view.ui.name_edit.textEdited.connect(self._enable_setup_button)
430- self.view.ui.email_edit.textEdited.connect(self._enable_setup_button)
431- self.view.ui.confirm_email_edit.textEdited.connect(
432- self._enable_setup_button)
433- self.view.ui.password_edit.textEdited.connect(
434- self._enable_setup_button)
435- self.view.ui.confirm_password_edit.textEdited.connect(
436- self._enable_setup_button)
437- self.view.ui.captcha_solution_edit.textEdited.connect(
438- self._enable_setup_button)
439- self.view.terms_checkbox.stateChanged.connect(
440- self._enable_setup_button)
441-
442- def _enable_setup_button(self):
443- """Only enable the setup button if the form is valid."""
444- name = unicode(self.view.ui.name_edit.text()).strip()
445- email = unicode(self.view.ui.email_edit.text())
446- confirm_email = unicode(self.view.ui.confirm_email_edit.text())
447- password = unicode(self.view.ui.password_edit.text())
448- confirm_password = unicode(self.view.ui.confirm_password_edit.text())
449- captcha_solution = unicode(self.view.ui.captcha_solution_edit.text())
450-
451- # Check for len(name) > 0 to ensure that a bool is assigned to enabled
452- enabled = self.view.terms_checkbox.isChecked() and \
453- len(captcha_solution) > 0 and \
454- is_min_required_password(password) and \
455- password == confirm_password and is_correct_email(email) and \
456- email == confirm_email and len(name) > 0
457-
458- self.view.set_up_button.setEnabled(enabled)
459- self.view.set_up_button.setProperty("DisabledState", not enabled)
460- self.view.set_up_button.style().unpolish(self.view.set_up_button)
461- self.view.set_up_button.style().polish(self.view.set_up_button)
462-
463- def _refresh_captcha(self):
464- """Refresh the captcha image shown in the ui."""
465- logger.debug('SetUpAccountController._refresh_captcha')
466- # lets clean behind us, do we have the old file arround?
467- old_file = None
468- if self.view.captcha_file and os.path.exists(self.view.captcha_file):
469- old_file = self.view.captcha_file
470- fd = tempfile.TemporaryFile(mode='r')
471- file_name = fd.name
472- self.view.captcha_file = file_name
473- d = self.backend.generate_captcha(self.view.wizard().app_name,
474- file_name)
475- if old_file:
476- d.addCallback(lambda x: os.unlink(old_file))
477- d.addErrback(self.on_captcha_generation_error)
478- self.view.on_captcha_refreshing()
479-
480- def _set_titles(self):
481- """Set the diff titles of the view."""
482- logger.debug('SetUpAccountController._set_titles')
483- self.view.header.set_title(
484- JOIN_HEADER_LABEL % {'app_name': self.view.wizard().app_name})
485- self.view.header.set_subtitle(self.view.wizard().help_text)
486-
487- def _register_fields(self):
488- """Register the diff fields of the Ui."""
489- self.view.registerField('email_address', self.view.ui.email_edit)
490- self.view.registerField('password', self.view.ui.password_edit)
491-
492- def on_captcha_generated(self, app_name, result):
493- """A new image was generated."""
494- logger.debug('SetUpAccountController.on_captcha_generated for %r '
495- '(captcha id %r, filename %r).',
496- app_name, result, self.view.captcha_file)
497- self.view.captcha_id = result
498- # HACK: First, let me apologize before hand, you can mention my mother
499- # if needed I would do the same (mandel)
500- # In an ideal world we could use the Qt plug-in for the images so that
501- # we could load jpgs etc.. but this won't work when the app has been
502- # brozen win py2exe using bundle_files=1
503- # The main issue is that Qt will complain about the thread not being
504- # the correct one when performing a moveToThread operation which is
505- # done either by a setParent or something within the qtreactor, PIL
506- # in this case does solve the issue. Sorry :(
507- pil_image = Image.open(self.view.captcha_file)
508- string_io = StringIO.StringIO()
509- pil_image.save(string_io, format='png')
510- pixmap_image = QPixmap()
511- pixmap_image.loadFromData(string_io.getvalue())
512- self.view.captcha_image = pixmap_image
513- self.view.on_captcha_refresh_complete()
514-
515- def on_captcha_generation_error(self, error):
516- """An error ocurred."""
517- logger.debug('SetUpAccountController.on_captcha_generation_error')
518- self.message_box.critical(CAPTCHA_LOAD_ERROR, self.view)
519- self.view.on_captcha_refresh_complete()
520-
521- def on_user_registration_error(self, app_name, error):
522- """Let the user know we could not register."""
523- logger.debug('SetUpAccountController.on_user_registration_error')
524- # errors are returned as a dict with the data we want to show.
525- self._refresh_captcha()
526- msg = error.pop(ERROR_EMAIL, None)
527- if msg:
528- self.view.set_error_message(self.view.ui.email_assistance, msg)
529- self.message_box.critical(_build_general_error_message(error),
530- self.view)
531-
532- def on_user_registered(self, app_name, result):
533- """Execute when the user did register."""
534- logger.debug('SetUpAccountController.on_user_registered')
535- self.view.next = self.view.wizard().email_verification_page_id
536- self.view.wizard().next()
537-
538- def validate_form(self):
539- """Validate the info of the form and return an error."""
540- logger.debug('SetUpAccountController.validate_form')
541- name = unicode(self.view.ui.name_edit.text()).strip()
542- email = unicode(self.view.ui.email_edit.text())
543- confirm_email = unicode(self.view.ui.confirm_email_edit.text())
544- password = unicode(self.view.ui.password_edit.text())
545- confirm_password = unicode(self.view.ui.confirm_password_edit.text())
546- captcha_solution = unicode(self.view.ui.captcha_solution_edit.text())
547- condition = True
548- messages = []
549- if not name:
550- condition = False
551- self.view.set_error_message(self.view.ui.name_assistance,
552- NAME_INVALID)
553- if not is_correct_email(email):
554- condition = False
555- self.view.set_error_message(self.view.ui.email_assistance,
556- EMAIL_INVALID)
557- if email != confirm_email:
558- condition = False
559- self.view.set_error_message(self.view.ui.confirm_email_assistance,
560- EMAIL_MISMATCH)
561- if not is_min_required_password(password):
562- messages.append(PASSWORD_TOO_WEAK)
563- if password != confirm_password:
564- messages.append(PASSWORD_MISMATCH)
565- if not captcha_solution:
566- messages.append(CAPTCHA_REQUIRED_ERROR)
567- if len(messages) > 0:
568- condition = False
569- self.message_box.critical('\n'.join(messages), self.view)
570- return condition
571-
572- def set_next_validation(self):
573- """Set the validation as the next page."""
574- logger.debug('SetUpAccountController.set_next_validation')
575- email = unicode(self.view.ui.email_edit.text())
576- password = unicode(self.view.ui.password_edit.text())
577- name = unicode(self.view.ui.name_edit.text())
578- captcha_id = self.view.captcha_id
579- captcha_solution = unicode(self.view.ui.captcha_solution_edit.text())
580- # validate the current info of the form, try to perform the action
581- # to register the user, and then move foward
582- if self.validate_form():
583- self.backend.register_user(self.view.wizard().app_name, email,
584- password, name, captcha_id,
585- captcha_solution)
586- # Update validation page's title, which contains the email
587- p_id = self.view.wizard().email_verification_page_id
588- self.view.wizard().page(p_id).controller.set_titles()
589-
590- def is_correct_email(self, email_address):
591- """Return if the email is correct."""
592- logger.debug('SetUpAccountController.is_correct_email')
593- return '@' in email_address
594-
595- def is_correct_email_confirmation(self, email_address):
596- """Return that the email is the same."""
597- logger.debug('SetUpAccountController.is_correct_email_confirmation')
598- return unicode(self.view.ui.email_edit.text()) == email_address
599-
600- def is_correct_password_confirmation(self, password):
601- """Return that the passwords are correct."""
602- logger.debug('SetUpAccountController.is_correct_password_confirmation')
603- return unicode(self.view.ui.password_edit.text()) == password
604-
605- #pylint: disable=C0103
606- @defer.inlineCallbacks
607- def setupUi(self, view):
608- """Setup the view."""
609- self.view = view
610- # request the backend to be used with the ui
611- self.backend = yield self.get_backend()
612- self._connect_ui_elements()
613- self._refresh_captcha()
614- self._set_titles()
615- self.view.header.set_title(self._title)
616- self.view.header.set_subtitle(self._subtitle)
617- self._set_translated_strings()
618- self._set_line_edits_validations()
619- self._register_fields()
620- #pylint: enable=C0103
621-
622-
623-class EmailVerificationController(BackendController):
624- """Controller used for the verification page."""
625-
626- def __init__(self, message_box=None, title='', subtitle=''):
627- """Create a new instance."""
628- super(EmailVerificationController, self).__init__(title, subtitle)
629- if message_box is None:
630- message_box = QMessageBox
631- self.message_box = message_box
632-
633- def _set_translated_strings(self):
634- """Set the trnaslated strings."""
635- logger.debug('EmailVerificationController._set_translated_strings')
636-
637- def _connect_ui_elements(self):
638- """Set the connection of signals."""
639- logger.debug('EmailVerificationController._connect_ui_elements')
640- self.view.ui.verification_code_edit.textChanged.connect(
641- self.validate_form)
642- self.view.next_button.clicked.connect(self.validate_email)
643- self.backend.on_email_validated_cb = lambda app, result: \
644- self.on_email_validated(app)
645- self.backend.on_email_validation_error_cb = \
646- self.on_email_validation_error
647-
648- def validate_form(self):
649- """Check the state of the form."""
650- code = self.view.verification_code.strip()
651- enabled = len(code) > 0
652- self.view.next_button.setEnabled(enabled)
653- self.view.next_button.setProperty('DisabledState',
654- not self.view.next_button.isEnabled())
655- self.view.next_button.style().unpolish(
656- self.view.next_button)
657- self.view.next_button.style().polish(
658- self.view.next_button)
659-
660- def _set_titles(self):
661- """Set the different titles."""
662- logger.debug('EmailVerificationController._set_titles')
663- self.view.header.set_title(VERIFY_EMAIL_TITLE)
664- self.view.header.set_subtitle(VERIFY_EMAIL_CONTENT % {
665- "app_name": self.view.wizard().app_name,
666- "email": self.view.wizard().field("email_address").toString(),
667- })
668-
669- def set_titles(self):
670- """This class needs to have a public set_titles.
671-
672- Since the subtitle contains data that is only known after SetupAccount
673- and _set_titles is only called on initialization.
674- """
675- self._set_titles()
676-
677- #pylint: disable=C0103
678- @defer.inlineCallbacks
679- def setupUi(self, view):
680- """Setup the view."""
681- self.view = view
682- self.backend = yield self.get_backend()
683- self._set_titles()
684- self._set_translated_strings()
685- self._connect_ui_elements()
686- #pylint: enable=C0103
687-
688- def validate_email(self):
689- """Call the next action."""
690- logger.debug('EmailVerificationController.validate_email')
691- email = unicode(self.view.wizard().field('email_address').toString())
692- password = unicode(self.view.wizard().field('password').toString())
693- code = unicode(self.view.ui.verification_code_edit.text())
694- self.backend.validate_email(self.view.wizard().app_name, email,
695- password, code)
696-
697- def on_email_validated(self, app_name):
698- """Signal thrown after the email is validated."""
699- logger.info('EmailVerificationController.on_email_validated')
700- email = self.view.wizard().field('email_address').toString()
701- self.view.wizard().registrationSuccess.emit(app_name, email)
702-
703- def on_email_validation_error(self, app_name, error):
704- """Signal thrown when there's a problem validating the email."""
705- msg = error.pop(ERROR_EMAIL_TOKEN, '')
706- msg += _build_general_error_message(error)
707- self.message_box.critical(msg, self.view)
708-
709- #pylint: disable=C0103
710- def pageInitialized(self):
711- """Called to prepare the page just before it is shown."""
712- self.view.next_button.setDefault(True)
713- self.view.next_button.setEnabled(False)
714- self.view.next_button.setProperty('DisabledState',
715- not self.view.next_button.isEnabled())
716- self.view.next_button.style().unpolish(
717- self.view.next_button)
718- self.view.next_button.style().polish(
719- self.view.next_button)
720- #pylint: enable=C0103
721-
722-
723-class ErrorController(BackendController):
724- """Controller used for the error page."""
725-
726- def __init__(self, title='', subtitle=''):
727- """Create a new instance."""
728- super(ErrorController, self).__init__(title, subtitle)
729- self.view = None
730-
731- #pylint: disable=C0103
732- def setupUi(self, view):
733- """Setup the view."""
734- self.view = view
735- self.view.next = -1
736- self.view.ui.error_message_label.setText(ERROR)
737- self.view.header.set_title(self._title)
738- self.view.header.set_subtitle(self._subtitle)
739- #pylint: enable=C0103
740-
741-
742-class ForgottenPasswordController(BackendController):
743- """Controller used to deal with the forgotten pwd page."""
744-
745- def __init__(self, message_box=None, title='', subtitle=''):
746- """Create a new instance."""
747- super(ForgottenPasswordController, self).__init__()
748- if message_box is None:
749- message_box = QMessageBox
750- self.message_box = message_box
751- super(ForgottenPasswordController, self).__init__(title, subtitle)
752-
753- #pylint: disable=C0103
754- def pageInitialized(self):
755- """Set the initial state of ForgottenPassword page."""
756- self.view.send_button.setDefault(True)
757- enabled = not self.view.ui.email_line_edit.text().isEmpty()
758- self.view.send_button.setEnabled(enabled)
759- # The style from this property come from the Wizard
760- self.view.send_button.setProperty("DisabledState",
761- not self.view.send_button.isEnabled())
762- self.view.send_button.style().unpolish(
763- self.view.send_button)
764- self.view.send_button.style().polish(
765- self.view.send_button)
766- #pylint: enable=C0103
767-
768- def _register_fields(self):
769- """Register the fields of the wizard page."""
770- self.view.registerField('email_address',
771- self.view.email_address_line_edit)
772-
773- def _set_translated_strings(self):
774- """Set the translated strings in the view."""
775- self.view.forgotted_password_intro_label.setText(
776- REQUEST_PASSWORD_TOKEN_LABEL % {'app_name':
777- self.view.wizard().app_name})
778- self.view.email_address_label.setText(EMAIL_LABEL)
779- self.view.send_button.setText(RESET_PASSWORD)
780- self.view.try_again_button.setText(TRY_AGAIN_BUTTON)
781-
782- def _set_enhanced_line_edit(self):
783- """Set the extra logic to the line edits."""
784- self.view.set_line_edit_validation_rule(
785- self.view.email_address_line_edit,
786- is_correct_email)
787-
788- def _connect_ui(self):
789- """Connect the diff signals from the Ui."""
790- self.view.email_address_line_edit.textChanged.connect(self._validate)
791- self.view.send_button.clicked.connect(
792- lambda: self.backend.request_password_reset_token(
793- self.view.wizard().app_name,
794- self.view.email_address))
795- self.view.try_again_button.clicked.connect(self.on_try_again)
796- # set the backend callbacks to be used
797- self.backend.on_password_reset_token_sent_cb = lambda app, result:\
798- self.on_password_reset_token_sent()
799- self.backend.on_password_reset_error_cb = self.on_password_reset_error
800-
801- def _validate(self):
802- """Validate that we have an email."""
803- email = unicode(self.view.email_address_line_edit.text())
804- self.view.send_button.setEnabled(is_correct_email(email))
805- self.view.send_button.setProperty("DisabledState",
806- not self.view.send_button.isEnabled())
807- self.view.send_button.style().unpolish(
808- self.view.send_button)
809- self.view.send_button.style().polish(
810- self.view.send_button)
811-
812- def on_try_again(self):
813- """Set back the widget to the initial state."""
814- self.view.try_again_widget.setVisible(False)
815- self.view.email_widget.setVisible(True)
816-
817- def on_password_reset_token_sent(self):
818- """Action taken when we managed to get the password reset done."""
819- # ignore the result and move to the reset page
820- self.view.next = self.view.wizard().reset_password_page_id
821- self.view.wizard().next()
822-
823- def on_password_reset_error(self, app_name, error):
824- """Action taken when there was an error requesting the reset."""
825- # set the error message
826- msg = REQUEST_PASSWORD_TOKEN_TECH_ERROR
827- if error['errtype'] == 'ResetPasswordTokenError':
828- # the account provided is wrong, lets tell the user to try
829- # again
830- msg = REQUEST_PASSWORD_TOKEN_WRONG_EMAIL
831- else:
832- # ouch, I dont know what went wrong, tell the user to try later
833- self.view.email_widget.setVisible(False)
834- self.view.forgotted_password_intro_label.setVisible(False)
835- self.view.try_again_widget.setVisible(True)
836- self.message_box.critical(msg, self.view)
837-
838- #pylint: disable=C0103
839- @defer.inlineCallbacks
840- def setupUi(self, view):
841- """Setup the view."""
842- self.view = view
843- self.backend = yield self.get_backend()
844- # hide the error label
845- self.view.try_again_widget.setVisible(False)
846- self._set_translated_strings()
847- self._connect_ui()
848- self._set_enhanced_line_edit()
849- self._register_fields()
850- self.view.header.set_title(self._title)
851- self.view.header.set_subtitle(self._subtitle)
852- #pylint: enable=C0103
853-
854-
855-class ResetPasswordController(BackendController):
856- """Controller used to deal with reseintg the password."""
857-
858- def __init__(self, title='', subtitle='', message_box=None):
859- """Create a new instance."""
860- if message_box is None:
861- message_box = QMessageBox
862- self.message_box = message_box
863- super(ResetPasswordController, self).__init__(title, subtitle)
864-
865- #pylint: disable=C0103
866- def pageInitialized(self):
867- """Set the initial state of ForgottenPassword page."""
868- self.view.ui.reset_password_button.setDefault(True)
869- self.view.ui.reset_password_button.setEnabled(False)
870- # The style from this property come from the Wizard
871- self.view.ui.reset_password_button.setProperty("DisabledState",
872- not self.view.ui.reset_password_button.isEnabled())
873- self.view.ui.reset_password_button.style().unpolish(
874- self.view.ui.reset_password_button)
875- self.view.ui.reset_password_button.style().polish(
876- self.view.ui.reset_password_button)
877- #pylint: enable=C0103
878-
879- def _set_translated_strings(self):
880- """Translate the diff strings used in the app."""
881- self.view.ui.reset_password_button.setText(RESET_PASSWORD)
882- self.view.setSubTitle(PASSWORD_HELP)
883-
884- def _connect_ui(self):
885- """Connect the different ui signals."""
886- self.view.ui.reset_password_button.clicked.connect(
887- self.set_new_password)
888- self.backend.on_password_changed_cb = self.on_password_changed
889- self.backend.on_password_change_error_cb = \
890- self.on_password_change_error
891- self.view.ui.reset_code_line_edit.textChanged.connect(self._validate)
892- self.view.ui.password_line_edit.textChanged.connect(self._validate)
893- self.view.ui.confirm_password_line_edit.textChanged.connect(
894- self._validate)
895-
896- def _validate(self):
897- """Enable the submit button if data is valid."""
898- enabled = True
899- code = unicode(self.view.ui.reset_code_line_edit.text())
900- password = unicode(self.view.ui.password_line_edit.text())
901- confirm_password = unicode(
902- self.view.ui.confirm_password_line_edit.text())
903- if not is_min_required_password(password):
904- enabled = False
905- elif not self.is_correct_password_confirmation(confirm_password):
906- enabled = False
907- elif not code:
908- enabled = False
909- self.view.ui.reset_password_button.setEnabled(enabled)
910- self.view.ui.reset_password_button.setProperty("DisabledState",
911- not self.view.ui.reset_password_button.isEnabled())
912- self.view.ui.reset_password_button.style().unpolish(
913- self.view.ui.reset_password_button)
914- self.view.ui.reset_password_button.style().polish(
915- self.view.ui.reset_password_button)
916-
917- def _add_line_edits_validations(self):
918- """Add the validations to be use by the line edits."""
919- self.view.set_line_edit_validation_rule(
920- self.view.ui.password_line_edit,
921- is_min_required_password)
922- self.view.set_line_edit_validation_rule(
923- self.view.ui.confirm_password_line_edit,
924- self.is_correct_password_confirmation)
925- # same as the above case, lets connect a signal to a signal
926- self.view.ui.password_line_edit.textChanged.connect(
927- self.view.ui.confirm_password_line_edit.textChanged.emit)
928-
929- def on_password_changed(self, app_name, result):
930- """Let user know that the password was changed."""
931- email = unicode(self.view.wizard().forgotten.ui.email_line_edit.text())
932- self.view.wizard().current_user.ui.email_edit.setText(email)
933- self.view.wizard().overlay.hide()
934- current_user_id = self.view.wizard().current_user_page_id
935- visited_pages = self.view.wizard().visitedPages()
936- for index in reversed(visited_pages):
937- if index == current_user_id:
938- break
939- self.view.wizard().back()
940-
941- def on_password_change_error(self, app_name, error):
942- """Let the user know that there was an error."""
943- logger.error('Got error changing password for %s, error: %s',
944- self.view.wizard().app_name, error)
945- self.message_box.critical(_build_general_error_message(error),
946- self.view)
947-
948- def set_new_password(self):
949- """Request a new password to be set."""
950- app_name = self.view.wizard().app_name
951- email = unicode(self.view.wizard().forgotten.ui.email_line_edit.text())
952- code = unicode(self.view.ui.reset_code_line_edit.text())
953- password = unicode(self.view.ui.password_line_edit.text())
954- logger.info('Setting new password for %r and email %r with code %r',
955- app_name, email, code)
956- self.backend.set_new_password(app_name, email, code, password)
957-
958- def is_correct_password_confirmation(self, password):
959- """Return if the password is correct."""
960- return unicode(self.view.ui.password_line_edit.text()) == password
961-
962- #pylint: disable=C0103
963- @defer.inlineCallbacks
964- def setupUi(self, view):
965- """Setup the view."""
966- self.view = view
967- self.backend = yield self.get_backend()
968- self._set_translated_strings()
969- self._connect_ui()
970- self._add_line_edits_validations()
971- self.view.header.set_title(self._title)
972- self.view.header.set_subtitle(self._subtitle)
973- #pylint: enable=C0103
974-
975-
976-class SuccessController(BackendController):
977- """Controller used for the success page."""
978-
979- def __init__(self, title='', subtitle=''):
980- """Create a new instance."""
981- super(SuccessController, self).__init__(title, subtitle)
982- self.view = None
983-
984- #pylint: disable=C0103
985- def setupUi(self, view):
986- """Setup the view."""
987- self.view = view
988- self.view.next = -1
989- self.view.header.set_title(self._title)
990- self.view.header.set_subtitle(self._subtitle)
991- #pylint: enable=C0103
992-
993-
994-class UbuntuSSOWizardController(object):
995- """Controller used for the overall wizard."""
996-
997- def __init__(self, login_success_callback=NO_OP,
998- registration_success_callback=NO_OP,
999- user_cancellation_callback=NO_OP):
1000- """Create a new instance."""
1001- self.view = None
1002- self.login_success_callback = login_success_callback
1003- self.registration_success_callback = registration_success_callback
1004- self.user_cancellation_callback = user_cancellation_callback
1005-
1006- def on_user_cancelation(self):
1007- """Process the cancel action."""
1008- logger.debug('UbuntuSSOWizardController.on_user_cancelation')
1009- self.user_cancellation_callback(self.view.app_name)
1010- self.view.close()
1011-
1012- @defer.inlineCallbacks
1013- def on_login_success(self, app_name, email):
1014- """Process the success of a login."""
1015- logger.debug('UbuntuSSOWizardController.on_login_success')
1016- result = yield self.login_success_callback(
1017- unicode(app_name), unicode(email))
1018- logger.debug('Result from callback is %s', result)
1019- if result == 0:
1020- logger.info('Success in calling the given success_callback')
1021- self.show_success_message()
1022- else:
1023- logger.info('Error in calling the given success_callback')
1024- self.show_error_message()
1025-
1026- @defer.inlineCallbacks
1027- def on_registration_success(self, app_name, email):
1028- """Process the success of a registration."""
1029- logger.debug('UbuntuSSOWizardController.on_registration_success')
1030- result = yield self.registration_success_callback(unicode(app_name),
1031- unicode(email))
1032- logger.debug('Result from callback is %s', result)
1033- if result == 0:
1034- logger.info('Success in calling the given registration_callback')
1035- self.show_success_message()
1036- else:
1037- logger.info('Success in calling the given registration_callback')
1038- self.show_error_message()
1039-
1040- def show_success_message(self):
1041- """Show the success message in the view."""
1042- logger.info('Showing success message.')
1043- # get the id of the success page, set it as the next id of the
1044- # current page and let the wizard move to the next step
1045- self.view.currentPage().next = self.view.success_page_id
1046- self.view.next()
1047- # show the finish button but with a close message
1048- buttons_layout = []
1049- buttons_layout.append(QWizard.Stretch)
1050- buttons_layout.append(QWizard.FinishButton)
1051- self.view.setButtonLayout(buttons_layout)
1052-
1053- def show_error_message(self):
1054- """Show the error page in the view."""
1055- logger.info('Showing error message.')
1056- # similar to the success page but using the error id
1057- self.view.currentPage().next = self.view.error_page_id
1058- self.view.next()
1059- # show the finish button but with a close message
1060- buttons_layout = []
1061- buttons_layout.append(QWizard.Stretch)
1062- buttons_layout.append(QWizard.FinishButton)
1063- self.view.setButtonLayout(buttons_layout)
1064-
1065- #pylint: disable=C0103
1066- def setupUi(self, view):
1067- """Setup the view."""
1068- self.view = view
1069- self.view.setWizardStyle(QWizard.ModernStyle)
1070- self.view.button(QWizard.CancelButton).clicked.connect(
1071- self.on_user_cancelation)
1072- self.view.loginSuccess.connect(self.on_login_success)
1073- self.view.registrationSuccess.connect(self.on_registration_success)
1074- #pylint: enable=C0103
1075
1076=== modified file 'ubuntu_sso/qt/current_user_sign_in_page.py'
1077--- ubuntu_sso/qt/current_user_sign_in_page.py 2012-02-02 13:40:36 +0000
1078+++ ubuntu_sso/qt/current_user_sign_in_page.py 2012-02-10 11:32:20 +0000
1079@@ -1,5 +1,5 @@
1080 # -*- coding: utf-8 -*-
1081-
1082+#
1083 # Copyright 2012 Canonical Ltd.
1084 #
1085 # This program is free software: you can redistribute it and/or modify it
1086@@ -19,16 +19,58 @@
1087 import gettext
1088
1089 from PyQt4 import QtGui
1090+from twisted.internet import defer
1091
1092+from ubuntu_sso.qt import build_general_error_message
1093 from ubuntu_sso.qt.gui import SSOWizardPage
1094+from ubuntu_sso.logger import setup_logging
1095+from ubuntu_sso.utils.ui import (
1096+ EMAIL_LABEL,
1097+ FORGOTTEN_PASSWORD_BUTTON,
1098+ is_correct_email,
1099+ LOGIN_PASSWORD_LABEL,
1100+ SIGN_IN_BUTTON,
1101+)
1102
1103
1104 _ = gettext.gettext
1105+logger = setup_logging('ubuntu_sso.current_user_sign_in_page')
1106
1107
1108 class CurrentUserSignInPage(SSOWizardPage):
1109 """Wizard Page that lets a current user Sign into Ubuntu One."""
1110
1111+ def __init__(self, ui, *args, **kwargs):
1112+ super(CurrentUserSignInPage, self).__init__(ui, *args, **kwargs)
1113+
1114+ self._signals = {
1115+ 'LoggedIn':
1116+ self._filter_by_app_name(self.on_logged_in),
1117+ 'LoginError':
1118+ self._filter_by_app_name(self.on_login_error),
1119+ 'UserNotValidated':
1120+ self._filter_by_app_name(self.on_user_not_validated),
1121+ }
1122+ self.setup_page()
1123+
1124+ # pylint: disable=W0212
1125+ def on_user_not_validated(self, *args):
1126+ """Show the validate email page."""
1127+ self.next = self.wizard()._pages[self.wizard().email_verification]
1128+ email = unicode(self.ui.email_edit.text())
1129+ self.wizard().page(self.next).set_titles(email)
1130+ self.wizard().next()
1131+ # pylint: enable=W0212
1132+
1133+ @defer.inlineCallbacks
1134+ def setup_page(self):
1135+ """Setup the widget components."""
1136+ self.backend = yield self.get_backend()
1137+ self._set_translated_strings()
1138+ # lets add call backs to be execute for the calls we are interested
1139+ self._setup_signals()
1140+ self._connect_ui()
1141+
1142 # Invalid names of Qt-inherited methods
1143 # pylint: disable=C0103
1144
1145@@ -59,3 +101,64 @@
1146 def cleanupPage(self):
1147 """Reset the state of the wizard if Verification code was visited."""
1148 self.wizard()._next_id = None
1149+
1150+ def _set_translated_strings(self):
1151+ """Set the translated strings."""
1152+ logger.debug('CurrentUserSignInPage._set_translated_strings')
1153+ self.ui.email_label.setText(EMAIL_LABEL)
1154+ self.ui.password_label.setText(LOGIN_PASSWORD_LABEL)
1155+ self.ui.forgot_password_label.setText(FORGOTTEN_PASSWORD_BUTTON)
1156+ self.ui.sign_in_button.setText(SIGN_IN_BUTTON)
1157+
1158+ def _connect_ui(self):
1159+ """Connect the buttons to perform actions."""
1160+ logger.debug('CurrentUserSignInPage._connect_buttons')
1161+ self.ui.forgot_password_label.linkActivated.connect(
1162+ self.on_forgotten_password)
1163+ self.ui.email_edit.textChanged.connect(self._validate)
1164+ self.ui.password_edit.textChanged.connect(self._validate)
1165+ self.ui.sign_in_button.clicked.connect(self.login)
1166+
1167+ def _validate(self):
1168+ """Perform input validation."""
1169+ valid = True
1170+ correct_mail = is_correct_email(unicode(self.ui.email_edit.text()))
1171+ password = unicode(self.ui.password_edit.text())
1172+ if not correct_mail or not password:
1173+ valid = False
1174+ self.ui.sign_in_button.setEnabled(valid)
1175+ self.ui.sign_in_button.setProperty("DisabledState",
1176+ not self.ui.sign_in_button.isEnabled())
1177+ self.ui.sign_in_button.style().unpolish(self.ui.sign_in_button)
1178+ self.ui.sign_in_button.style().polish(self.ui.sign_in_button)
1179+
1180+ def login(self):
1181+ """Perform the login using the self.backend."""
1182+ logger.debug('CurrentUserSignInPage.login')
1183+ # grab the data from the view and call the backend
1184+ email = unicode(self.ui.email_edit.text())
1185+ password = unicode(self.ui.password_edit.text())
1186+ self.backend.login(self.app_name, email, password)
1187+
1188+ def on_login_error(self, app_name, error):
1189+ """There was an error when login in."""
1190+ # let the user know
1191+ logger.error('Got error when login %s, error: %s',
1192+ self.app_name, error)
1193+ self.message_box.critical(self, self.app_name,
1194+ build_general_error_message(error))
1195+
1196+ # pylint: disable=W0212
1197+ def on_logged_in(self, app_name, result):
1198+ """We managed to log in."""
1199+ logger.info('Logged in for %s', app_name)
1200+ self.next = self.wizard()._pages[self.wizard().success]
1201+ self.wizard().next()
1202+ logger.debug('Wizard.loginSuccess emitted.')
1203+
1204+ def on_forgotten_password(self):
1205+ """Show the user the forgotten password page."""
1206+ logger.info('Forgotten password')
1207+ self.next = self.wizard()._pages[self.wizard().forgotten]
1208+ self.wizard().next()
1209+ # pylint: enable=W0212
1210
1211=== added file 'ubuntu_sso/qt/email_verification_page.py'
1212--- ubuntu_sso/qt/email_verification_page.py 1970-01-01 00:00:00 +0000
1213+++ ubuntu_sso/qt/email_verification_page.py 2012-02-10 11:32:20 +0000
1214@@ -0,0 +1,132 @@
1215+# -*- coding: utf-8 -*-
1216+#
1217+# Copyright 2012 Canonical Ltd.
1218+#
1219+# This program is free software: you can redistribute it and/or modify it
1220+# under the terms of the GNU General Public License version 3, as published
1221+# by the Free Software Foundation.
1222+#
1223+# This program is distributed in the hope that it will be useful, but
1224+# WITHOUT ANY WARRANTY; without even the implied warranties of
1225+# MERCHANTABILITY, SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR
1226+# PURPOSE. See the GNU General Public License for more details.
1227+#
1228+# You should have received a copy of the GNU General Public License along
1229+# with this program. If not, see <http://www.gnu.org/licenses/>.
1230+
1231+"""Email Verification page UI."""
1232+
1233+from twisted.internet import defer
1234+
1235+from ubuntu_sso.qt import build_general_error_message
1236+from ubuntu_sso.qt.gui import SSOWizardPage
1237+from ubuntu_sso.logger import setup_logging
1238+from ubuntu_sso.utils.ui import (
1239+ VERIFY_EMAIL_TITLE,
1240+ VERIFY_EMAIL_CONTENT,
1241+)
1242+from ubuntu_sso.utils.ui import ERROR_EMAIL_TOKEN
1243+
1244+
1245+logger = setup_logging('ubuntu_sso.email_verification_page')
1246+
1247+
1248+class EmailVerificationPage(SSOWizardPage):
1249+ """Widget used to input the email verification code."""
1250+
1251+ def __init__(self, ui, *args, **kwargs):
1252+ super(EmailVerificationPage, self).__init__(ui, *args, **kwargs)
1253+ self.email = ''
1254+ self._signals = {
1255+ 'EmailValidated':
1256+ self._filter_by_app_name(self.on_email_validated),
1257+ 'EmailValidationError':
1258+ self._filter_by_app_name(self.on_email_validation_error),
1259+ }
1260+ self.setup_page()
1261+
1262+ @defer.inlineCallbacks
1263+ def setup_page(self):
1264+ """Setup the ui components."""
1265+ self.backend = yield self.get_backend()
1266+ self._setup_signals()
1267+ self._connect_ui_elements()
1268+
1269+ @property
1270+ def verification_code(self):
1271+ """Return the content of the verification code edit."""
1272+ return str(self.ui.verification_code_edit.text())
1273+
1274+ @property
1275+ def next_button(self):
1276+ """Return the button that move to the next stage."""
1277+ return self.ui.next_button
1278+
1279+ def _connect_ui_elements(self):
1280+ """Set the connection of signals."""
1281+ logger.debug('EmailVerificationController._connect_ui_elements')
1282+ self.ui.verification_code_edit.textChanged.connect(
1283+ self.validate_form)
1284+ self.next_button.clicked.connect(self.validate_email)
1285+
1286+ def validate_form(self):
1287+ """Check the state of the form."""
1288+ code = self.verification_code.strip()
1289+ enabled = len(code) > 0
1290+ self.next_button.setEnabled(enabled)
1291+ self.next_button.setProperty('DisabledState',
1292+ not self.next_button.isEnabled())
1293+ self.next_button.style().unpolish(
1294+ self.next_button)
1295+ self.next_button.style().polish(
1296+ self.next_button)
1297+
1298+ def _set_titles(self):
1299+ """Set the different titles."""
1300+ logger.debug('EmailVerificationController._set_titles')
1301+ self.header.set_title(VERIFY_EMAIL_TITLE)
1302+ self.header.set_subtitle(VERIFY_EMAIL_CONTENT % {
1303+ "app_name": self.app_name,
1304+ "email": self.email,
1305+ })
1306+
1307+ def set_titles(self, email):
1308+ """This class needs to have a public set_titles.
1309+
1310+ Since the subtitle contains data that is only known after SetupAccount
1311+ and _set_titles is only called on initialization.
1312+ """
1313+ self.email = email
1314+ self._set_titles()
1315+
1316+ def validate_email(self):
1317+ """Call the next action."""
1318+ logger.debug('EmailVerificationController.validate_email')
1319+ email = unicode(self.wizard().field('email_address').toString())
1320+ password = unicode(self.wizard().field('password').toString())
1321+ code = unicode(self.ui.verification_code_edit.text())
1322+ self.backend.validate_email(self.app_name, email,
1323+ password, code)
1324+
1325+ def on_email_validated(self, app_name, *args, **kwargs):
1326+ """Signal thrown after the email is validated."""
1327+ logger.info('EmailVerificationController.on_email_validated')
1328+ email = self.wizard().field('email_address').toString()
1329+ self.wizard().registrationSuccess.emit(app_name, email)
1330+
1331+ def on_email_validation_error(self, app_name, error):
1332+ """Signal thrown when there's a problem validating the email."""
1333+ msg = error.pop(ERROR_EMAIL_TOKEN, '')
1334+ msg += build_general_error_message(error)
1335+ self.message_box.critical(self, self.app_name, msg)
1336+
1337+ #pylint: disable=C0103
1338+ def initializePage(self):
1339+ """Called to prepare the page just before it is shown."""
1340+ self.next_button.setDefault(True)
1341+ self.next_button.setEnabled(False)
1342+ self.next_button.setProperty('DisabledState',
1343+ not self.next_button.isEnabled())
1344+ self.next_button.style().unpolish(self.next_button)
1345+ self.next_button.style().polish(self.next_button)
1346+ #pylint: enable=C0103
1347
1348=== added file 'ubuntu_sso/qt/error_page.py'
1349--- ubuntu_sso/qt/error_page.py 1970-01-01 00:00:00 +0000
1350+++ ubuntu_sso/qt/error_page.py 2012-02-10 11:32:20 +0000
1351@@ -0,0 +1,29 @@
1352+# -*- coding: utf-8 -*-
1353+#
1354+# Copyright 2012 Canonical Ltd.
1355+#
1356+# This program is free software: you can redistribute it and/or modify it
1357+# under the terms of the GNU General Public License version 3, as published
1358+# by the Free Software Foundation.
1359+#
1360+# This program is distributed in the hope that it will be useful, but
1361+# WITHOUT ANY WARRANTY; without even the implied warranties of
1362+# MERCHANTABILITY, SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR
1363+# PURPOSE. See the GNU General Public License for more details.
1364+#
1365+# You should have received a copy of the GNU General Public License along
1366+# with this program. If not, see <http://www.gnu.org/licenses/>.
1367+
1368+"""Email Verification page UI."""
1369+
1370+from ubuntu_sso.qt.gui import SSOWizardPage
1371+from ubuntu_sso.utils.ui import ERROR
1372+
1373+
1374+class ErrorPage(SSOWizardPage):
1375+ """Widget used to show the diff errors."""
1376+
1377+ def __init__(self, ui, *args, **kwargs):
1378+ super(ErrorPage, self).__init__(ui, *args, **kwargs)
1379+ self.next = -1
1380+ self.ui.error_message_label.setText(ERROR)
1381
1382=== added file 'ubuntu_sso/qt/forgotten_password_page.py'
1383--- ubuntu_sso/qt/forgotten_password_page.py 1970-01-01 00:00:00 +0000
1384+++ ubuntu_sso/qt/forgotten_password_page.py 2012-02-10 11:32:20 +0000
1385@@ -0,0 +1,180 @@
1386+# -*- coding: utf-8 -*-
1387+#
1388+# Copyright 2012 Canonical Ltd.
1389+#
1390+# This program is free software: you can redistribute it and/or modify it
1391+# under the terms of the GNU General Public License version 3, as published
1392+# by the Free Software Foundation.
1393+#
1394+# This program is distributed in the hope that it will be useful, but
1395+# WITHOUT ANY WARRANTY; without even the implied warranties of
1396+# MERCHANTABILITY, SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR
1397+# PURPOSE. See the GNU General Public License for more details.
1398+#
1399+# You should have received a copy of the GNU General Public License along
1400+# with this program. If not, see <http://www.gnu.org/licenses/>.
1401+
1402+"""Forgotten Password page UI."""
1403+
1404+from twisted.internet import defer
1405+
1406+from ubuntu_sso.qt.gui import SSOWizardEnhancedEditPage
1407+from ubuntu_sso.utils.ui import (
1408+ EMAIL_LABEL,
1409+ is_correct_email,
1410+ RESET_PASSWORD,
1411+ REQUEST_PASSWORD_TOKEN_LABEL,
1412+ REQUEST_PASSWORD_TOKEN_WRONG_EMAIL,
1413+ REQUEST_PASSWORD_TOKEN_TECH_ERROR,
1414+ TRY_AGAIN_BUTTON,
1415+)
1416+
1417+
1418+class ForgottenPasswordPage(SSOWizardEnhancedEditPage):
1419+ """Widget used to deal with users that forgot the password."""
1420+
1421+ def __init__(self, ui, *args, **kwargs):
1422+ super(ForgottenPasswordPage, self).__init__(ui, *args, **kwargs)
1423+ self._signals = {
1424+ 'PasswordResetTokenSent':
1425+ self._filter_by_app_name(self.on_password_reset_token_sent),
1426+ 'PasswordResetError':
1427+ self._filter_by_app_name(self.on_password_reset_error),
1428+ }
1429+ self.setup_page()
1430+
1431+ @defer.inlineCallbacks
1432+ def setup_page(self):
1433+ """Setup the widget components."""
1434+ self.backend = yield self.get_backend()
1435+ self._setup_signals()
1436+ # hide the error label
1437+ self.try_again_widget.setVisible(False)
1438+ self._set_translated_strings()
1439+ self._connect_ui()
1440+ self._set_enhanced_line_edit()
1441+ self._register_fields()
1442+
1443+ @property
1444+ def email_widget(self):
1445+ """Return the widget used to show the email information."""
1446+ return self.ui.email_widget
1447+
1448+ @property
1449+ def forgotted_password_intro_label(self):
1450+ """Return the intro label that lets the user know the issue."""
1451+ return self.ui.forgotted_password_intro_label
1452+
1453+ @property
1454+ def error_label(self):
1455+ """Return the label used to show error."""
1456+ return self.ui.error_label
1457+
1458+ @property
1459+ def email_address_label(self):
1460+ """Return the lable used to state the use of the line edit."""
1461+ return self.ui.email_address_label
1462+
1463+ @property
1464+ def email_address(self):
1465+ """Return the email address provided by the user."""
1466+ return str(self.ui.email_line_edit.text())
1467+
1468+ @property
1469+ def email_address_line_edit(self):
1470+ """Return the line edit with the content."""
1471+ return self.ui.email_line_edit
1472+
1473+ @property
1474+ def send_button(self):
1475+ """Return the button used to request the new password."""
1476+ return self.ui.send_button
1477+
1478+ @property
1479+ def try_again_widget(self):
1480+ """Return the widget used to display the try again button."""
1481+ return self.ui.try_again_widget
1482+
1483+ @property
1484+ def try_again_button(self):
1485+ """Return the button used to try again the reset password."""
1486+ return self.ui.try_again_button
1487+
1488+ #pylint: disable=C0103
1489+ def initializePage(self):
1490+ """Set the initial state of ForgottenPassword page."""
1491+ self.send_button.setDefault(True)
1492+ enabled = not self.ui.email_line_edit.text().isEmpty()
1493+ self.send_button.setEnabled(enabled)
1494+ # The style from this property come from the Wizard
1495+ self.send_button.setProperty("DisabledState",
1496+ not self.send_button.isEnabled())
1497+ self.send_button.style().unpolish(self.send_button)
1498+ self.send_button.style().polish(self.send_button)
1499+ #pylint: enable=C0103
1500+
1501+ def _register_fields(self):
1502+ """Register the fields of the wizard page."""
1503+ self.registerField('email_address',
1504+ self.email_address_line_edit)
1505+
1506+ def _set_translated_strings(self):
1507+ """Set the translated strings in the view."""
1508+ self.forgotted_password_intro_label.setText(
1509+ REQUEST_PASSWORD_TOKEN_LABEL % {'app_name':
1510+ self.app_name})
1511+ self.email_address_label.setText(EMAIL_LABEL)
1512+ self.send_button.setText(RESET_PASSWORD)
1513+ self.try_again_button.setText(TRY_AGAIN_BUTTON)
1514+
1515+ def _set_enhanced_line_edit(self):
1516+ """Set the extra logic to the line edits."""
1517+ self.set_line_edit_validation_rule(
1518+ self.email_address_line_edit,
1519+ is_correct_email)
1520+
1521+ def _connect_ui(self):
1522+ """Connect the diff signals from the Ui."""
1523+ self.email_address_line_edit.textChanged.connect(self._validate)
1524+ self.send_button.clicked.connect(
1525+ lambda: self.backend.request_password_reset_token(
1526+ self.app_name,
1527+ self.email_address))
1528+ self.try_again_button.clicked.connect(self.on_try_again)
1529+
1530+ def _validate(self):
1531+ """Validate that we have an email."""
1532+ email = unicode(self.email_address_line_edit.text())
1533+ self.send_button.setEnabled(is_correct_email(email))
1534+ self.send_button.setProperty("DisabledState",
1535+ not self.send_button.isEnabled())
1536+ self.send_button.style().unpolish(self.send_button)
1537+ self.send_button.style().polish(self.send_button)
1538+
1539+ def on_try_again(self):
1540+ """Set back the widget to the initial state."""
1541+ self.try_again_widget.setVisible(False)
1542+ self.email_widget.setVisible(True)
1543+
1544+ # pylint: disable=W0212
1545+ def on_password_reset_token_sent(self):
1546+ """Action taken when we managed to get the password reset done."""
1547+ # ignore the result and move to the reset page
1548+ self.next = self.wizard()._pages[self.wizard().reset_password]
1549+ self.wizard().next()
1550+ # pylint: enable=W0212
1551+
1552+ def on_password_reset_error(self, app_name, error):
1553+ """Action taken when there was an error requesting the reset."""
1554+ # set the error message
1555+ msg = REQUEST_PASSWORD_TOKEN_TECH_ERROR
1556+ if error['errtype'] == 'ResetPasswordTokenError':
1557+ # the account provided is wrong, lets tell the user to try
1558+ # again
1559+ msg = REQUEST_PASSWORD_TOKEN_WRONG_EMAIL
1560+ else:
1561+ # ouch, I dont know what went wrong, tell the user to try later
1562+ self.email_widget.setVisible(False)
1563+ self.forgotted_password_intro_label.setVisible(False)
1564+ self.try_again_widget.setVisible(True)
1565+ self.message_box.critical(self, self.app_name, msg)
1566
1567=== modified file 'ubuntu_sso/qt/gui.py'
1568--- ubuntu_sso/qt/gui.py 2012-02-02 18:09:21 +0000
1569+++ ubuntu_sso/qt/gui.py 2012-02-10 11:32:20 +0000
1570@@ -1,6 +1,6 @@
1571 # -*- coding: utf-8 -*-
1572 #
1573-# Copyright 2011 Canonical Ltd.
1574+# Copyright 2011-2012 Canonical Ltd.
1575 #
1576 # This program is free software: you can redistribute it and/or modify it
1577 # under the terms of the GNU General Public License version 3, as published
1578@@ -16,55 +16,31 @@
1579 """Qt implementation of the UI."""
1580
1581 import gettext
1582+from functools import wraps
1583
1584 # pylint: disable=F0401,E0611
1585
1586-from PyQt4.QtCore import pyqtSignal, Qt, SIGNAL
1587+from PyQt4.QtCore import Qt
1588 from PyQt4.QtGui import (
1589 QApplication,
1590 QWidget,
1591 QCursor,
1592 QHBoxLayout,
1593 QVBoxLayout,
1594- QPixmap,
1595+ QMessageBox,
1596 QStyle,
1597- QWizard,
1598 QWizardPage,
1599 QLabel,
1600 )
1601+from twisted.internet import defer
1602
1603+from ubuntu_sso import main
1604+from ubuntu_sso.qt.loadingoverlay import LoadingOverlay
1605 from ubuntu_sso.logger import setup_logging
1606-from ubuntu_sso.qt import common
1607-from ubuntu_sso.qt.controllers import (
1608- ChooseSignInController,
1609- CurrentUserController,
1610- EmailVerificationController,
1611- ErrorController,
1612- ForgottenPasswordController,
1613- ResetPasswordController,
1614- SetUpAccountController,
1615- SuccessController,
1616- UbuntuSSOWizardController,
1617-)
1618-from ubuntu_sso.qt.ui.choose_sign_in_ui import Ui_ChooseSignInPage
1619-from ubuntu_sso.qt.ui.current_user_sign_in_ui import Ui_CurrentUserSignInPage
1620-from ubuntu_sso.qt.ui.email_verification_ui import Ui_EmailVerificationPage
1621-from ubuntu_sso.qt.ui.error_message_ui import Ui_ErrorPage
1622-from ubuntu_sso.qt.ui.setup_account_ui import Ui_SetUpAccountPage
1623-from ubuntu_sso.qt.ui.success_message_ui import Ui_SuccessPage
1624-from ubuntu_sso.qt.ui.forgotten_password_ui import Ui_ForgottenPasswordPage
1625-from ubuntu_sso.qt.ui.reset_password_ui import Ui_ResetPasswordPage
1626-from ubuntu_sso.utils.ui import (
1627- PASSWORD1_ENTRY,
1628- PASSWORD2_ENTRY,
1629- RESET_CODE_ENTRY,
1630-)
1631+
1632
1633 _ = gettext.gettext
1634 logger = setup_logging('ubuntu_sso.gui')
1635-RESET_TITLE = _("Reset password")
1636-RESET_SUBTITLE = _("A password reset code has been sent to your e-mail."
1637- "Please enter the code below along with your new password.")
1638
1639
1640 class Header(QWidget):
1641@@ -105,43 +81,79 @@
1642 class SSOWizardPage(QWizardPage):
1643 """Root class for all wizard pages."""
1644
1645- def __init__(self, ui, controller, parent=None):
1646+ def __init__(self, ui, app_name=None, title='', subtitle='', parent=None):
1647 """Create a new instance."""
1648 super(SSOWizardPage, self).__init__(parent)
1649 self.ui = ui
1650 self.ui.setupUi(self)
1651+ self.overlay = LoadingOverlay(self)
1652+ self.overlay.hide()
1653+ self.app_name = app_name
1654 self.header = Header()
1655+ self.header.set_title(title)
1656+ self.header.set_subtitle(subtitle)
1657 self.layout().insertWidget(0, self.header)
1658- self.controller = controller
1659- if self.controller:
1660- self.controller.setupUi(self)
1661+ self.message_box = QMessageBox
1662 self.next = -1
1663+ self._signals = {}
1664+ self._signals_receivers = {}
1665+ self.backend = None
1666+
1667+ @defer.inlineCallbacks
1668+ def get_backend(self):
1669+ """Return the backend used by the controller."""
1670+ if self.backend is None:
1671+ client = yield main.get_sso_client()
1672+ self.backend = client.sso_login
1673+ defer.returnValue(self.backend)
1674
1675 # pylint: disable=C0103
1676 def nextId(self):
1677 """Provide the next id."""
1678 return self.next
1679- # pylint: enable=C0103
1680-
1681- # pylint: disable=C0103
1682- def initializePage(self):
1683- """Called to prepare the page just before it is shown."""
1684- if self.controller:
1685- self.controller.pageInitialized()
1686- # pylint: enable=C0103
1687-
1688- # pylint: disable=C0103
1689+
1690+ def resizeEvent(self, event):
1691+ """Resize the overlay to fit all the widget."""
1692+ QWizardPage.resizeEvent(self, event)
1693+ self.overlay.resize(event.size())
1694+
1695 def setTitle(self, title=''):
1696 """Set the Wizard Page Title."""
1697 self.header.set_title(title)
1698- # pylint: enable=C0103
1699
1700- # pylint: disable=C0103
1701 def setSubTitle(self, subtitle=''):
1702 """Set the Wizard Page Subtitle."""
1703 self.header.set_subtitle(subtitle)
1704 # pylint: enable=C0103
1705
1706+ def _filter_by_app_name(self, f):
1707+ """Excecute the decorated function only for 'self.app_name'."""
1708+
1709+ @wraps(f)
1710+ def inner(app_name, *args, **kwargs):
1711+ """Execute 'f' only if 'app_name' matches 'self.app_name'."""
1712+ result = None
1713+ if app_name == self.app_name:
1714+ result = f(app_name, *args, **kwargs)
1715+ else:
1716+ logger.info('%s: ignoring call since received app_name '\
1717+ '"%s" (expected "%s")',
1718+ f.__name__, app_name, self.app_name)
1719+ return result
1720+
1721+ return inner
1722+
1723+ def _setup_signals(self):
1724+ """Bind signals to callbacks to be able to test the pages."""
1725+ for signal, method in self._signals.iteritems():
1726+ actual = self._signals_receivers.get(signal)
1727+ if actual is not None:
1728+ msg = 'Signal %r is already connected with %r.'
1729+ logger.warning(msg, signal, actual)
1730+
1731+ match = self.backend.connect_to_signal(signal, method)
1732+ self._signals_receivers[signal] = match
1733+
1734
1735 class EnhancedLineEdit(object):
1736 """Represents and enhanced lineedit.
1737@@ -186,10 +198,11 @@
1738 class SSOWizardEnhancedEditPage(SSOWizardPage):
1739 """Page that contains enhanced line edits."""
1740
1741- def __init__(self, ui, controller, parent=None):
1742+ def __init__(self, ui, app_name=None, parent=None):
1743 """Create a new instance."""
1744 self._enhanced_edits = {}
1745- super(SSOWizardEnhancedEditPage, self).__init__(ui, controller, parent)
1746+ super(SSOWizardEnhancedEditPage, self).__init__(ui,
1747+ app_name=app_name, parent=parent)
1748
1749 def set_line_edit_validation_rule(self, edit, cb):
1750 """Set a new enhanced edit so that we can show an icon."""
1751@@ -199,322 +212,3 @@
1752 # create a new enhanced edit
1753 enhanced_edit = EnhancedLineEdit(edit, cb)
1754 self._enhanced_edits[edit] = enhanced_edit
1755-
1756-
1757-class ChooseSignInPage(SSOWizardPage):
1758- """Widget that allows the user to choose how to sign in."""
1759-
1760-
1761-class CurrentUserSignInPage(SSOWizardPage):
1762- """Widget that allows to get the data of user to sign in."""
1763-
1764-
1765-class EmailVerificationPage(SSOWizardPage):
1766- """Widget used to input the email verification code."""
1767-
1768- @property
1769- def verification_code(self):
1770- """Return the content of the verification code edit."""
1771- return str(self.ui.verification_code_edit.text())
1772-
1773- @property
1774- def next_button(self):
1775- """Return the button that move to the next stage."""
1776- return self.ui.next_button
1777-
1778-
1779-class ErrorPage(SSOWizardPage):
1780- """Widget used to show the diff errors."""
1781-
1782-
1783-class ForgottenPasswordPage(SSOWizardEnhancedEditPage):
1784- """Widget used to deal with users that forgot the password."""
1785-
1786- @property
1787- def email_widget(self):
1788- """Return the widget used to show the email information."""
1789- return self.ui.email_widget
1790-
1791- @property
1792- def forgotted_password_intro_label(self):
1793- """Return the intro label that lets the user know the issue."""
1794- return self.ui.forgotted_password_intro_label
1795-
1796- @property
1797- def error_label(self):
1798- """Return the label used to show error."""
1799- return self.ui.error_label
1800-
1801- @property
1802- def email_address_label(self):
1803- """Return the lable used to state the use of the line edit."""
1804- return self.ui.email_address_label
1805-
1806- @property
1807- def email_address(self):
1808- """Return the email address provided by the user."""
1809- return str(self.ui.email_line_edit.text())
1810-
1811- @property
1812- def email_address_line_edit(self):
1813- """Return the line edit with the content."""
1814- return self.ui.email_line_edit
1815-
1816- @property
1817- def send_button(self):
1818- """Return the button used to request the new password."""
1819- return self.ui.send_button
1820-
1821- @property
1822- def try_again_widget(self):
1823- """Return the widget used to display the try again button."""
1824- return self.ui.try_again_widget
1825-
1826- @property
1827- def try_again_button(self):
1828- """Return the button used to try again the reset password."""
1829- return self.ui.try_again_button
1830-
1831-
1832-class ResetPasswordPage(SSOWizardEnhancedEditPage):
1833- """Widget used to allow the user change his password."""
1834-
1835- def __init__(self, ui, controller, parent=None):
1836- """Create a new instance."""
1837- super(ResetPasswordPage, self).__init__(ui, controller, parent)
1838- self.ui.password_line_edit.textEdited.connect(
1839- lambda: common.password_assistance(self.ui.password_line_edit,
1840- self.ui.password_assistance,
1841- common.NORMAL))
1842- self.ui.confirm_password_line_edit.textEdited.connect(
1843- lambda: common.password_check_match(self.ui.password_line_edit,
1844- self.ui.confirm_password_line_edit,
1845- self.ui.password_assistance))
1846-
1847- def focus_changed(self, old, now):
1848- """Check who has the focus to activate password popups if necessary."""
1849- if now == self.ui.password_line_edit:
1850- self.ui.password_assistance.setVisible(True)
1851- common.password_default_assistance(self.ui.password_assistance)
1852- elif now == self.ui.confirm_password_line_edit:
1853- common.password_check_match(self.ui.password_line_edit,
1854- self.ui.confirm_password_line_edit,
1855- self.ui.password_assistance)
1856-
1857- # Invalid name "initializePage"
1858- # pylint: disable=C0103
1859-
1860- def initializePage(self):
1861- super(ResetPasswordPage, self).initializePage()
1862- common.password_default_assistance(self.ui.password_assistance)
1863- self.ui.password_assistance.setVisible(False)
1864- self.setTitle(RESET_TITLE)
1865- self.setSubTitle(RESET_SUBTITLE)
1866- self.ui.password_label.setText(PASSWORD1_ENTRY)
1867- self.ui.confirm_password_label.setText(PASSWORD2_ENTRY)
1868- self.ui.reset_code.setText(RESET_CODE_ENTRY)
1869-
1870- def showEvent(self, event):
1871- """Connect focusChanged signal from the application."""
1872- super(ResetPasswordPage, self).showEvent(event)
1873- self.connect(QApplication.instance(),
1874- SIGNAL("focusChanged(QWidget*, QWidget*)"),
1875- self.focus_changed)
1876-
1877- def hideEvent(self, event):
1878- """Disconnect the focusChanged signal when the page change."""
1879- super(ResetPasswordPage, self).hideEvent(event)
1880- try:
1881- self.disconnect(QApplication.instance(),
1882- SIGNAL("focusChanged(QWidget*, QWidget*)"),
1883- self.focus_changed)
1884- except TypeError:
1885- pass
1886-
1887- # pylint: enable=C0103
1888-
1889-
1890-class SetupAccountPage(SSOWizardEnhancedEditPage):
1891- """Widget used to create a new account."""
1892-
1893- def __init__(self, ui, controller, parent=None):
1894- """Create a new widget to be used."""
1895- super(SetupAccountPage, self).__init__(ui, controller, parent)
1896- # palettes that will be used to set the colors of the password strengh
1897- self.captcha_id = None
1898- self.captcha_file = None
1899- self.ui.captcha_view.setPixmap(QPixmap())
1900-
1901- def get_captcha_image(self):
1902- """Return the path to the captcha image."""
1903- return self.ui.captcha_view.pixmap()
1904-
1905- def set_captcha_image(self, pixmap_image):
1906- """Set the new image of the captcha."""
1907- # lets set the QPixmap for the label
1908- self.ui.captcha_view.setPixmap(pixmap_image)
1909-
1910- captcha_image = property(get_captcha_image, set_captcha_image)
1911-
1912-
1913-class SuccessPage(SSOWizardPage):
1914- """Page used to display success message."""
1915-
1916-
1917-class UbuntuSSOWizard(QWizard):
1918- """Wizard used to create or use sso."""
1919-
1920- # definition of the signals raised by the widget
1921- recoverableError = pyqtSignal('QString', 'QString')
1922- loginSuccess = pyqtSignal('QString', 'QString')
1923- registrationSuccess = pyqtSignal('QString', 'QString')
1924-
1925- def __init__(self, controller, app_name, **kwargs):
1926- """Create a new wizard."""
1927- parent = kwargs.get('parent')
1928- super(UbuntuSSOWizard, self).__init__(parent)
1929-
1930- # store common useful data provided by the app
1931- self.app_name = app_name
1932- self.ping_url = kwargs.get('ping_url', '')
1933- self.tc_url = kwargs.get('tc_url', '')
1934- self.help_text = kwargs.get('help_text', '')
1935- self.login_only = kwargs.get('login_only', False)
1936- self.close_callback = kwargs.get('close_callback', lambda: None)
1937-
1938- # set the diff pages of the QWizard
1939- self.sign_in_controller = ChooseSignInController(title='Sign In')
1940- self.sign_in_page = ChooseSignInPage(Ui_ChooseSignInPage(),
1941- self.sign_in_controller,
1942- parent=self)
1943- self.setup_controller = SetUpAccountController()
1944- self.setup_account = SetupAccountPage(Ui_SetUpAccountPage(),
1945- self.setup_controller,
1946- parent=self)
1947- self.email_verification = EmailVerificationPage(
1948- Ui_EmailVerificationPage(),
1949- EmailVerificationController())
1950- self.current_user_controller = CurrentUserController(title='Sign in')
1951- self.current_user = CurrentUserSignInPage(Ui_CurrentUserSignInPage(),
1952- self.current_user_controller,
1953- parent=self)
1954- self.success_controller = SuccessController()
1955- self.success = SuccessPage(Ui_SuccessPage(), self.success_controller,
1956- parent=self)
1957- self.error_controller = ErrorController()
1958- self.error = ErrorPage(Ui_ErrorPage(), self.error_controller)
1959- self.forgotte_pwd_controller = ForgottenPasswordController()
1960- self.forgotten = ForgottenPasswordPage(Ui_ForgottenPasswordPage(),
1961- self.forgotte_pwd_controller,
1962- parent=self)
1963- self.reset_password_controller = ResetPasswordController()
1964- self.reset_password = ResetPasswordPage(Ui_ResetPasswordPage(),
1965- self.reset_password_controller,
1966- parent=self)
1967- # store the ids of the pages so that it is easier to access them later
1968- self._pages = {}
1969- for page in [self.sign_in_page, self.setup_account,
1970- self.email_verification, self.current_user, self.success,
1971- self.error, self.forgotten, self.reset_password]:
1972- self._pages[page] = self.addPage(page)
1973-
1974- # set the buttons layout to only have cancel and back since the next
1975- # buttons are the ones used in the diff pages.
1976- buttons_layout = []
1977- buttons_layout.append(QWizard.Stretch)
1978- buttons_layout.append(QWizard.BackButton)
1979- buttons_layout.append(QWizard.CancelButton)
1980- self.setButtonLayout(buttons_layout)
1981- self.setWindowTitle(self.app_name)
1982- self.controller = controller
1983- self.controller.setupUi(self)
1984-
1985- @property
1986- def sign_in_page_id(self):
1987- """Return the id of the page used for choosing sign in type."""
1988- return self._pages[self.sign_in_page]
1989-
1990- @property
1991- def setup_account_page_id(self):
1992- """Return the id of the page used for sign in."""
1993- return self._pages[self.setup_account]
1994-
1995- @property
1996- def email_verification_page_id(self):
1997- """Return the id of the verification page."""
1998- return self._pages[self.email_verification]
1999-
2000- @property
2001- def current_user_page_id(self):
2002- """Return the id used to signin by a current user."""
2003- return self._pages[self.current_user]
2004-
2005- @property
2006- def success_page_id(self):
2007- """Return the id of the success page."""
2008- return self._pages[self.success]
2009-
2010- @property
2011- def forgotten_password_page_id(self):
2012- """Return the id of the forgotten password page."""
2013- return self._pages[self.forgotten]
2014-
2015- @property
2016- def reset_password_page_id(self):
2017- """Return the id of the reset password page."""
2018- return self._pages[self.reset_password]
2019-
2020- @property
2021- def error_page_id(self):
2022- """Return the id of the error page."""
2023- return self._pages[self.error]
2024-
2025-
2026-class UbuntuSSOClientGUI(object):
2027- """Ubuntu single sign-on GUI."""
2028-
2029- def __init__(self, app_name, **kwargs):
2030- """Create a new instance."""
2031- super(UbuntuSSOClientGUI, self).__init__()
2032- logger.debug('UbuntuSSOClientGUI: app_name %r, kwargs %r.',
2033- app_name, kwargs)
2034- self.app_name = app_name
2035- # create the controller and the ui, then set the cb and call the show
2036- # method so that we can work
2037- self.controller = UbuntuSSOWizardController(app_name)
2038- self.view = UbuntuSSOWizard(self.controller, app_name=app_name,
2039- **kwargs)
2040- self.view.show()
2041-
2042- def get_login_success_callback(self):
2043- """Return the log in success cb."""
2044- return self.controller.login_success_callback
2045-
2046- def set_login_success_callback(self, cb):
2047- """Set log in success cb."""
2048- self.controller.login_success_callback = cb
2049-
2050- login_success_callback = property(get_login_success_callback,
2051- set_login_success_callback)
2052-
2053- def get_registration_success_callback(self):
2054- """Return the registration success cb."""
2055- return self.controller.registration_success_callback
2056-
2057- def set_registration_success_callback(self, cb):
2058- """Set registration success cb."""
2059- self.controller.registration_success_callback = cb
2060-
2061- registration_success_callback = property(get_registration_success_callback,
2062- set_registration_success_callback)
2063-
2064- def get_user_cancellation_callback(self):
2065- """Return the user cancellation callback."""
2066- return self.controller.user_cancellation_callback
2067-
2068- def set_user_cancellation_callback(self, cb):
2069- """Set the user cancellation callback."""
2070- self.controller.user_cancellation_callback = cb
2071-
2072- user_cancellation_callback = property(get_user_cancellation_callback,
2073- set_user_cancellation_callback)
2074
2075=== modified file 'ubuntu_sso/qt/main.py'
2076--- ubuntu_sso/qt/main.py 2012-02-01 19:19:32 +0000
2077+++ ubuntu_sso/qt/main.py 2012-02-10 11:32:20 +0000
2078@@ -20,7 +20,7 @@
2079 import sys
2080
2081 from PyQt4 import QtGui
2082-from ubuntu_sso.qt.gui import UbuntuSSOClientGUI
2083+from ubuntu_sso.qt.ubuntu_sso_wizard import UbuntuSSOClientGUI
2084
2085
2086 def parse_args():
2087@@ -32,6 +32,8 @@
2088 help='a link to be used as the ping url (to notify about new tokens)')
2089 parser.add_argument('--tc_url', default='',
2090 help='a link to be used as Terms & Conditions url')
2091+ parser.add_argument('--policy_url', default='',
2092+ help='a link to be used as Privacy Policy url')
2093 parser.add_argument('--help_text', default='',
2094 help='extra text that will be shown below the headers')
2095 parser.add_argument('--login_only', action='store_true', default=False,
2096
2097=== added file 'ubuntu_sso/qt/reset_password_page.py'
2098--- ubuntu_sso/qt/reset_password_page.py 1970-01-01 00:00:00 +0000
2099+++ ubuntu_sso/qt/reset_password_page.py 2012-02-10 11:32:20 +0000
2100@@ -0,0 +1,203 @@
2101+# -*- coding: utf-8 -*-
2102+#
2103+# Copyright 2012 Canonical Ltd.
2104+#
2105+# This program is free software: you can redistribute it and/or modify it
2106+# under the terms of the GNU General Public License version 3, as published
2107+# by the Free Software Foundation.
2108+#
2109+# This program is distributed in the hope that it will be useful, but
2110+# WITHOUT ANY WARRANTY; without even the implied warranties of
2111+# MERCHANTABILITY, SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR
2112+# PURPOSE. See the GNU General Public License for more details.
2113+#
2114+# You should have received a copy of the GNU General Public License along
2115+# with this program. If not, see <http://www.gnu.org/licenses/>.
2116+
2117+"""Reset Password page UI."""
2118+
2119+from PyQt4.QtCore import SIGNAL
2120+from PyQt4.QtGui import QApplication
2121+from twisted.internet import defer
2122+
2123+from ubuntu_sso.qt import build_general_error_message
2124+from ubuntu_sso.qt.gui import SSOWizardEnhancedEditPage
2125+from ubuntu_sso.qt import common
2126+from ubuntu_sso.utils.ui import (
2127+ is_min_required_password,
2128+ PASSWORD1_ENTRY,
2129+ PASSWORD2_ENTRY,
2130+ PASSWORD_HELP,
2131+ RESET_CODE_ENTRY,
2132+ RESET_PASSWORD,
2133+ RESET_TITLE,
2134+ RESET_SUBTITLE,
2135+)
2136+from ubuntu_sso.logger import setup_logging
2137+
2138+
2139+logger = setup_logging('ubuntu_sso.reset_password_page')
2140+
2141+
2142+class ResetPasswordPage(SSOWizardEnhancedEditPage):
2143+ """Widget used to allow the user change his password."""
2144+
2145+ def __init__(self, ui, app_name=None, parent=None):
2146+ """Create a new instance."""
2147+ super(ResetPasswordPage, self).__init__(ui, app_name, parent)
2148+ self.ui.password_line_edit.textEdited.connect(
2149+ lambda: common.password_assistance(self.ui.password_line_edit,
2150+ self.ui.password_assistance,
2151+ common.NORMAL))
2152+ self.ui.confirm_password_line_edit.textEdited.connect(
2153+ lambda: common.password_check_match(self.ui.password_line_edit,
2154+ self.ui.confirm_password_line_edit,
2155+ self.ui.password_assistance))
2156+ self._signals = {
2157+ 'PasswordChanged':
2158+ self._filter_by_app_name(self.on_password_changed),
2159+ 'PasswordChangeError':
2160+ self._filter_by_app_name(self.on_password_change_error),
2161+ }
2162+ self.setup_page()
2163+
2164+ @defer.inlineCallbacks
2165+ def setup_page(self):
2166+ """Setup the widget components."""
2167+ self.backend = yield self.get_backend()
2168+ self._setup_signals()
2169+ self._set_translated_strings()
2170+ self._connect_ui()
2171+ self._add_line_edits_validations()
2172+
2173+ def focus_changed(self, old, now):
2174+ """Check who has the focus to activate password popups if necessary."""
2175+ if now == self.ui.password_line_edit:
2176+ self.ui.password_assistance.setVisible(True)
2177+ common.password_default_assistance(self.ui.password_assistance)
2178+ elif now == self.ui.confirm_password_line_edit:
2179+ common.password_check_match(self.ui.password_line_edit,
2180+ self.ui.confirm_password_line_edit,
2181+ self.ui.password_assistance)
2182+
2183+ # Invalid name "initializePage"
2184+ # pylint: disable=C0103
2185+
2186+ def initializePage(self):
2187+ """Extends QWizardPage initializePage method."""
2188+ super(ResetPasswordPage, self).initializePage()
2189+ common.password_default_assistance(self.ui.password_assistance)
2190+ self.ui.password_assistance.setVisible(False)
2191+ self.setTitle(RESET_TITLE)
2192+ self.setSubTitle(RESET_SUBTITLE)
2193+ self.ui.password_label.setText(PASSWORD1_ENTRY)
2194+ self.ui.confirm_password_label.setText(PASSWORD2_ENTRY)
2195+ self.ui.reset_code.setText(RESET_CODE_ENTRY)
2196+
2197+ self.ui.reset_password_button.setDefault(True)
2198+ self.ui.reset_password_button.setEnabled(False)
2199+ # The style from this property come from the Wizard
2200+ self.ui.reset_password_button.setProperty("DisabledState",
2201+ not self.ui.reset_password_button.isEnabled())
2202+ self.ui.reset_password_button.style().unpolish(
2203+ self.ui.reset_password_button)
2204+ self.ui.reset_password_button.style().polish(
2205+ self.ui.reset_password_button)
2206+
2207+ def showEvent(self, event):
2208+ """Connect focusChanged signal from the application."""
2209+ super(ResetPasswordPage, self).showEvent(event)
2210+ self.connect(QApplication.instance(),
2211+ SIGNAL("focusChanged(QWidget*, QWidget*)"),
2212+ self.focus_changed)
2213+
2214+ def hideEvent(self, event):
2215+ """Disconnect the focusChanged signal when the page change."""
2216+ super(ResetPasswordPage, self).hideEvent(event)
2217+ try:
2218+ self.disconnect(QApplication.instance(),
2219+ SIGNAL("focusChanged(QWidget*, QWidget*)"),
2220+ self.focus_changed)
2221+ except TypeError:
2222+ pass
2223+
2224+ # pylint: enable=C0103
2225+
2226+ def _set_translated_strings(self):
2227+ """Translate the diff strings used in the app."""
2228+ self.ui.reset_password_button.setText(RESET_PASSWORD)
2229+ self.setSubTitle(PASSWORD_HELP)
2230+
2231+ def _connect_ui(self):
2232+ """Connect the different ui signals."""
2233+ self.ui.reset_password_button.clicked.connect(
2234+ self.set_new_password)
2235+ self.ui.reset_code_line_edit.textChanged.connect(self._validate)
2236+ self.ui.password_line_edit.textChanged.connect(self._validate)
2237+ self.ui.confirm_password_line_edit.textChanged.connect(
2238+ self._validate)
2239+
2240+ def _validate(self):
2241+ """Enable the submit button if data is valid."""
2242+ enabled = True
2243+ code = unicode(self.ui.reset_code_line_edit.text())
2244+ password = unicode(self.ui.password_line_edit.text())
2245+ confirm_password = unicode(
2246+ self.ui.confirm_password_line_edit.text())
2247+ if not is_min_required_password(password):
2248+ enabled = False
2249+ elif not self.is_correct_password_confirmation(confirm_password):
2250+ enabled = False
2251+ elif not code:
2252+ enabled = False
2253+ self.ui.reset_password_button.setEnabled(enabled)
2254+ self.ui.reset_password_button.setProperty("DisabledState",
2255+ not self.ui.reset_password_button.isEnabled())
2256+ self.ui.reset_password_button.style().unpolish(
2257+ self.ui.reset_password_button)
2258+ self.ui.reset_password_button.style().polish(
2259+ self.ui.reset_password_button)
2260+
2261+ def _add_line_edits_validations(self):
2262+ """Add the validations to be use by the line edits."""
2263+ self.set_line_edit_validation_rule(
2264+ self.ui.password_line_edit,
2265+ is_min_required_password)
2266+ self.set_line_edit_validation_rule(
2267+ self.ui.confirm_password_line_edit,
2268+ self.is_correct_password_confirmation)
2269+ # same as the above case, lets connect a signal to a signal
2270+ self.ui.password_line_edit.textChanged.connect(
2271+ self.ui.confirm_password_line_edit.textChanged.emit)
2272+
2273+ def on_password_changed(self, app_name, result):
2274+ """Let user know that the password was changed."""
2275+ email = unicode(self.wizard().forgotten.ui.email_line_edit.text())
2276+ self.wizard().current_user.ui.email_edit.setText(email)
2277+ self.overlay.hide()
2278+ current_user_id = self.wizard().current_user_page_id
2279+ visited_pages = self.wizard().visitedPages()
2280+ for index in reversed(visited_pages):
2281+ if index == current_user_id:
2282+ break
2283+ self.wizard().back()
2284+
2285+ def on_password_change_error(self, app_name, error):
2286+ """Let the user know that there was an error."""
2287+ logger.error('Got error changing password for %s, error: %s',
2288+ self.app_name, error)
2289+ self.message_box.critical(self, self.app_name,
2290+ build_general_error_message(error))
2291+
2292+ def set_new_password(self):
2293+ """Request a new password to be set."""
2294+ email = unicode(self.wizard().forgotten.ui.email_line_edit.text())
2295+ code = unicode(self.ui.reset_code_line_edit.text())
2296+ password = unicode(self.ui.password_line_edit.text())
2297+ logger.info('Setting new password for %r and email %r with code %r',
2298+ self.app_name, email, code)
2299+ self.backend.set_new_password(self.app_name, email, code, password)
2300+
2301+ def is_correct_password_confirmation(self, password):
2302+ """Return if the password is correct."""
2303+ return unicode(self.ui.password_line_edit.text()) == password
2304
2305=== modified file 'ubuntu_sso/qt/setup_account_page.py'
2306--- ubuntu_sso/qt/setup_account_page.py 2012-02-02 15:57:02 +0000
2307+++ ubuntu_sso/qt/setup_account_page.py 2012-02-10 11:32:20 +0000
2308@@ -1,5 +1,5 @@
2309 # -*- coding: utf-8 -*-
2310-
2311+#
2312 # Copyright 2012 Canonical Ltd.
2313 #
2314 # This program is free software: you can redistribute it and/or modify it
2315@@ -16,59 +16,138 @@
2316
2317 """Customized Setup Account page for SSO."""
2318
2319-import gettext
2320-import re
2321+import StringIO
2322+import tempfile
2323+import os
2324+
2325+# pylint: disable=F0401
2326+try:
2327+ from PIL import Image
2328+except ImportError:
2329+ import Image
2330+# pylint: enable=F0401
2331
2332 from PyQt4 import QtGui, QtCore
2333+from twisted.internet import defer
2334
2335+from ubuntu_sso.qt import build_general_error_message
2336 from ubuntu_sso.qt import common
2337-from ubuntu_sso.qt import gui as sso_gui
2338+from ubuntu_sso.qt.gui import SSOWizardEnhancedEditPage
2339 from ubuntu_sso.qt import enhanced_check_box
2340 from ubuntu_sso.utils.ui import SET_UP_ACCOUNT_BUTTON
2341-
2342-
2343-_ = gettext.gettext
2344+from ubuntu_sso.utils.ui import (
2345+ AGREE_TO_TERMS,
2346+ AGREE_TO_TERMS_AND_POLICY,
2347+ CAPTCHA_LOAD_ERROR,
2348+ CAPTCHA_REQUIRED_ERROR,
2349+ CAPTCHA_SOLUTION_ENTRY,
2350+ EMAIL,
2351+ EMAIL1_ENTRY,
2352+ EMAIL2_ENTRY,
2353+ EMAIL_INVALID,
2354+ EMAIL_MATCH,
2355+ EMAIL_MISMATCH,
2356+ EMPTY_NAME,
2357+ INVALID_EMAIL,
2358+ is_min_required_password,
2359+ is_correct_email,
2360+ JOIN_HEADER_LABEL,
2361+ NAME,
2362+ NAME_ENTRY,
2363+ NAME_INVALID,
2364+ PASSWORD,
2365+ PASSWORD1_ENTRY,
2366+ PASSWORD2_ENTRY,
2367+ PASSWORD_HELP,
2368+ PASSWORD_MISMATCH,
2369+ PASSWORD_TOO_WEAK,
2370+ PRIVACY_POLICY_TEXT,
2371+ RETYPE_EMAIL,
2372+ RETYPE_PASSWORD,
2373+ TERMS_TEXT,
2374+ TITLE,
2375+)
2376+from ubuntu_sso.logger import setup_logging
2377+
2378+
2379+logger = setup_logging('ubuntu_sso.setup_account_page')
2380
2381 # pylint: disable=C0103
2382 ERROR = u'<font color="#df2d1f"><b> %s </b></font>'
2383 TITLE_STYLE = "<span style=\"font-size:24px\">%s</span>"
2384
2385-EMAIL = _("Email")
2386-EMPTY_NAME = _("Please enter your name")
2387-INVALID_EMAIL = _("Please enter a valid email address")
2388-EMAIL_MATCH = _("The email addresses do not match")
2389-NAME = _("Name")
2390-PASSWORD = _("Create a password")
2391-RETYPE_EMAIL = _("Retype email")
2392-RETYPE_PASSWORD = _("Retype password")
2393-SUBTITLE = _("You only need to set up your account "
2394- "once to get access to Ubuntu One across your devices.")
2395-TERMS = _("By signing up to Ubuntu One you agree to our "
2396- "{terms_and_conditions} and {privacy_policy}")
2397-TERMS_LINK = _("<a href='https://one.ubuntu.com/terms/'>"
2398- "<span style='color:#df2d1f;'>Terms of Service</span></a>")
2399-PRIVACY_POLICY_LINK = _("<a href='https://one.ubuntu.com/privacy/'>"
2400- "<span style='color:#df2d1f;'>Privacy Policy"
2401+ERROR_EMAIL = 'email'
2402+TERMS_LINK = ("<a href='{toc_link}'>"
2403+ "<span style='color:#df2d1f;'>{terms_text}</span></a>")
2404+PRIVACY_POLICY_LINK = ("<a href='{policy_link}'>"
2405+ "<span style='color:#df2d1f;'>{privacy_policy_text}"
2406 "</span></a></font>")
2407-TITLE = _("Sign Up to Ubuntu One")
2408-
2409-
2410-class SetupAccountPage(sso_gui.SetupAccountPage):
2411+
2412+
2413+class SetupAccountPage(SSOWizardEnhancedEditPage):
2414 """Customized Setup Account page for SSO."""
2415
2416- def __init__(self, *args, **kwargs):
2417- super(SetupAccountPage, self).__init__(*args, **kwargs)
2418+ def __init__(self, ui, subtitle, toc_link, policy_link, *args, **kwargs):
2419+ super(SetupAccountPage, self).__init__(ui, *args, **kwargs)
2420+ self._subtitle = subtitle
2421+ self._toc_link = toc_link
2422+ self._policy_link = policy_link
2423+ self.captcha_id = None
2424+ self.captcha_file = None
2425+ self._show_checkbox = True
2426+ self.ui.captcha_view.setPixmap(QtGui.QPixmap())
2427 self.ui.password_edit.textEdited.connect(
2428 lambda: common.password_assistance(self.ui.password_edit,
2429 self.ui.password_assistance,
2430 common.NORMAL))
2431- terms = TERMS.format(terms_and_conditions=TERMS_LINK,
2432- privacy_policy=PRIVACY_POLICY_LINK)
2433+ terms_links = TERMS_LINK.format(toc_link=self._toc_link,
2434+ terms_text=TERMS_TEXT)
2435+ privacy_policy_link = PRIVACY_POLICY_LINK.format(
2436+ policy_link=self._policy_link,
2437+ privacy_policy_text=PRIVACY_POLICY_TEXT)
2438+ if self._toc_link and self._policy_link:
2439+ terms = AGREE_TO_TERMS_AND_POLICY
2440+ elif self._toc_link:
2441+ terms = AGREE_TO_TERMS
2442+ elif self._policy_link:
2443+ terms = AGREE_TO_TERMS
2444+ terms_links = privacy_policy_link
2445+ else:
2446+ terms = ''
2447+ self._show_checkbox = False
2448+ terms = terms.format(app_name=self.app_name,
2449+ terms_and_conditions=terms_links,
2450+ privacy_policy=privacy_policy_link)
2451 self.terms_checkbox = enhanced_check_box.EnhancedCheckBox(terms)
2452 self.ui.hlayout_check.addWidget(self.terms_checkbox)
2453+ self.terms_checkbox.setVisible(self._show_checkbox)
2454
2455 self.set_up_button = None
2456 self.captcha_received = False
2457+ self._signals = {
2458+ 'CaptchaGenerated':
2459+ self._filter_by_app_name(self.on_captcha_generated),
2460+ 'CaptchaGenerationError':
2461+ self._filter_by_app_name(self.on_captcha_generation_error),
2462+ 'UserRegistered':
2463+ self._filter_by_app_name(self.on_user_registered),
2464+ 'UserRegistrationError':
2465+ self._filter_by_app_name(self.on_user_registration_error),
2466+ }
2467+ self.setup_page()
2468+
2469+ @defer.inlineCallbacks
2470+ def setup_page(self):
2471+ """Setup the widget components."""
2472+ # request the backend to be used with the ui
2473+ self.backend = yield self.get_backend()
2474+ # set the callbacks for the captcha generation
2475+ self._setup_signals()
2476+ self._connect_ui_elements()
2477+ self._refresh_captcha()
2478+ self._set_translated_strings()
2479+ self._set_line_edits_validations()
2480+ self._register_fields()
2481
2482 # Invalid name "initializePage"
2483 # pylint: disable=C0103
2484@@ -77,9 +156,9 @@
2485 """Setup UI details."""
2486 # We need to override some texts from SSO
2487 # to match our spec
2488- title_page = TITLE_STYLE % TITLE
2489+ title_page = TITLE_STYLE % TITLE.format(app_name=self.app_name)
2490 self.setTitle(title_page)
2491- self.setSubTitle(SUBTITLE)
2492+ self.setSubTitle(self._subtitle)
2493 # Set Setup Account button
2494 self.wizard().setOption(QtGui.QWizard.HaveCustomButton3, True)
2495 try:
2496@@ -88,8 +167,8 @@
2497 pass
2498 self.setButtonText(QtGui.QWizard.CustomButton3, SET_UP_ACCOUNT_BUTTON)
2499 self.set_up_button = self.wizard().button(QtGui.QWizard.CustomButton3)
2500- self.set_up_button.clicked.connect(self.wizard().overlay.show)
2501- self.set_up_button.clicked.connect(self.controller.set_next_validation)
2502+ self.set_up_button.clicked.connect(self.overlay.show)
2503+ self.set_up_button.clicked.connect(self.set_next_validation)
2504 self.set_up_button.setEnabled(False)
2505
2506 self.ui.name_label.setText(NAME)
2507@@ -112,6 +191,224 @@
2508 self.ui.confirm_email_assistance.setVisible(False)
2509 self.ui.password_assistance.setVisible(False)
2510 self.ui.refresh_label.setVisible(True)
2511+ #pylint: enable=C0103
2512+
2513+ def _set_translated_strings(self):
2514+ """Set the different gettext translated strings."""
2515+ logger.debug('SetUpAccountPage._set_translated_strings')
2516+ # set the translated string
2517+ self.ui.name_label.setText(NAME_ENTRY)
2518+ self.ui.email_label.setText(EMAIL1_ENTRY)
2519+ self.ui.confirm_email_label.setText(EMAIL2_ENTRY)
2520+ self.ui.password_label.setText(PASSWORD1_ENTRY)
2521+ self.ui.confirm_password_label.setText(PASSWORD2_ENTRY)
2522+ self.ui.password_info_label.setText(PASSWORD_HELP)
2523+ self.ui.captcha_solution_edit.setPlaceholderText(
2524+ CAPTCHA_SOLUTION_ENTRY)
2525+
2526+ def _set_line_edits_validations(self):
2527+ """Set the validations to be performed on the edits."""
2528+ logger.debug('SetUpAccountPage._set_line_edits_validations')
2529+ self.set_line_edit_validation_rule(self.ui.email_edit,
2530+ is_correct_email)
2531+ # set the validation rule for the email confirmation
2532+ self.set_line_edit_validation_rule(
2533+ self.ui.confirm_email_edit,
2534+ self.is_correct_email_confirmation)
2535+ # connect the changed text of the password to trigger a changed text
2536+ # in the confirm so that the validation is redone
2537+ self.ui.email_edit.textChanged.connect(
2538+ self.ui.confirm_email_edit.textChanged.emit)
2539+ self.set_line_edit_validation_rule(self.ui.password_edit,
2540+ is_min_required_password)
2541+ self.set_line_edit_validation_rule(
2542+ self.ui.confirm_password_edit,
2543+ self.is_correct_password_confirmation)
2544+ # same as the above case, lets connect a signal to a signal
2545+ self.ui.password_edit.textChanged.connect(
2546+ self.ui.confirm_password_edit.textChanged.emit)
2547+
2548+ def _connect_ui_elements(self):
2549+ """Set the connection of signals."""
2550+ logger.debug('SetUpAccountPage._connect_ui_elements')
2551+ self.ui.refresh_label.linkActivated.connect(lambda url: \
2552+ self._refresh_captcha())
2553+ # We need to check if we enable the button on many signals
2554+ self.ui.name_edit.textEdited.connect(self._enable_setup_button)
2555+ self.ui.email_edit.textEdited.connect(self._enable_setup_button)
2556+ self.ui.confirm_email_edit.textEdited.connect(
2557+ self._enable_setup_button)
2558+ self.ui.password_edit.textEdited.connect(self._enable_setup_button)
2559+ self.ui.confirm_password_edit.textEdited.connect(
2560+ self._enable_setup_button)
2561+ self.ui.captcha_solution_edit.textEdited.connect(
2562+ self._enable_setup_button)
2563+ self.terms_checkbox.stateChanged.connect(self._enable_setup_button)
2564+
2565+ def _enable_setup_button(self):
2566+ """Only enable the setup button if the form is valid."""
2567+ name = unicode(self.ui.name_edit.text()).strip()
2568+ email = unicode(self.ui.email_edit.text())
2569+ confirm_email = unicode(self.ui.confirm_email_edit.text())
2570+ password = unicode(self.ui.password_edit.text())
2571+ confirm_password = unicode(self.ui.confirm_password_edit.text())
2572+ captcha_solution = unicode(self.ui.captcha_solution_edit.text())
2573+
2574+ # Check for len(name) > 0 to ensure that a bool is assigned to enabled
2575+ if not self._show_checkbox:
2576+ checkbox_terms = True
2577+ else:
2578+ checkbox_terms = self.terms_checkbox.isChecked()
2579+ enabled = checkbox_terms and \
2580+ len(captcha_solution) > 0 and \
2581+ is_min_required_password(password) and \
2582+ password == confirm_password and is_correct_email(email) and \
2583+ email == confirm_email and len(name) > 0
2584+
2585+ self.set_up_button.setEnabled(enabled)
2586+ self.set_up_button.setProperty("DisabledState", not enabled)
2587+ self.set_up_button.style().unpolish(self.set_up_button)
2588+ self.set_up_button.style().polish(self.set_up_button)
2589+
2590+ def _refresh_captcha(self):
2591+ """Refresh the captcha image shown in the ui."""
2592+ logger.debug('SetUpAccountPage._refresh_captcha')
2593+ # lets clean behind us, do we have the old file arround?
2594+ if self.captcha_file and os.path.exists(self.captcha_file):
2595+ os.unlink(self.captcha_file)
2596+ fd = tempfile.TemporaryFile(mode='r')
2597+ file_name = fd.name
2598+ self.captcha_file = file_name
2599+ self.backend.generate_captcha(self.app_name, file_name)
2600+ self.on_captcha_refreshing()
2601+
2602+ def _set_titles(self):
2603+ """Set the diff titles of the view."""
2604+ logger.debug('SetUpAccountPage._set_titles')
2605+ self.header.set_title(
2606+ JOIN_HEADER_LABEL % {'app_name': self.app_name})
2607+ self.header.set_subtitle(self.wizard().help_text)
2608+
2609+ def _register_fields(self):
2610+ """Register the diff fields of the Ui."""
2611+ self.registerField('email_address', self.ui.email_edit)
2612+ self.registerField('password', self.ui.password_edit)
2613+
2614+ def on_captcha_generated(self, app_name, result):
2615+ """A new image was generated."""
2616+ logger.debug('SetUpAccountPage.on_captcha_generated for %r '
2617+ '(captcha id %r, filename %r).',
2618+ app_name, result, self.captcha_file)
2619+ self.captcha_id = result
2620+ # HACK: First, let me apologize before hand, you can mention my mother
2621+ # if needed I would do the same (mandel)
2622+ # In an ideal world we could use the Qt plug-in for the images so that
2623+ # we could load jpgs etc.. but this won't work when the app has been
2624+ # brozen win py2exe using bundle_files=1
2625+ # The main issue is that Qt will complain about the thread not being
2626+ # the correct one when performing a moveToThread operation which is
2627+ # done either by a setParent or something within the qtreactor, PIL
2628+ # in this case does solve the issue. Sorry :(
2629+ pil_image = Image.open(self.captcha_file)
2630+ string_io = StringIO.StringIO()
2631+ pil_image.save(string_io, format='png')
2632+ pixmap_image = QtGui.QPixmap()
2633+ pixmap_image.loadFromData(string_io.getvalue())
2634+ self.captcha_image = pixmap_image
2635+ self.on_captcha_refresh_complete()
2636+
2637+ def on_captcha_generation_error(self, error, *args, **kwargs):
2638+ """An error ocurred."""
2639+ logger.debug('SetUpAccountPage.on_captcha_generation_error')
2640+ self.message_box.critical(self, self.app_name, CAPTCHA_LOAD_ERROR)
2641+ self.on_captcha_refresh_complete()
2642+
2643+ def on_user_registration_error(self, app_name, error):
2644+ """Let the user know we could not register."""
2645+ logger.debug('SetUpAccountPage.on_user_registration_error')
2646+ # errors are returned as a dict with the data we want to show.
2647+ msg = error.pop(ERROR_EMAIL, '')
2648+ if msg:
2649+ self.set_error_message(self.ui.email_assistance, msg)
2650+ error_msg = build_general_error_message(error)
2651+ if error_msg:
2652+ self.message_box.critical(self, self.app_name, error_msg)
2653+ self._refresh_captcha()
2654+
2655+ def on_user_registered(self, app_name, result):
2656+ """Execute when the user did register."""
2657+ logger.debug('SetUpAccountPage.on_user_registered')
2658+ self.next = self.wizard().email_verification_page_id
2659+ self.wizard().next()
2660+
2661+ def validate_form(self):
2662+ """Validate the info of the form and return an error."""
2663+ logger.debug('SetUpAccountPage.validate_form')
2664+ name = unicode(self.ui.name_edit.text()).strip()
2665+ email = unicode(self.ui.email_edit.text())
2666+ confirm_email = unicode(self.ui.confirm_email_edit.text())
2667+ password = unicode(self.ui.password_edit.text())
2668+ confirm_password = unicode(self.ui.confirm_password_edit.text())
2669+ captcha_solution = unicode(self.ui.captcha_solution_edit.text())
2670+ condition = True
2671+ messages = []
2672+ if not name:
2673+ condition = False
2674+ self.set_error_message(self.ui.name_assistance,
2675+ NAME_INVALID)
2676+ if not is_correct_email(email):
2677+ condition = False
2678+ self.set_error_message(self.ui.email_assistance,
2679+ EMAIL_INVALID)
2680+ if email != confirm_email:
2681+ condition = False
2682+ self.set_error_message(self.ui.confirm_email_assistance,
2683+ EMAIL_MISMATCH)
2684+ if not is_min_required_password(password):
2685+ messages.append(PASSWORD_TOO_WEAK)
2686+ if password != confirm_password:
2687+ messages.append(PASSWORD_MISMATCH)
2688+ if not captcha_solution:
2689+ messages.append(CAPTCHA_REQUIRED_ERROR)
2690+ if len(messages) > 0:
2691+ condition = False
2692+ self.message_box.critical(self, self.app_name, '\n'.join(messages))
2693+ return condition
2694+
2695+ # pylint: disable=W0212
2696+ def set_next_validation(self):
2697+ """Set the validation as the next page."""
2698+ logger.debug('SetUpAccountPage.set_next_validation')
2699+ email = unicode(self.ui.email_edit.text())
2700+ password = unicode(self.ui.password_edit.text())
2701+ name = unicode(self.ui.name_edit.text())
2702+ captcha_id = self.captcha_id
2703+ captcha_solution = unicode(self.ui.captcha_solution_edit.text())
2704+ # validate the current info of the form, try to perform the action
2705+ # to register the user, and then move foward
2706+ if self.validate_form():
2707+ self.backend.register_user(self.app_name, email,
2708+ password, name, captcha_id,
2709+ captcha_solution)
2710+ # Update validation page's title, which contains the email
2711+ self.next = self.wizard()._pages[self.wizard().email_verification]
2712+ self.wizard().page(self.next).set_titles(email)
2713+ # pylint: enable=W0212
2714+
2715+ def is_correct_email(self, email_address):
2716+ """Return if the email is correct."""
2717+ logger.debug('SetUpAccountPage.is_correct_email')
2718+ return '@' in email_address
2719+
2720+ def is_correct_email_confirmation(self, email_address):
2721+ """Return that the email is the same."""
2722+ logger.debug('SetUpAccountPage.is_correct_email_confirmation')
2723+ return unicode(self.ui.email_edit.text()) == email_address
2724+
2725+ def is_correct_password_confirmation(self, password):
2726+ """Return that the passwords are correct."""
2727+ logger.debug('SetUpAccountPage.is_correct_password_confirmation')
2728+ return unicode(self.ui.password_edit.text()) == password
2729
2730 def focus_changed(self, old, now):
2731 """Check who has the focus to activate password popups if necessary."""
2732@@ -167,6 +464,7 @@
2733 label.setText(ERROR % msg)
2734 label.setVisible(True)
2735
2736+ #pylint: disable=C0103
2737 def showEvent(self, event):
2738 """Set set_up_button as default button when the page is shown."""
2739 # This method should stays here because if we move it to initializePage
2740@@ -183,7 +481,7 @@
2741 self.focus_changed)
2742 super(SetupAccountPage, self).showEvent(event)
2743 if not self.captcha_received:
2744- self.wizard().overlay.show()
2745+ self.overlay.show()
2746
2747 def hideEvent(self, event):
2748 """Disconnect the focusChanged signal when the page change."""
2749@@ -196,31 +494,26 @@
2750 except TypeError:
2751 pass
2752 super(SetupAccountPage, self).hideEvent(event)
2753+ #pylint: enable=C0103
2754
2755 def on_captcha_refreshing(self):
2756 """Show overlay when captcha is refreshing."""
2757 if self.isVisible():
2758- self.wizard().overlay.show()
2759+ self.overlay.show()
2760 self.captcha_received = False
2761
2762 def on_captcha_refresh_complete(self):
2763 """Hide overlay when captcha finished refreshing."""
2764- self.wizard().overlay.hide()
2765+ self.overlay.hide()
2766 self.captcha_received = True
2767
2768-
2769-def is_min_required_password(password):
2770- """Return if the password meets the minimum requirements."""
2771- if (len(password) < 8 or
2772- re.search('[A-Z]', password) is None or
2773- re.search('\d+', password) is None):
2774- return False
2775- return True
2776-
2777-
2778-# pylint: disable=W0511
2779-# FIXME: this should do the same check as SSO's server side
2780-def is_correct_email(email_address):
2781- """Return if the email is correct."""
2782- return '@' in email_address
2783-# pylint: enable=W0511
2784+ def get_captcha_image(self):
2785+ """Return the path to the captcha image."""
2786+ return self.ui.captcha_view.pixmap()
2787+
2788+ def set_captcha_image(self, pixmap_image):
2789+ """Set the new image of the captcha."""
2790+ # lets set the QPixmap for the label
2791+ self.ui.captcha_view.setPixmap(pixmap_image)
2792+
2793+ captcha_image = property(get_captcha_image, set_captcha_image)
2794
2795=== modified file 'ubuntu_sso/qt/sign_in_page.py'
2796--- ubuntu_sso/qt/sign_in_page.py 2012-02-02 18:54:38 +0000
2797+++ ubuntu_sso/qt/sign_in_page.py 2012-02-10 11:32:20 +0000
2798@@ -1,5 +1,5 @@
2799 # -*- coding: utf-8 -*-
2800-
2801+#
2802 # Copyright 2012 Canonical Ltd.
2803 #
2804 # This program is free software: you can redistribute it and/or modify it
2805@@ -21,17 +21,55 @@
2806 from PyQt4 import QtGui
2807
2808 from ubuntu_sso.qt.gui import SSOWizardPage
2809+from ubuntu_sso.logger import setup_logging
2810+from ubuntu_sso.utils.ui import (
2811+ CONGRATULATIONS,
2812+ EXISTING_ACCOUNT_CHOICE_BUTTON,
2813+ SET_UP_ACCOUNT_CHOICE_BUTTON,
2814+)
2815
2816
2817 _ = gettext.gettext
2818+logger = setup_logging('ubuntu_sso.sing_in_page')
2819
2820
2821 class SignInPage(SSOWizardPage):
2822 """Wizard Page that lets the user Sign into Ubuntu One."""
2823
2824- def __init__(self, image_pixmap, *args, **kwargs):
2825- super(SignInPage, self).__init__(*args, **kwargs)
2826+ def __init__(self, ui, image_pixmap, *args, **kwargs):
2827+ super(SignInPage, self).__init__(ui, *args, **kwargs)
2828 self.ui.image_label.setPixmap(image_pixmap)
2829+ self._set_up_translated_strings()
2830+ self._connect_buttons()
2831+
2832+ def _set_up_translated_strings(self):
2833+ """Set the correct strings for the UI."""
2834+ logger.debug('SingInPage._set_up_translated_strings')
2835+ congratulations = CONGRATULATIONS.format(app_name=self.app_name)
2836+ self.ui.message_label.setText(congratulations)
2837+ self.ui.existing_account_button.setText(
2838+ EXISTING_ACCOUNT_CHOICE_BUTTON)
2839+ self.ui.setup_account_button.setText(
2840+ SET_UP_ACCOUNT_CHOICE_BUTTON)
2841+
2842+ def _connect_buttons(self):
2843+ """Connect the buttons to the actions to perform."""
2844+ logger.debug('SingInPage._connect_buttons')
2845+ self.ui.existing_account_button.clicked.connect(
2846+ self._set_next_existing)
2847+ self.ui.setup_account_button.clicked.connect(self._set_next_new)
2848+
2849+ def _set_next_existing(self):
2850+ """Set the next id and fire signal."""
2851+ logger.debug('SignInPage._set_next_existing')
2852+ self.next = self.wizard().current_user_page_id
2853+ self.wizard().next()
2854+
2855+ def _set_next_new(self):
2856+ """Set the next id and fire signal."""
2857+ logger.debug('SignInPage._set_next_new')
2858+ self.next = self.wizard().setup_account_page_id
2859+ self.wizard().next()
2860
2861 # Invalid names of Qt-inherited methods
2862 # pylint: disable=C0103
2863
2864=== added file 'ubuntu_sso/qt/success_page.py'
2865--- ubuntu_sso/qt/success_page.py 1970-01-01 00:00:00 +0000
2866+++ ubuntu_sso/qt/success_page.py 2012-02-10 11:32:20 +0000
2867@@ -0,0 +1,27 @@
2868+# -*- coding: utf-8 -*-
2869+#
2870+# Copyright 2012 Canonical Ltd.
2871+#
2872+# This program is free software: you can redistribute it and/or modify it
2873+# under the terms of the GNU General Public License version 3, as published
2874+# by the Free Software Foundation.
2875+#
2876+# This program is distributed in the hope that it will be useful, but
2877+# WITHOUT ANY WARRANTY; without even the implied warranties of
2878+# MERCHANTABILITY, SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR
2879+# PURPOSE. See the GNU General Public License for more details.
2880+#
2881+# You should have received a copy of the GNU General Public License along
2882+# with this program. If not, see <http://www.gnu.org/licenses/>.
2883+
2884+"""Success page UI."""
2885+
2886+from ubuntu_sso.qt.gui import SSOWizardPage
2887+
2888+
2889+class SuccessPage(SSOWizardPage):
2890+ """Page used to display success message."""
2891+
2892+ def __init__(self, ui, *args, **kwargs):
2893+ super(SuccessPage, self).__init__(ui, *args, **kwargs)
2894+ self.next = -1
2895
2896=== modified file 'ubuntu_sso/qt/tests/__init__.py'
2897--- ubuntu_sso/qt/tests/__init__.py 2012-02-03 15:11:19 +0000
2898+++ ubuntu_sso/qt/tests/__init__.py 2012-02-10 11:32:20 +0000
2899@@ -28,7 +28,8 @@
2900 """Fake an object, record every call."""
2901
2902 next_result = None
2903- exposed_methods = []
2904+ exposed_methods = ['connect_to_signal',
2905+ 'generate_captcha']
2906
2907 def __init__(self, *args, **kwargs):
2908 self._args = args
2909@@ -249,16 +250,47 @@
2910 """Replace wizard() function on wizard pages."""
2911
2912 customButtonClicked = QtCore.QObject()
2913+ sign_in_page_id = 1
2914+ setup_account_id = 2
2915+ current_user_id = 3
2916+ email_verification_id = 4
2917+ success_id = 5
2918+ error_id = 6
2919+ forgotten_id = 7
2920+ reset_password_id = 8
2921
2922 def __init__(self):
2923 self.overlay = FakeOverlay()
2924 self.called = []
2925 self.buttons = {}
2926+ self.buttons_text = {}
2927 self._next_id = -1
2928+ self.sign_in_page = object()
2929+ self.setup_account = object()
2930+ self.current_user = object()
2931+ self.email_verification = object()
2932+ self.success = object()
2933+ self.error = object()
2934+ self.forgotten = object()
2935+ self.reset_password = object()
2936+ self._pages = {
2937+ self.sign_in_page: self.sign_in_page_id,
2938+ self.setup_account: self.setup_account_id,
2939+ self.current_user: self.current_user_id,
2940+ self.email_verification: self.email_verification_id,
2941+ self.success: self.success_id,
2942+ self.error: self.error_id,
2943+ self.forgotten: self.forgotten_id,
2944+ self.reset_password: self.reset_password_id,
2945+ }
2946
2947 # Invalid name "setButtonLayout", "setOption"
2948 # pylint: disable=C0103
2949
2950+ def setButtonText(self, key, value):
2951+ """Fake setButtonText."""
2952+ self.buttons_text[key] = value
2953+
2954 def setButtonLayout(self, *args, **kwargs):
2955 """Fake the functionality of setButtonLayout on QWizard class."""
2956 self.called.append(('setButtonLayout', (args, kwargs)))
2957@@ -273,6 +305,18 @@
2958 """Fake the functionality of button on QWizard class."""
2959 return self.buttons.setdefault(button_id, QtGui.QPushButton())
2960
2961+ def next(self):
2962+ """Fake next."""
2963+ self.called.append('next')
2964+
2965+ def page(self, p_id):
2966+ """Fake page method."""
2967+ return self
2968+
2969+ def set_titles(self, title):
2970+ """Fake set_titles."""
2971+ self.called.append(('set_titles', title))
2972+
2973
2974 class FakeWizardButtonStyle(FakeWizard):
2975
2976
2977=== removed file 'ubuntu_sso/qt/tests/test_controllers.py'
2978--- ubuntu_sso/qt/tests/test_controllers.py 2012-02-06 16:18:24 +0000
2979+++ ubuntu_sso/qt/tests/test_controllers.py 1970-01-01 00:00:00 +0000
2980@@ -1,2113 +0,0 @@
2981-# -*- coding: utf-8 -*-
2982-
2983-# Authors: Manuel de la Pena <manuel@canonical.com>
2984-# Natalia B. Bidart <natalia.bidart@canonical.com>
2985-#
2986-# Copyright 2011 Canonical Ltd.
2987-#
2988-# This program is free software: you can redistribute it and/or modify it
2989-# under the terms of the GNU General Public License version 3, as published
2990-# by the Free Software Foundation.
2991-#
2992-# This program is distributed in the hope that it will be useful, but
2993-# WITHOUT ANY WARRANTY; without even the implied warranties of
2994-# MERCHANTABILITY, SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR
2995-# PURPOSE. See the GNU General Public License for more details.
2996-#
2997-# You should have received a copy of the GNU General Public License along
2998-# with this program. If not, see <http://www.gnu.org/licenses/>.
2999-
3000-"""Test the ui controllers."""
3001-
3002-import collections
3003-
3004-from twisted.internet import defer
3005-
3006-# pylint: disable=F0401,E0611
3007-
3008-from PyQt4.QtCore import QString, SIGNAL
3009-from PyQt4.QtGui import QCheckBox, QLabel, QLineEdit, QWizard
3010-
3011-try:
3012- from PIL import Image
3013-except ImportError:
3014- import Image
3015-# pylint: enable=F0401,E0611
3016-
3017-from ubuntu_sso.qt.controllers import (
3018- BackendController,
3019- ChooseSignInController,
3020- CurrentUserController,
3021- EmailVerificationController,
3022- ErrorController,
3023- ForgottenPasswordController,
3024- ResetPasswordController,
3025- SetUpAccountController,
3026- SuccessController,
3027- UbuntuSSOWizardController,
3028-)
3029-from ubuntu_sso.qt.tests import BaseTestCase, FakePageUiStyle
3030-from ubuntu_sso.utils.ui import (
3031- CAPTCHA_REQUIRED_ERROR,
3032- CAPTCHA_SOLUTION_ENTRY,
3033- EMAIL1_ENTRY,
3034- EMAIL2_ENTRY,
3035- EMAIL_INVALID,
3036- EMAIL_MISMATCH,
3037- ERROR,
3038- EMAIL_LABEL,
3039- EXISTING_ACCOUNT_CHOICE_BUTTON,
3040- FORGOTTEN_PASSWORD_BUTTON,
3041- JOIN_HEADER_LABEL,
3042- LOGIN_PASSWORD_LABEL,
3043- NAME_ENTRY,
3044- NAME_INVALID,
3045- PASSWORD1_ENTRY,
3046- PASSWORD2_ENTRY,
3047- PASSWORD_HELP,
3048- PASSWORD_TOO_WEAK,
3049- PASSWORD_MISMATCH,
3050- RESET_PASSWORD,
3051- REQUEST_PASSWORD_TOKEN_LABEL,
3052- SET_UP_ACCOUNT_CHOICE_BUTTON,
3053- SIGN_IN_BUTTON,
3054- SUCCESS,
3055- REQUEST_PASSWORD_TOKEN_WRONG_EMAIL,
3056- REQUEST_PASSWORD_TOKEN_TECH_ERROR,
3057- TRY_AGAIN_BUTTON,
3058- VERIFY_EMAIL_CONTENT,
3059- VERIFY_EMAIL_TITLE,
3060-)
3061-
3062-#ignore the comon mocker issues with lint
3063-# pylint: disable=W0212,W0104,W0106,E1103
3064-
3065-
3066-class FakeButton(FakePageUiStyle):
3067-
3068- """Fake Button."""
3069-
3070- def __init__(self):
3071- """Initialize object."""
3072- super(FakeButton, self).__init__()
3073- self.function = None
3074- self.clicked = self
3075-
3076- def connect(self, function):
3077- """Receive the function that should be connected to this button."""
3078- self.function = function
3079-
3080-
3081-class FakeField(object):
3082-
3083- """Fake Field for text input."""
3084-
3085- def __init__(self, text):
3086- self.text = text
3087-
3088- # pylint: disable=C0103
3089- def toString(self):
3090- """Fake toString method for input fields."""
3091- return self.text
3092- # pylint: enable=C0103
3093-
3094-
3095-class FakeWizard(object):
3096-
3097- """Fake Wizard."""
3098-
3099- current_user_page_id = 1
3100- setup_account_page_id = 2
3101- forgotten_password_page_id = 3
3102- reset_password_page_id = 4
3103-
3104- def __init__(self, view):
3105- """Initialize object."""
3106- self.properties = {}
3107- self.app_name = 'app_name'
3108- self.help_text = 'help'
3109- self.view = view
3110- self.next_id = 0
3111- # pylint: disable=C0103
3112- self.loginSuccess = self
3113- self.registrationSuccess = self
3114- # pylint: enable=C0103
3115- self.forgotten = self
3116- self.ui = self
3117- self.email_line_edit = QLineEdit()
3118-
3119- # pylint: disable=C0103
3120- def registerField(self, key, text):
3121- """Fake registerField from wizard."""
3122- self.properties['field_%s' % key] = FakeField(text)
3123- # pylint: enable=C0103
3124-
3125- def field(self, key):
3126- """Fake field from wizard"""
3127- return self.properties['field_%s' % key]
3128-
3129- def next(self):
3130- """Fake next method to move to to next page in the wizard."""
3131- self.view.properties['wizard_next'] = True
3132-
3133- def emit(self, name, email):
3134- """Fake emit."""
3135- self.properties['emit'] = (name, email)
3136-
3137-
3138-class FakeGenericView(object):
3139-
3140- """Fake Generic page."""
3141-
3142- def __init__(self):
3143- """Initialize object."""
3144- self.properties = {}
3145- self.ui = self
3146- self.header = self
3147- self.properties['currentId'] = -1
3148- self.next = 0
3149- self.fake_wizard = FakeWizard(self)
3150-
3151- def set_title(self, title):
3152- """Fake set_title for the page."""
3153- self.properties['title'] = title
3154-
3155- def set_subtitle(self, subtitle):
3156- """Fake set_subtitle for the page"""
3157- self.properties['subtitle'] = subtitle
3158-
3159- # pylint: disable=C0103
3160- def setTitle(self, title):
3161- """Fake setTitle."""
3162- self.set_title(title)
3163-
3164- def setSubTitle(self, subtitle):
3165- """Fake setTitle."""
3166- self.set_subtitle(subtitle)
3167- # pylint: enable=C0103
3168-
3169- def wizard(self):
3170- """Fake wizard is returned."""
3171- return self.fake_wizard
3172-
3173-
3174-class FakeChooseSignInView(FakeGenericView):
3175-
3176- """Fake ChooseSignIn page."""
3177-
3178- def __init__(self):
3179- """Initialize object."""
3180- super(FakeChooseSignInView, self).__init__()
3181- self.existing_account_button = FakeButton()
3182- self.setup_account_button = FakeButton()
3183-
3184-
3185-class FakeCurrentUserView(FakeGenericView):
3186-
3187- """Fake Current User view."""
3188-
3189- def __init__(self):
3190- super(FakeCurrentUserView, self).__init__()
3191- self.email_label = QLabel()
3192- self.password_label = QLabel()
3193- self.forgot_password_label = QLabel()
3194- self.sign_in_button = FakeButton()
3195- self.email_edit = QLineEdit()
3196- self.password_edit = QLineEdit()
3197- self.on_logged_in_cb = None
3198- self.on_login_error_cb = None
3199-
3200- def fake_backend(self):
3201- """Fake get_backend."""
3202- return self
3203-
3204- def login(self, appname, email, password):
3205- """Fake backend login."""
3206- self.properties['backend_login'] = (appname, email, password)
3207- return self
3208-
3209- # pylint: disable=C0103
3210- def addErrback(self, function):
3211- """Fake defer addErrback."""
3212- self.properties['login-addErrback'] = function
3213- # pylint: enable=C0103
3214-
3215-
3216-class FakeLineEdit(object):
3217-
3218- """A fake QLineEdit."""
3219-
3220- def __init__(self, *args):
3221- """Initialize."""
3222- self._text = u""
3223-
3224- def text(self):
3225- """Save text."""
3226- return self._text
3227-
3228- # setText is inherited
3229- # pylint: disable=C0103
3230- def setText(self, text):
3231- """Return saved text."""
3232- self._text = text
3233-
3234-
3235-class FakePage(object):
3236-
3237- """A fake wizard page."""
3238-
3239- app_name = "APP"
3240-
3241- def wizard(self):
3242- """Fake wizard."""
3243- return self
3244-
3245-
3246-class FakeCurrentUserPage(FakePageUiStyle):
3247-
3248- """Fake CurrentuserPage."""
3249-
3250- def __init__(self, *args):
3251- """Initialize."""
3252- super(FakeCurrentUserPage, self).__init__(*args)
3253- self.ui.email_edit = FakeLineEdit()
3254- self.ui.password_edit = FakeLineEdit()
3255- self.sign_in_button = self
3256-
3257-
3258-class FakeSetupAccountPageView(FakeGenericView):
3259-
3260- """Fake SetupAccount view."""
3261-
3262- def __init__(self):
3263- super(FakeSetupAccountPageView, self).__init__()
3264- self.ui = self
3265- # Labels
3266- self.name_label = QLabel()
3267- self.email_label = QLabel()
3268- self.confirm_email_label = QLabel()
3269- self.password_label = QLabel()
3270- self.confirm_password_label = QLabel()
3271- self.password_info_label = QLabel()
3272- self.refresh_label = QLabel()
3273- self.name_assistance = QLabel()
3274- self.email_assistance = QLabel()
3275- self.confirm_email_assistance = QLabel()
3276- # Line Edits
3277- self.captcha_solution_edit = QLineEdit()
3278- self.name_edit = QLineEdit()
3279- self.email_edit = QLineEdit()
3280- self.confirm_email_edit = QLineEdit()
3281- self.password_edit = QLineEdit()
3282- self.confirm_password_edit = QLineEdit()
3283- self.captcha_solution_edit = QLineEdit()
3284- # Check Box
3285- self.terms_checkbox = QCheckBox()
3286- # Setup Button
3287- self.set_up_button = FakeButton()
3288- # Backend
3289- self.on_captcha_generated_cb = None
3290- self.on_captcha_generation_error_cb = None
3291- self.on_user_registered_cb = None
3292- self.on_user_registration_error_cb = None
3293-
3294- def fake_backend(self):
3295- """Fake get_backend."""
3296- return self
3297-
3298- def set_line_edit_validation_rule(self, edit, function):
3299- """Fake set_line_edit_validation_rule from view."""
3300- elements = self.properties.get('validation_rule', None)
3301- if elements is None:
3302- elements = []
3303- elements.append((edit, function))
3304- self.properties['validation_rule'] = elements
3305-
3306- # pylint: disable=C0103
3307- def registerField(self, key, edit):
3308- """Fake registerField from page."""
3309- self.properties[key] = edit
3310- # pylint: enable=C0103
3311-
3312- def set_error_message(self, label, message):
3313- """Fake set_error_message."""
3314- label.setText(message)
3315-
3316-
3317-class FakeMessageBox(object):
3318-
3319- """A fake message box."""
3320-
3321- args = None
3322- critical_args = None
3323-
3324- def __init__(self, *args, **kwargs):
3325- self.args = (args, kwargs)
3326-
3327- def critical(self, *args, **kwargs):
3328- """Fake critical popup."""
3329- self.critical_args = (args, kwargs)
3330-
3331-
3332-class FakeSetupAccountUi(object):
3333- """Fake ui variable for FakeView."""
3334-
3335- email_assistance = None
3336-
3337-
3338-class FakeView(object):
3339-
3340- """A fake view."""
3341-
3342- app_name = "TestApp"
3343- ui = FakeSetupAccountUi()
3344-
3345- def wizard(self):
3346- """Use itself as a fake wizard, too."""
3347- return self
3348-
3349- def set_error_message(self, label, msg):
3350- """Simulate to add the message to the label as SetupAccount do."""
3351-
3352-
3353-class FakeSetupAccountView(FakeView):
3354-
3355- """A Fake view."""
3356-
3357- captcha_file = None
3358- captcha_image = None
3359- captcha_refresh_executed = False
3360- captcha_refreshing_value = False
3361-
3362- def on_captcha_refresh_complete(self):
3363- """Fake the call to refresh finished."""
3364- self.captcha_refresh_executed = True
3365-
3366- def on_captcha_refreshing(self):
3367- """Fake the call to refreshing."""
3368- self.captcha_refreshing_value = True
3369-
3370- def fake_open(self, filename):
3371- """Fake open for Image."""
3372- return self
3373-
3374- # pylint: disable=W0622
3375- def save(self, io, format):
3376- """Fake save for Image."""
3377- # pylint: enable=W0622
3378-
3379-
3380-class FakeControllerForCaptcha(object):
3381-
3382- """Fake controller to test refresh_captcha method."""
3383-
3384- def __init__(self):
3385- """Initialize FakeControllerForCaptcha."""
3386- self.callback_error = False
3387-
3388- def generate_captcha(self, *args):
3389- """Fake generate deferred for captcha."""
3390- return self
3391-
3392- # pylint: disable=C0103
3393- def addErrback(self, call):
3394- """Fake addErrback."""
3395- self.callback_error = call is not None
3396- # pylint: enable=C0103
3397-
3398-
3399-class FakeEmailVerificationView(FakePageUiStyle):
3400- """Fake EmailVerification Page."""
3401-
3402- def __init__(self, *args):
3403- """Initialize."""
3404- super(FakeEmailVerificationView, self).__init__(*args)
3405- self.verification_code = ''
3406- self.next_button = self
3407-
3408-
3409-class EmailVerificationView(FakeGenericView):
3410-
3411- """Fake Email Verification view."""
3412-
3413- def __init__(self):
3414- super(EmailVerificationView, self).__init__()
3415- self.ui = self
3416- # Line Edits
3417- self.verification_code_edit = QLineEdit()
3418- self.verification_code = ''
3419- # Setup Button
3420- self.next_button = FakeButton()
3421- # Backend
3422- self.on_email_validated_cb = None
3423- self.on_email_validation_error_cb = None
3424-
3425- def fake_backend(self):
3426- """Fake get_backend."""
3427- return self
3428-
3429- def validate_email(self, *args, **kwargs):
3430- """Fake validate_email method from backend"""
3431- self.properties['validate_email'] = (args, kwargs)
3432-
3433-
3434-class ErrorPageView(FakeGenericView):
3435-
3436- """Fake Error Page view."""
3437-
3438- def __init__(self):
3439- super(ErrorPageView, self).__init__()
3440- self.ui = self
3441- self.error_message_label = QLabel()
3442-
3443-
3444-class SuccessPageView(FakeGenericView):
3445-
3446- """Fake Success Page view."""
3447-
3448- def __init__(self):
3449- super(SuccessPageView, self).__init__()
3450- self.ui = self
3451- self.success_message_body = QLabel()
3452-
3453-
3454-class UbuntuSSOView(object):
3455-
3456- """Fake view for UbuntuSSO."""
3457-
3458- success_page_id = 5
3459- error_page_id = 6
3460-
3461- def __init__(self):
3462- self.properties = {}
3463- self.ui = self
3464- self.app_name = 'app_name'
3465- self.success_message_label = QLabel()
3466- self.result = 0
3467- # pylint: disable=C0103
3468- self.loginSuccess = FakeButton()
3469- self.registrationSuccess = FakeButton()
3470- # pylint: enable=C0103
3471- self.page = FakeGenericView()
3472-
3473- def button(self, *args):
3474- """Fake button."""
3475- self.properties['button_method'] = args
3476- self.properties['button'] = FakeButton()
3477- return self.properties['button']
3478-
3479- def callback(self, *args, **kwargs):
3480- """Fake callback."""
3481- self.properties['callback'] = (args, kwargs)
3482- return self.result
3483-
3484- def close(self):
3485- """Fake close."""
3486- self.properties['close'] = True
3487-
3488- # pylint: disable=C0103
3489- def setButtonLayout(self, layout):
3490- """Fake setButtonLayout."""
3491- self.properties['buttons_layout'] = layout
3492-
3493- def currentPage(self):
3494- """Fake currentPage."""
3495- return self.page
3496-
3497- def setWizardStyle(self, style):
3498- """Fake setWizardStyle."""
3499- self.properties['wizard_style'] = style
3500- # pylint: enable=C0103
3501-
3502- def next(self):
3503- """Fake next method to move to to next page in the wizard."""
3504- self.properties['wizard_next'] = True
3505-
3506-
3507-class FakeLineEditForForgottenPage(object):
3508-
3509- """Fake Line Edit"""
3510-
3511- def __init__(self):
3512- """Initilize object."""
3513- self.email_text = QString()
3514-
3515- def text(self):
3516- """Fake text for QLineEdit."""
3517- return self.email_text
3518-
3519- # setText is inherited
3520- # pylint: disable=C0103
3521- def setText(self, text):
3522- """Fake setText for QLineEdit."""
3523- self.email_text = QString(text)
3524- # pylint: enable=C0103
3525-
3526-
3527-class FakeForgottenPasswordPage(FakePageUiStyle):
3528- """Fake the page."""
3529-
3530- def __init__(self):
3531- super(FakeForgottenPasswordPage, self).__init__()
3532- self.email_address_line_edit = self
3533- self.send_button = self
3534- self.email_widget = FakePageUiStyle()
3535- self.forgotted_password_intro_label = FakePageUiStyle()
3536- self.try_again_widget = FakePageUiStyle()
3537- self.email_line_edit = FakeLineEditForForgottenPage()
3538-
3539- def fake_backend(self):
3540- """Fake get_backend."""
3541- return self
3542-
3543-
3544-class FakeForgottenPasswordPageView(FakeGenericView):
3545- """Fake the page."""
3546-
3547- def __init__(self):
3548- super(FakeForgottenPasswordPageView, self).__init__()
3549- self.email_address_line_edit = QLineEdit()
3550- self.send_button = FakeButton()
3551- self.try_again_button = FakeButton()
3552- self.email_widget = FakePageUiStyle()
3553- self.forgotted_password_intro_label = QLabel()
3554- self.email_address_label = QLabel()
3555- self.try_again_widget = FakePageUiStyle()
3556- self.email_line_edit = QLineEdit()
3557- # Backend
3558- self.on_password_reset_error_cb = None
3559- self.on_password_reset_token_sent_cb = None
3560-
3561- def fake_backend(self):
3562- """Fake get_backend."""
3563- return self
3564-
3565- # pylint: disable=C0103
3566- def registerField(self, key, item):
3567- """Fake registerField from wizard."""
3568- self.properties['field_%s' % key] = item
3569- # pylint: enable=C0103
3570-
3571- def field(self, key):
3572- """Fake field from wizard"""
3573- return self.properties['field_%s' % key]
3574-
3575- def set_line_edit_validation_rule(self, edit, function):
3576- """Fake set_line_edit_validation_rule from view."""
3577- elements = self.properties.get('validation_rule', None)
3578- if elements is None:
3579- elements = []
3580- elements.append((edit, function))
3581- self.properties['validation_rule'] = elements
3582-
3583-
3584-class ResetPasswordPageView(FakeGenericView):
3585- """Fake the page."""
3586-
3587- def __init__(self):
3588- super(ResetPasswordPageView, self).__init__()
3589- self.reset_password_button = FakeButton()
3590- self.reset_code_line_edit = QLineEdit()
3591- self.password_line_edit = QLineEdit()
3592- self.confirm_password_line_edit = QLineEdit()
3593- # Backend
3594- self.on_password_changed_cb = None
3595- self.on_password_change_error_cb = None
3596-
3597- def fake_backend(self):
3598- """Fake get_backend."""
3599- return self
3600-
3601- # pylint: disable=C0103
3602- def registerField(self, key, item):
3603- """Fake registerField from wizard."""
3604- self.properties['field_%s' % key] = item
3605- # pylint: enable=C0103
3606-
3607- def field(self, key):
3608- """Fake field from wizard"""
3609- return self.properties['field_%s' % key]
3610-
3611- def set_line_edit_validation_rule(self, edit, function):
3612- """Fake set_line_edit_validation_rule from view."""
3613- elements = self.properties.get('validation_rule', None)
3614- if elements is None:
3615- elements = []
3616- elements.append((edit, function))
3617- self.properties['validation_rule'] = elements
3618-
3619- def set_new_password(self, *args, **kwargs):
3620- """Fake set_new_password from backend."""
3621- self.properties['backend_new_password'] = (args, kwargs)
3622-
3623-
3624-class FakeResetPasswordPage(FakePageUiStyle):
3625-
3626- """Fake ResetPasswordPage."""
3627-
3628- def __init__(self, *args):
3629- """Initialize."""
3630- super(FakeResetPasswordPage, self).__init__()
3631- self.ui.reset_code_line_edit = FakeLineEdit()
3632- self.ui.password_line_edit = FakeLineEdit()
3633- self.ui.confirm_password_line_edit = FakeLineEdit()
3634- self.reset_password_button = self
3635- self._enabled = 9 # Intentionally wrong.
3636-
3637-
3638-class FakeWizardForResetPassword(object):
3639-
3640- """Fake Wizard for ResetPasswordController."""
3641-
3642- def __init__(self):
3643- self.current_user = self
3644- self.ui = self
3645- self.email_edit = self
3646- self.email_line_edit = self
3647- self.forgotten = self
3648- self.overlay = self
3649- self.count_back = 0
3650- self.hide_value = False
3651- self.text_value = 'mail@mail.com'
3652- self.current_user_page_id = 4
3653-
3654- def wizard(self):
3655- """Fake wizard function for view."""
3656- return self
3657-
3658- def back(self):
3659- """Fake back for wizard."""
3660- self.count_back += 1
3661-
3662- def hide(self):
3663- """Fake hide for overlay."""
3664- self.hide_value = True
3665-
3666- def text(self):
3667- """Fake text for QLineEdit."""
3668- return self.text_value
3669-
3670- # pylint: disable=C0103
3671- def setText(self, text):
3672- """Fake setText for QLineEdit."""
3673- self.text_value = text
3674-
3675- def visitedPages(self):
3676- """Return an int list of fake visited pages."""
3677- return [1, 4, 6, 8]
3678- # pylint: enable=C0103
3679-
3680-
3681-class BackendControllerTestCase(BaseTestCase):
3682- """The test case for the BackendController."""
3683-
3684- @defer.inlineCallbacks
3685- def setUp(self):
3686- yield super(BackendControllerTestCase, self).setUp()
3687- self.backend = BackendController()
3688-
3689- @defer.inlineCallbacks
3690- def test_get_backend(self):
3691- """The backend is properly retrieved."""
3692- result = yield self.backend.get_backend()
3693- self.assertTrue(self.sso_login_backend is result)
3694-
3695-
3696-class ChooseSignInControllerTestCase(BaseTestCase):
3697- """Test the choose sign in controller."""
3698-
3699- @defer.inlineCallbacks
3700- def setUp(self):
3701- """Set tests."""
3702- yield super(ChooseSignInControllerTestCase, self).setUp()
3703- self.title = 'title'
3704- self.subtitle = 'subtitle'
3705- self.controller = ChooseSignInController(self.title, self.subtitle)
3706- self.view = FakeChooseSignInView()
3707- self.controller.view = self.view
3708-
3709- def test_setup_ui(self):
3710- """Test the set up of the ui."""
3711- self.controller.setupUi(self.view)
3712- self.assertEqual(self.view.properties['title'], self.title)
3713- self.assertEqual(self.view.properties['subtitle'], self.subtitle)
3714- self.assertEqual(self.view.existing_account_button.text(),
3715- EXISTING_ACCOUNT_CHOICE_BUTTON)
3716- self.assertEqual(self.view.setup_account_button.text(),
3717- SET_UP_ACCOUNT_CHOICE_BUTTON)
3718- self.assertIsInstance(self.view.existing_account_button.function,
3719- collections.Callable)
3720- self.assertIsInstance(self.view.setup_account_button.function,
3721- collections.Callable)
3722-
3723- def test_set_up_translated_strings(self):
3724- """Ensure that the translations are used."""
3725- self.controller._set_up_translated_strings()
3726- self.assertEqual(self.view.existing_account_button.text(),
3727- EXISTING_ACCOUNT_CHOICE_BUTTON)
3728- self.assertEqual(self.view.setup_account_button.text(),
3729- SET_UP_ACCOUNT_CHOICE_BUTTON)
3730-
3731- def test_connect_buttons(self):
3732- """Ensure that all the buttons are correcly connected."""
3733- self.controller._connect_buttons()
3734- self.assertIsInstance(self.view.existing_account_button.function,
3735- collections.Callable)
3736- self.assertIsInstance(self.view.setup_account_button.function,
3737- collections.Callable)
3738-
3739- def test_set_next_existing(self):
3740- """Test the execution of the callback."""
3741- self.controller._set_next_existing()
3742- self.assertEqual(self.view.next,
3743- self.view.fake_wizard.current_user_page_id)
3744-
3745- def test_set_next_new(self):
3746- """Test the execution of the callback."""
3747- self.controller._set_next_new()
3748- self.assertEqual(self.view.next,
3749- self.view.fake_wizard.setup_account_page_id)
3750-
3751-
3752-class CurrentUserControllerTestCase(BaseTestCase):
3753- """Test the current user controller."""
3754-
3755- @defer.inlineCallbacks
3756- def setUp(self):
3757- """Setup tests."""
3758- yield super(CurrentUserControllerTestCase, self).setUp()
3759- self.controller = CurrentUserController(title='the title',
3760- subtitle='the subtitle')
3761- self.view = FakeCurrentUserView()
3762- self.controller.view = self.view
3763- self.patch(self.controller, "get_backend", self.view.fake_backend)
3764- self.controller.setupUi(self.view)
3765-
3766- def test_translated_strings(self):
3767- """test that the ui is correctly set up."""
3768- self.controller._set_translated_strings()
3769- self.assertEqual(self.view.email_label.text(), EMAIL_LABEL)
3770- self.assertEqual(self.view.password_label.text(), LOGIN_PASSWORD_LABEL)
3771- self.assertEqual(self.view.forgot_password_label.text(),
3772- FORGOTTEN_PASSWORD_BUTTON)
3773- self.assertEqual(self.view.sign_in_button.text(), SIGN_IN_BUTTON)
3774-
3775- def test_connect_ui(self):
3776- """test that the ui is correctly set up."""
3777- self.controller._connect_ui()
3778- self.assertIsInstance(self.view.sign_in_button.function,
3779- collections.Callable)
3780- # Check if receivers is 2 because _connect_ui was called on setUp
3781- self.assertEqual(self.view.forgot_password_label.receivers(
3782- SIGNAL('linkActivated(QString)')), 2)
3783- self.assertEqual(self.view.email_edit.receivers(
3784- SIGNAL('textChanged(QString)')), 2)
3785- self.assertEqual(self.view.password_edit.receivers(
3786- SIGNAL('textChanged(QString)')), 2)
3787- self.assertFalse(self.view.on_login_error_cb is None)
3788- self.assertFalse(self.view.on_logged_in_cb is None)
3789-
3790- def test_title_subtitle(self):
3791- """Ensure we are storing the title and subtitle correctly."""
3792- self.assertEqual(self.view.properties['title'], 'the title')
3793- self.assertEqual(self.view.properties['subtitle'], 'the subtitle')
3794-
3795- def test_on_logged_in(self):
3796- """Test on_logged_in method."""
3797- self.view.email_edit.setText('email@email.com')
3798- self.controller.on_logged_in('app-name', {})
3799- self.assertEqual(self.view.fake_wizard.properties['emit'],
3800- ('app-name', 'email@email.com'))
3801-
3802- def test_login(self):
3803- """Test login method."""
3804- email = 'email@email.com'
3805- password = 'password'
3806- self.view.email_edit.setText(email)
3807- self.view.password_edit.setText(password)
3808- self.controller.login()
3809- self.assertEqual(self.view.properties['backend_login'],
3810- (self.view.fake_wizard.app_name, email, password))
3811- self.assertIsInstance(self.view.properties['login-addErrback'],
3812- collections.Callable)
3813-
3814- def test_on_forgotten_password(self):
3815- """Test on_forgotten_password flow."""
3816- email = 'email@email.com'
3817- self.view.email_edit.setText(email)
3818- self.controller.on_forgotten_password()
3819- self.assertEqual(self.view.next,
3820- self.view.fake_wizard.forgotten_password_page_id)
3821- self.assertTrue(self.view.properties['wizard_next'])
3822-
3823- def test_setup_ui(self):
3824- """Test the set up of the ui."""
3825- self.controller._title = 'title_setup'
3826- self.controller._subtitle = 'subtitle_setup'
3827- self.controller.setupUi(self.view)
3828- self.assertEqual(self.view.properties['title'], 'title_setup')
3829- self.assertEqual(self.view.properties['subtitle'], 'subtitle_setup')
3830- self.assertIsInstance(self.view.sign_in_button.function,
3831- collections.Callable)
3832- # Check if receivers is 2 because _connect_ui was called on setUp
3833- self.assertEqual(self.view.forgot_password_label.receivers(
3834- SIGNAL('linkActivated(QString)')), 2)
3835- self.assertEqual(self.view.email_edit.receivers(
3836- SIGNAL('textChanged(QString)')), 2)
3837- self.assertEqual(self.view.password_edit.receivers(
3838- SIGNAL('textChanged(QString)')), 2)
3839- self.assertFalse(self.view.on_login_error_cb is None)
3840- self.assertFalse(self.view.on_logged_in_cb is None)
3841- self.assertEqual(self.view.email_label.text(), EMAIL_LABEL)
3842- self.assertEqual(self.view.password_label.text(), LOGIN_PASSWORD_LABEL)
3843- self.assertEqual(self.view.forgot_password_label.text(),
3844- FORGOTTEN_PASSWORD_BUTTON)
3845- self.assertEqual(self.view.sign_in_button.text(), SIGN_IN_BUTTON)
3846-
3847-
3848-class CurrentUserControllerErrorTestCase(BaseTestCase):
3849-
3850- """Tests for CurrentUserController's error handler."""
3851-
3852- on_error_method_name = "on_login_error"
3853- controller_class = CurrentUserController
3854-
3855- @defer.inlineCallbacks
3856- def setUp(self):
3857- """Setup test."""
3858- yield super(CurrentUserControllerErrorTestCase, self).setUp()
3859- self.message_box = FakeMessageBox()
3860- self.controller = self.controller_class(
3861- message_box=self.message_box)
3862- self.controller.view = FakePage()
3863- self.on_error_method = getattr(
3864- self.controller, self.on_error_method_name)
3865-
3866- def test_error_message_key(self):
3867- """Test that on_login_error reacts to errors with "error_message"."""
3868- self.on_error_method({"error_message": "WORRY!"})
3869- self.assertEqual(self.message_box.critical_args, (('WORRY!',
3870- self.controller.view), {}))
3871-
3872- def test_message_key(self):
3873- """Test that on_login_error reacts to errors with "message"."""
3874- self.on_error_method({"message": "WORRY!"})
3875- self.assertEqual(self.message_box.critical_args, (('WORRY!',
3876- self.controller.view), {}))
3877-
3878- def test_broken_error(self):
3879- """Test that on_login_error reacts to broken errors."""
3880- self.on_error_method({"boo!": "WORRY!"})
3881- result = '\n'.join(
3882- [('%s: %s' % (k, v)) for k, v in \
3883- {"boo!": "WORRY!"}.iteritems()])
3884- self.assertEqual(self.message_box.critical_args,
3885- ((result, self.controller.view), {}))
3886-
3887- def test_all_and_message(self):
3888- """Test that on_login_error reacts to broken errors."""
3889- self.on_error_method(
3890- {"message": "WORRY!", "__all__": "MORE!"})
3891- self.assertEqual(self.message_box.critical_args,
3892- (('MORE!\nWORRY!', self.controller.view), {}))
3893-
3894- def test_all_and_error_message(self):
3895- """Test that on_login_error reacts to broken errors."""
3896- self.on_error_method(
3897- {"error_message": "WORRY!", "__all__": "MORE!"})
3898- self.assertEqual(self.message_box.critical_args,
3899- (('MORE!\nWORRY!', self.controller.view), {}))
3900-
3901- def test_only_all(self):
3902- """Test that on_login_error reacts to broken errors."""
3903- self.on_error_method(
3904- {"__all__": "MORE!"})
3905- self.assertEqual(self.message_box.critical_args,
3906- (('MORE!', self.controller.view), {}))
3907-
3908-
3909-class EmailVerificationControllerErrorTestCase(
3910- CurrentUserControllerErrorTestCase):
3911-
3912- """Tests for EmailVerificationController's error handler."""
3913-
3914- on_error_method_name = "on_email_validation_error"
3915- controller_class = EmailVerificationController
3916-
3917- @defer.inlineCallbacks
3918- def setUp(self):
3919- """Setup test."""
3920- yield super(EmailVerificationControllerErrorTestCase, self).setUp()
3921- # This error handler takes one extra argument.
3922- self.on_error_method = lambda error: getattr(
3923- self.controller, self.on_error_method_name)('APP', error)
3924-
3925-
3926-class SetUpAccountControllerErrorTestCase(
3927- EmailVerificationControllerErrorTestCase):
3928-
3929- """Tests for SetUpAccountController's error handler."""
3930-
3931- on_error_method_name = "on_user_registration_error"
3932- controller_class = SetUpAccountController
3933-
3934- @defer.inlineCallbacks
3935- def setUp(self):
3936- """Setup test."""
3937- yield super(SetUpAccountControllerErrorTestCase, self).setUp()
3938- self.patch(self.controller, "_refresh_captcha", lambda *args: None)
3939-
3940-
3941-class ResetPasswordControllerErrorTestCase(
3942- EmailVerificationControllerErrorTestCase):
3943-
3944- """Tests for ResetPasswordController's error handler."""
3945-
3946- on_error_method_name = "on_password_change_error"
3947- controller_class = ResetPasswordController
3948-
3949-
3950-class CurrentUserControllerValidationTest(BaseTestCase):
3951-
3952- """Tests for CurrentUserController, but without Mocker."""
3953-
3954- @defer.inlineCallbacks
3955- def setUp(self):
3956- """Setup test."""
3957- yield super(CurrentUserControllerValidationTest, self).setUp()
3958- self.message_box = FakeMessageBox()
3959- self.controller = CurrentUserController(
3960- message_box=self.message_box)
3961- self.controller.view = FakeCurrentUserPage()
3962-
3963- def test_valid(self):
3964- """Enable the button with a valid email/password."""
3965- self.controller.view.email_edit.setText("a@b")
3966- self.controller.view.password_edit.setText("pass")
3967- self.controller._validate()
3968- self.assertTrue(self.controller.view.sign_in_button.enabled())
3969- self.assertTrue(self.controller.view.properties.get('unpolish', False))
3970- self.assertTrue(self.controller.view.properties.get('polish', False))
3971- self.assertTrue("DisabledState" in self.controller.view.properties)
3972- self.assertEqual(self.controller.view.properties["DisabledState"],
3973- not self.controller.view.sign_in_button.enabled())
3974-
3975- def test_invalid_email(self):
3976- """The submit button should be disabled with an invalid email."""
3977- self.controller.view.email_edit.setText("ab")
3978- self.controller.view.password_edit.setText("pass")
3979- self.controller._validate()
3980- self.assertFalse(self.controller.view.sign_in_button.enabled())
3981- self.assertTrue("DisabledState" in self.controller.view.properties)
3982- self.assertEqual(self.controller.view.properties["DisabledState"],
3983- not self.controller.view.sign_in_button.enabled())
3984- self.assertTrue(self.controller.view.properties.get('unpolish', False))
3985- self.assertTrue(self.controller.view.properties.get('polish', False))
3986-
3987- def test_invalid_password(self):
3988- """The submit button should be disabled with an invalid password."""
3989- self.controller.view.email_edit.setText("a@b")
3990- self.controller.view.password_edit.setText("")
3991- self.controller._validate()
3992- self.assertFalse(self.controller.view.sign_in_button.enabled())
3993- self.assertTrue(self.controller.view.properties.get('unpolish', False))
3994- self.assertTrue(self.controller.view.properties.get('polish', False))
3995- self.assertTrue("DisabledState" in self.controller.view.properties)
3996- self.assertEqual(self.controller.view.properties["DisabledState"],
3997- not self.controller.view.sign_in_button.enabled())
3998-
3999- def test_invalid_both(self):
4000- """The submit button should be disabled with invalid data."""
4001- self.controller.view.email_edit.setText("ab")
4002- self.controller.view.password_edit.setText("")
4003- self.controller._validate()
4004- self.assertFalse(self.controller.view.sign_in_button.enabled())
4005- self.assertTrue(self.controller.view.properties.get('unpolish', False))
4006- self.assertTrue(self.controller.view.properties.get('polish', False))
4007- self.assertTrue("DisabledState" in self.controller.view.properties)
4008- self.assertEqual(self.controller.view.properties["DisabledState"],
4009- not self.controller.view.sign_in_button.enabled())
4010-
4011-
4012-class SetUpAccountControllerTestCase(BaseTestCase):
4013- """test the controller used to setup a new account."""
4014-
4015- @defer.inlineCallbacks
4016- def setUp(self):
4017- """Set the different tests."""
4018- yield super(SetUpAccountControllerTestCase, self).setUp()
4019- self.view = FakeSetupAccountPageView()
4020- self.message_box = FakeMessageBox()
4021- self.controller = SetUpAccountController(message_box=self.message_box)
4022- self.patch(self.controller, "get_backend", self.view.fake_backend)
4023- self.controller.view = self.view
4024- self.controller.backend = self.view
4025-
4026- def test_set_translated_strings(self):
4027- """Ensure all the strings are set."""
4028- self.controller._set_translated_strings()
4029- self.assertEqual(self.view.name_label.text(), NAME_ENTRY)
4030- self.assertEqual(self.view.email_label.text(), EMAIL1_ENTRY)
4031- self.assertEqual(self.view.confirm_email_label.text(), EMAIL2_ENTRY)
4032- self.assertEqual(self.view.password_label.text(), PASSWORD1_ENTRY)
4033- self.assertEqual(self.view.confirm_password_label.text(),
4034- PASSWORD2_ENTRY)
4035- self.assertEqual(self.view.password_info_label.text(), PASSWORD_HELP)
4036- self.assertEqual(self.view.captcha_solution_edit.placeholderText(),
4037- CAPTCHA_SOLUTION_ENTRY)
4038-
4039- def test_set_titles(self):
4040- """Test how the different titles are set."""
4041- self.controller._set_titles()
4042- self.assertEqual(self.view.properties['title'],
4043- JOIN_HEADER_LABEL % {'app_name': self.view.fake_wizard.app_name})
4044- self.assertEqual(self.view.properties['subtitle'],
4045- self.view.fake_wizard.help_text)
4046-
4047- def test_connect_ui_elements(self):
4048- """Test that the ui elements are correctly connect."""
4049- self.controller._connect_ui_elements()
4050- self.assertEqual(self.view.name_edit.receivers(
4051- SIGNAL('textEdited(QString)')), 1)
4052- self.assertEqual(self.view.email_edit.receivers(
4053- SIGNAL('textEdited(QString)')), 1)
4054- self.assertEqual(self.view.confirm_email_edit.receivers(
4055- SIGNAL('textEdited(QString)')), 1)
4056- self.assertEqual(self.view.password_edit.receivers(
4057- SIGNAL('textEdited(QString)')), 1)
4058- self.assertEqual(self.view.confirm_password_edit.receivers(
4059- SIGNAL('textEdited(QString)')), 1)
4060- self.assertEqual(self.view.captcha_solution_edit.receivers(
4061- SIGNAL('textEdited(QString)')), 1)
4062- self.assertEqual(self.view.refresh_label.receivers(
4063- SIGNAL('linkActivated(QString)')), 1)
4064- self.assertEqual(self.view.terms_checkbox.receivers(
4065- SIGNAL('stateChanged(int)')), 1)
4066- # set the callbacks for the captcha generation
4067- self.assertIsInstance(self.view.on_captcha_generated_cb,
4068- collections.Callable)
4069- self.assertIsInstance(self.view.on_captcha_generation_error_cb,
4070- collections.Callable)
4071- self.assertIsInstance(self.view.on_user_registration_error_cb,
4072- collections.Callable)
4073- self.assertIsInstance(self.view.on_user_registered_cb,
4074- collections.Callable)
4075-
4076- def test_set_line_edits_validations(self):
4077- """Test _set_line_validations from controller."""
4078- self.controller._set_line_edits_validations()
4079- elements = self.view.properties['validation_rule']
4080- self.assertFalse(elements is None)
4081- for e in elements:
4082- self.assertTrue(type(e[0]) is QLineEdit)
4083- self.assertIsInstance(e[1], collections.Callable)
4084-
4085- def all_valid(self):
4086- """Set all the widgets to a valid state."""
4087- self.view.name_edit.setText('Name')
4088- self.view.email_edit.setText('email@email.com')
4089- self.view.confirm_email_edit.setText('email@email.com')
4090- self.view.password_edit.setText('T3st3rqw')
4091- self.view.confirm_password_edit.setText('T3st3rqw')
4092- self.view.captcha_solution_edit.setText('captcha solution')
4093- self.view.terms_checkbox.setChecked(True)
4094-
4095- def all_invalid(self):
4096- """Set all the widgets to an invalid state."""
4097- self.view.name_edit.setText('')
4098- self.view.email_edit.setText('')
4099- self.view.confirm_email_edit.setText('email')
4100- self.view.password_edit.setText('')
4101- self.view.confirm_password_edit.setText('t')
4102- self.view.captcha_solution_edit.setText('')
4103- self.view.terms_checkbox.setChecked(False)
4104-
4105- def test_enable_setup_button_only_checkbox_ok(self):
4106- """Test enable button only name valid."""
4107- self.all_invalid()
4108- self.view.terms_checkbox.setChecked(True)
4109- self.controller._enable_setup_button()
4110-
4111- self.assertFalse(self.view.set_up_button.enabled())
4112- self.assertTrue(self.view.set_up_button.property('DisabledState'))
4113- self.assertTrue(self.view.set_up_button.properties['polish'])
4114- self.assertTrue(self.view.set_up_button.properties['unpolish'])
4115-
4116- def test_enable_setup_button_only_name_ok(self):
4117- """Test enable button only name valid."""
4118- self.all_invalid()
4119- self.view.name_edit.setText('Name')
4120- self.controller._enable_setup_button()
4121-
4122- self.assertFalse(self.view.set_up_button.enabled())
4123- self.assertTrue(self.view.set_up_button.property('DisabledState'))
4124- self.assertTrue(self.view.set_up_button.properties['polish'])
4125- self.assertTrue(self.view.set_up_button.properties['unpolish'])
4126-
4127- def test_enable_setup_button_only_email_ok(self):
4128- """Test enable button only name valid."""
4129- self.all_invalid()
4130- self.view.email_edit.setText('email@email.com')
4131- self.controller._enable_setup_button()
4132-
4133- self.assertFalse(self.view.set_up_button.enabled())
4134- self.assertTrue(self.view.set_up_button.property('DisabledState'))
4135- self.assertTrue(self.view.set_up_button.properties['polish'])
4136- self.assertTrue(self.view.set_up_button.properties['unpolish'])
4137-
4138- def test_enable_setup_button_only_confirm_email_ok(self):
4139- """Test enable button only name valid."""
4140- self.all_invalid()
4141- self.view.confirm_email_edit.setText('email@email.com')
4142- self.controller._enable_setup_button()
4143-
4144- self.assertFalse(self.view.set_up_button.enabled())
4145- self.assertTrue(self.view.set_up_button.property('DisabledState'))
4146- self.assertTrue(self.view.set_up_button.properties['polish'])
4147- self.assertTrue(self.view.set_up_button.properties['unpolish'])
4148-
4149- def test_enable_setup_button_only_email_and_confirm_ok(self):
4150- """Test enable button only name valid."""
4151- self.all_invalid()
4152- self.view.email_edit.setText('email@email.com')
4153- self.view.confirm_email_edit.setText('email@email.com')
4154- self.controller._enable_setup_button()
4155-
4156- self.assertFalse(self.view.set_up_button.enabled())
4157- self.assertTrue(self.view.set_up_button.property('DisabledState'))
4158- self.assertTrue(self.view.set_up_button.properties['polish'])
4159- self.assertTrue(self.view.set_up_button.properties['unpolish'])
4160-
4161- def test_enable_setup_button_only_password_ok(self):
4162- """Test enable button only name valid."""
4163- self.all_invalid()
4164- self.view.password_edit.setText('T3st3rqw')
4165- self.controller._enable_setup_button()
4166-
4167- self.assertFalse(self.view.set_up_button.enabled())
4168- self.assertTrue(self.view.set_up_button.property('DisabledState'))
4169- self.assertTrue(self.view.set_up_button.properties['polish'])
4170- self.assertTrue(self.view.set_up_button.properties['unpolish'])
4171-
4172- def test_enable_setup_button_only_password_confirm_ok(self):
4173- """Test enable button only name valid."""
4174- self.all_invalid()
4175- self.view.confirm_password_edit.setText('T3st3rqw')
4176- self.controller._enable_setup_button()
4177-
4178- self.assertFalse(self.view.set_up_button.enabled())
4179- self.assertTrue(self.view.set_up_button.property('DisabledState'))
4180- self.assertTrue(self.view.set_up_button.properties['polish'])
4181- self.assertTrue(self.view.set_up_button.properties['unpolish'])
4182-
4183- def test_enable_setup_button_only_password_and_confirm_ok(self):
4184- """Test enable button only name valid."""
4185- self.all_invalid()
4186- self.view.password_edit.setText('T3st3rqw')
4187- self.view.confirm_password_edit.setText('T3st3rqw')
4188- self.controller._enable_setup_button()
4189-
4190- self.assertFalse(self.view.set_up_button.enabled())
4191- self.assertTrue(self.view.set_up_button.property('DisabledState'))
4192- self.assertTrue(self.view.set_up_button.properties['polish'])
4193- self.assertTrue(self.view.set_up_button.properties['unpolish'])
4194-
4195- def test_enable_setup_button_only_captcha_ok(self):
4196- """Test enable button only name valid."""
4197- self.all_invalid()
4198- self.view.captcha_solution_edit.setText('captcha solution')
4199- self.controller._enable_setup_button()
4200-
4201- self.assertFalse(self.view.set_up_button.enabled())
4202- self.assertTrue(self.view.set_up_button.property('DisabledState'))
4203- self.assertTrue(self.view.set_up_button.properties['polish'])
4204- self.assertTrue(self.view.set_up_button.properties['unpolish'])
4205-
4206- def test_enable_setup_button_only_all_ok(self):
4207- """Test enable button only name valid."""
4208- self.all_valid()
4209- self.controller._enable_setup_button()
4210-
4211- self.assertTrue(self.view.set_up_button.enabled())
4212- self.assertFalse(self.view.set_up_button.property('DisabledState'))
4213- self.assertTrue(self.view.set_up_button.properties['polish'])
4214- self.assertTrue(self.view.set_up_button.properties['unpolish'])
4215-
4216- def test_enable_setup_button_all_wrong(self):
4217- """Test enable button only name valid."""
4218- self.all_invalid()
4219- self.controller._enable_setup_button()
4220-
4221- self.assertFalse(self.view.set_up_button.enabled())
4222- self.assertTrue(self.view.set_up_button.property('DisabledState'))
4223- self.assertTrue(self.view.set_up_button.properties['polish'])
4224- self.assertTrue(self.view.set_up_button.properties['unpolish'])
4225-
4226- def test_enable_setup_button_name_wrong(self):
4227- """Test enable button only name valid."""
4228- self.all_valid()
4229- self.view.name_edit.setText('')
4230- self.controller._enable_setup_button()
4231-
4232- self.assertFalse(self.view.set_up_button.enabled())
4233- self.assertTrue(self.view.set_up_button.property('DisabledState'))
4234- self.assertTrue(self.view.set_up_button.properties['polish'])
4235- self.assertTrue(self.view.set_up_button.properties['unpolish'])
4236-
4237- def test_enable_setup_button_email_wrong(self):
4238- """Test enable button only name valid."""
4239- self.all_valid()
4240- self.view.email_edit.setText('')
4241- self.controller._enable_setup_button()
4242-
4243- self.assertFalse(self.view.set_up_button.enabled())
4244- self.assertTrue(self.view.set_up_button.property('DisabledState'))
4245- self.assertTrue(self.view.set_up_button.properties['polish'])
4246- self.assertTrue(self.view.set_up_button.properties['unpolish'])
4247-
4248- def test_enable_setup_button_confirm_email_wrong(self):
4249- """Test enable button only name valid."""
4250- self.all_valid()
4251- self.view.confirm_email_edit.setText('')
4252- self.controller._enable_setup_button()
4253-
4254- self.assertFalse(self.view.set_up_button.enabled())
4255- self.assertTrue(self.view.set_up_button.property('DisabledState'))
4256- self.assertTrue(self.view.set_up_button.properties['polish'])
4257- self.assertTrue(self.view.set_up_button.properties['unpolish'])
4258-
4259- def test_enable_setup_button_password_wrong(self):
4260- """Test enable button only name valid."""
4261- self.all_valid()
4262- self.view.password_edit.setText('')
4263- self.controller._enable_setup_button()
4264-
4265- self.assertFalse(self.view.set_up_button.enabled())
4266- self.assertTrue(self.view.set_up_button.property('DisabledState'))
4267- self.assertTrue(self.view.set_up_button.properties['polish'])
4268- self.assertTrue(self.view.set_up_button.properties['unpolish'])
4269-
4270- def test_enable_setup_button_confirm_password_wrong(self):
4271- """Test enable button only name valid."""
4272- self.all_valid()
4273- self.view.confirm_password_edit.setText('')
4274- self.controller._enable_setup_button()
4275-
4276- self.assertFalse(self.view.set_up_button.enabled())
4277- self.assertTrue(self.view.set_up_button.property('DisabledState'))
4278- self.assertTrue(self.view.set_up_button.properties['polish'])
4279- self.assertTrue(self.view.set_up_button.properties['unpolish'])
4280-
4281- def test_enable_setup_button_captcha_wrong(self):
4282- """Test enable button only name valid."""
4283- self.all_valid()
4284- self.view.captcha_solution_edit.setText('')
4285- self.controller._enable_setup_button()
4286-
4287- self.assertFalse(self.view.set_up_button.enabled())
4288- self.assertTrue(self.view.set_up_button.property('DisabledState'))
4289- self.assertTrue(self.view.set_up_button.properties['polish'])
4290- self.assertTrue(self.view.set_up_button.properties['unpolish'])
4291-
4292- def test_enable_setup_button_checkbox_wrong(self):
4293- """Test enable button only name valid."""
4294- self.all_valid()
4295- self.view.terms_checkbox.setChecked(False)
4296- self.controller._enable_setup_button()
4297-
4298- self.assertFalse(self.view.set_up_button.enabled())
4299- self.assertTrue(self.view.set_up_button.property('DisabledState'))
4300- self.assertTrue(self.view.set_up_button.properties['polish'])
4301- self.assertTrue(self.view.set_up_button.properties['unpolish'])
4302-
4303- def test_register_fields(self):
4304- """Test _register_fields."""
4305- self.controller._register_fields()
4306- self.assertTrue('email_address' in self.view.properties)
4307- self.assertTrue('password' in self.view.properties)
4308- self.assertTrue(type(self.view.properties['email_address']) is \
4309- QLineEdit)
4310- self.assertTrue(type(self.view.properties['password']) is \
4311- QLineEdit)
4312-
4313- def test_validate_form_all_ok(self):
4314- """Test the result of validate_form."""
4315- self.all_valid()
4316- self.assertTrue(self.controller.validate_form())
4317- self.assertNotEqual(self.view.name_assistance.text(), NAME_INVALID)
4318- self.assertNotEqual(self.view.email_assistance.text(), EMAIL_INVALID)
4319- self.assertNotEqual(self.view.confirm_email_assistance.text(),
4320- EMAIL_MISMATCH)
4321- messages = '\n'.join([PASSWORD_TOO_WEAK, PASSWORD_MISMATCH,
4322- CAPTCHA_REQUIRED_ERROR])
4323- self.assertNotEqual(self.message_box.critical_args,
4324- ((messages, self.view), {}))
4325-
4326- def test_validate_form_all_wrong(self):
4327- """Test the result of validate_form."""
4328- self.all_invalid()
4329- self.assertFalse(self.controller.validate_form())
4330- self.assertEqual(self.view.name_assistance.text(), NAME_INVALID)
4331- self.assertEqual(self.view.email_assistance.text(), EMAIL_INVALID)
4332- self.assertEqual(self.view.confirm_email_assistance.text(),
4333- EMAIL_MISMATCH)
4334- messages = '\n'.join([PASSWORD_TOO_WEAK, PASSWORD_MISMATCH,
4335- CAPTCHA_REQUIRED_ERROR])
4336- self.assertEqual(self.message_box.critical_args,
4337- ((messages, self.view), {}))
4338-
4339- def test_validate_form_name_ok(self):
4340- """Test the result of validate_form."""
4341- self.all_invalid()
4342- self.view.name_edit.setText('Name')
4343- self.assertFalse(self.controller.validate_form())
4344- self.assertEqual(self.view.email_assistance.text(), EMAIL_INVALID)
4345- self.assertEqual(self.view.confirm_email_assistance.text(),
4346- EMAIL_MISMATCH)
4347- messages = '\n'.join([PASSWORD_TOO_WEAK, PASSWORD_MISMATCH,
4348- CAPTCHA_REQUIRED_ERROR])
4349- self.assertEqual(self.message_box.critical_args,
4350- ((messages, self.view), {}))
4351-
4352- def test_validate_form_email_ok(self):
4353- """Test the result of validate_form."""
4354- self.all_invalid()
4355- self.view.email_edit.setText('email@email.com')
4356- self.assertFalse(self.controller.validate_form())
4357- self.assertEqual(self.view.name_assistance.text(), NAME_INVALID)
4358- self.assertEqual(self.view.confirm_email_assistance.text(),
4359- EMAIL_MISMATCH)
4360- messages = '\n'.join([PASSWORD_TOO_WEAK, PASSWORD_MISMATCH,
4361- CAPTCHA_REQUIRED_ERROR])
4362- self.assertEqual(self.message_box.critical_args,
4363- ((messages, self.view), {}))
4364-
4365- def test_validate_form_confirm_email_ok(self):
4366- """Test the result of validate_form."""
4367- self.all_invalid()
4368- self.view.email_edit.setText('email@email.com')
4369- self.view.confirm_email_edit.setText('email@email.com')
4370- self.assertFalse(self.controller.validate_form())
4371- self.assertEqual(self.view.name_assistance.text(), NAME_INVALID)
4372- messages = '\n'.join([PASSWORD_TOO_WEAK, PASSWORD_MISMATCH,
4373- CAPTCHA_REQUIRED_ERROR])
4374- self.assertEqual(self.message_box.critical_args,
4375- ((messages, self.view), {}))
4376-
4377- def test_validate_form_password_weak(self):
4378- """Test the result of validate_form."""
4379- self.all_valid()
4380- self.view.password_edit.setText('Test')
4381- self.view.confirm_password_edit.setText('Test')
4382- self.assertFalse(self.controller.validate_form())
4383- messages = '\n'.join([PASSWORD_TOO_WEAK])
4384- self.assertEqual(self.message_box.critical_args,
4385- ((messages, self.view), {}))
4386-
4387- def test_validate_form_password_mismatch(self):
4388- """Test the result of validate_form."""
4389- self.all_valid()
4390- self.view.password_edit.setText('T3st3rqw')
4391- self.view.confirm_password_edit.setText('test')
4392- self.assertFalse(self.controller.validate_form())
4393- messages = '\n'.join([PASSWORD_MISMATCH])
4394- self.assertEqual(self.message_box.critical_args,
4395- ((messages, self.view), {}))
4396-
4397- def test_validate_form_captcha_required(self):
4398- """Test the result of validate_form."""
4399- self.all_valid()
4400- self.view.captcha_solution_edit.setText('')
4401- self.assertFalse(self.controller.validate_form())
4402- messages = '\n'.join([CAPTCHA_REQUIRED_ERROR])
4403- self.assertEqual(self.message_box.critical_args,
4404- ((messages, self.view), {}))
4405-
4406- def test_is_correct_email(self):
4407- """Test if the email is correct."""
4408- self.assertTrue(self.controller.is_correct_email('email@'))
4409- self.assertFalse(self.controller.is_correct_email('email'))
4410-
4411- def test_is_correct_email_confirmation(self):
4412- """Test if the email confirmation is correct."""
4413- self.view.email_edit.setText('email@email.com')
4414- self.controller.is_correct_email_confirmation('email.@email.com')
4415-
4416- def test_is_correct_password_confirmation(self):
4417- """Test is_correct_password_confirmation method."""
4418- self.view.password_edit.setText('T3st3rqw')
4419- self.controller.is_correct_password_confirmation('T3st3rqw')
4420-
4421-
4422-class SetupAccountControllerCaptchaTest(BaseTestCase):
4423- """Tests for SetupAccountController, but without Mocker."""
4424-
4425- @defer.inlineCallbacks
4426- def setUp(self):
4427- """Set the different tests."""
4428- yield super(SetupAccountControllerCaptchaTest, self).setUp()
4429- self.message_box = FakeMessageBox()
4430- self.controller = SetUpAccountController(message_box=self.message_box)
4431- self.patch(self.controller, 'view', FakeSetupAccountView())
4432- self.fake_backend = FakeControllerForCaptcha()
4433- self.patch(self.controller, 'backend', self.fake_backend)
4434-
4435- def test_refresh_captcha(self):
4436- """Test the Refresh Captcha function."""
4437- self.assertFalse(self.controller.view.captcha_refreshing_value)
4438- self.controller._refresh_captcha()
4439- self.assertTrue(self.controller.view.captcha_refreshing_value)
4440- self.assertTrue(self.fake_backend.callback_error)
4441-
4442-
4443-class SetupAccountControllerValidationTest(BaseTestCase):
4444- """Tests for SetupAccountController, but without Mocker."""
4445-
4446- @defer.inlineCallbacks
4447- def setUp(self):
4448- """Set the different tests."""
4449- yield super(SetupAccountControllerValidationTest, self).setUp()
4450- self.message_box = FakeMessageBox()
4451- self.controller = SetUpAccountController(message_box=self.message_box)
4452- self.patch(self.controller, '_refresh_captcha', self._set_called)
4453- self.patch(self.controller, 'view', FakeSetupAccountView())
4454-
4455- def test_on_user_registration_refresh_captcha(self):
4456- """If there is a user reg. error, captcha should refresh."""
4457- self.controller.on_user_registration_error('TestApp', {})
4458- self.assertEqual(self._called, ((), {}))
4459-
4460- def test_on_user_registration_all_only(self):
4461- """Pass only a __all__ error key."""
4462- self.controller.on_user_registration_error('TestApp',
4463- {'__all__': "Error in All"})
4464- self.assertEqual(self.message_box.critical_args, ((
4465- "Error in All", self.controller.view), {}))
4466-
4467- def test_on_user_registration_all_fields(self):
4468- """Pass all known error keys, plus unknown one."""
4469- self.controller.on_user_registration_error('TestApp',
4470- {'__all__': "Error in All",
4471- 'email': "Error in email",
4472- 'pasword': "Error in password",
4473- 'unknownfield': "Error in unknown",
4474- })
4475- self.assertEqual(self.message_box.critical_args, ((
4476- "Error in All", self.controller.view), {}))
4477-
4478- def test_registration_errors_without_message_or_all(self):
4479- """Pass only a email error key."""
4480- errdict = {'errtype': "RegistrationError",
4481- 'email': "Error in email"}
4482- self.controller.on_user_registration_error('TestApp', errdict)
4483-
4484- expected = (('', self.controller.view), {})
4485- self.assertEqual(self.message_box.critical_args, expected)
4486-
4487- def test_on_captcha_generated(self):
4488- """Test if the method that shows the overlay is executed."""
4489- self.patch(Image, "open", self.controller.view.fake_open)
4490- self.assertFalse(self.controller.view.captcha_refresh_executed)
4491- self.controller.on_captcha_generated('app_name', 'captcha_id')
4492- self.assertTrue(self.controller.view.captcha_refresh_executed)
4493-
4494- def test_on_captcha_generation_error(self):
4495- """Test if the method that hides the overlay is executed."""
4496- self.assertFalse(self.controller.view.captcha_refresh_executed)
4497- self.controller.on_captcha_generation_error({})
4498- self.assertTrue(self.controller.view.captcha_refresh_executed)
4499-
4500-
4501-class EmailVerificationControllerTestCase(BaseTestCase):
4502- """Test the controller."""
4503-
4504- @defer.inlineCallbacks
4505- def setUp(self):
4506- """Set tests."""
4507- yield super(EmailVerificationControllerTestCase, self).setUp()
4508- self.view = EmailVerificationView()
4509- self.backend = self.view
4510- self.controller = EmailVerificationController(
4511- message_box=FakeMessageBox())
4512- self.controller.view = self.view
4513- self.controller.backend = self.backend
4514- self.patch(self.controller, "get_backend", self.view.fake_backend)
4515- self.email = 'email@email.com'
4516- self.password = 'T3st3rqw'
4517- self.view.fake_wizard.registerField('email_address', self.email)
4518- self.view.fake_wizard.registerField('password', self.password)
4519-
4520- def test_connect_ui_elements(self):
4521- """Set the ui connections."""
4522- self.controller._connect_ui_elements()
4523- self.assertEqual(self.view.verification_code_edit.receivers(
4524- SIGNAL('textChanged(QString)')), 1)
4525- self.assertIsInstance(self.view.next_button.function,
4526- collections.Callable)
4527- self.assertIsInstance(self.view.on_email_validated_cb,
4528- collections.Callable)
4529- self.assertIsInstance(self.view.on_email_validation_error_cb,
4530- collections.Callable)
4531-
4532- def test_set_titles(self):
4533- """Test that the titles are set."""
4534- self.controller.set_titles()
4535- self.assertEqual(self.view.properties['title'], VERIFY_EMAIL_TITLE)
4536- self.assertEqual(self.view.properties['subtitle'],
4537- VERIFY_EMAIL_CONTENT % {
4538- "app_name": self.view.fake_wizard.app_name,
4539- "email": self.email,
4540- })
4541-
4542- def test_validate_email(self):
4543- """Test the callback."""
4544- code = 'qwe123'
4545- self.view.verification_code_edit.setText(code)
4546- self.controller.validate_email()
4547- self.assertEqual(self.view.properties['validate_email'],
4548- ((self.view.fake_wizard.app_name, self.email, self.password,
4549- code), {}))
4550-
4551- def test_validate_form(self):
4552- """Test validate_form."""
4553- self.view.verification_code = 'qwe123'
4554- self.controller.validate_form()
4555- self.assertTrue(self.view.next_button.enabled())
4556- self.assertFalse(self.view.next_button.property('DisabledState'))
4557- self.assertTrue(self.view.next_button.properties['polish'])
4558- self.assertTrue(self.view.next_button.properties['unpolish'])
4559-
4560- self.view.verification_code = ''
4561- self.controller.validate_form()
4562- self.assertFalse(self.view.next_button.enabled())
4563- self.assertTrue(self.view.next_button.property('DisabledState'))
4564- self.assertTrue(self.view.next_button.properties['polish'])
4565- self.assertTrue(self.view.next_button.properties['unpolish'])
4566-
4567- def test_page_initialized(self):
4568- """test pageInitialized to check the initial state of the page."""
4569- self.controller.pageInitialized()
4570- self.assertTrue(self.view.next_button.properties['default'])
4571- self.assertFalse(self.view.next_button.enabled())
4572- self.assertTrue(self.view.next_button.property('DisabledState'))
4573- self.assertTrue(self.view.next_button.properties['polish'])
4574- self.assertTrue(self.view.next_button.properties['unpolish'])
4575-
4576- def test_on_email_validation_error(self):
4577- """Test on_email_validation_error."""
4578- error = dict(error='email error')
4579- self.controller.on_email_validation_error('app', error)
4580- result = '\n'.join(
4581- [('%s: %s' % (k, v)) for k, v in error.iteritems()])
4582- self.assertEqual(self.controller.message_box.critical_args,
4583- ((result, self.view), {}))
4584-
4585- def test_on_email_validated(self):
4586- """Test on_email_validated."""
4587- self.controller.on_email_validated('app_name')
4588- self.assertEqual(self.view.fake_wizard.properties['emit'],
4589- ('app_name', self.email))
4590-
4591-
4592-class EmailVerificationControllerValidationTestCase(BaseTestCase):
4593- """Tests for EmailVerificationController, but without Mocker."""
4594-
4595- @defer.inlineCallbacks
4596- def setUp(self):
4597- """Set the different tests."""
4598- yield super(EmailVerificationControllerValidationTestCase,
4599- self).setUp()
4600- self.message_box = FakeMessageBox()
4601- self.controller = EmailVerificationController(
4602- message_box=self.message_box)
4603- self.patch(self.controller, 'view', FakeEmailVerificationView())
4604-
4605- def test_on_email_validation_error(self):
4606- """Test that on_email_validation_error callback works as expected."""
4607- # Error type is removed from the final message
4608- error = dict(errtype='BadTokenError')
4609- app_name = 'app_name'
4610- self.controller.on_email_validation_error(app_name, error)
4611- self.assertEqual(self.message_box.critical_args,
4612- (('', self.controller.view), {}))
4613-
4614- def test_validate_form_wrong(self):
4615- """Check the state of the next button."""
4616- self.controller.view.verification_code = ''
4617- self.controller.validate_form()
4618- self.assertFalse(self.controller.view.next_button.isEnabled())
4619- self.assertTrue(self.controller.view.properties.get('unpolish', False))
4620- self.assertTrue(self.controller.view.properties.get('polish', False))
4621- self.assertTrue("DisabledState" in self.controller.view.properties)
4622- self.assertEqual(self.controller.view.properties["DisabledState"],
4623- not self.controller.view.next_button.enabled())
4624-
4625- def test_validate_form_ok(self):
4626- """Check the state of the next button."""
4627- self.controller.view.verification_code = 'as322fdw'
4628- self.controller.validate_form()
4629- self.assertTrue(self.controller.view.next_button.isEnabled)
4630- self.assertTrue(self.controller.view.properties.get('unpolish', False))
4631- self.assertTrue(self.controller.view.properties.get('polish', False))
4632- self.assertTrue("DisabledState" in self.controller.view.properties)
4633- self.assertEqual(self.controller.view.properties["DisabledState"],
4634- not self.controller.view.next_button.enabled())
4635-
4636-
4637-class ErrorControllerTestCase(BaseTestCase):
4638- """Test the success page controller."""
4639-
4640- @defer.inlineCallbacks
4641- def setUp(self):
4642- yield super(ErrorControllerTestCase, self).setUp()
4643- self.view = ErrorPageView()
4644- self.backend = self.view
4645- self.controller = ErrorController()
4646- self.controller.view = self.view
4647- self.controller.backend = self.backend
4648-
4649- def test_set_ui(self):
4650- """Test the process that sets the ui."""
4651- self.controller._title = ERROR
4652- self.controller._subtitle = ERROR
4653- self.controller.setupUi(self.view)
4654- self.assertEqual(self.view.error_message_label.text(), ERROR)
4655- self.assertEqual(self.view.properties['title'], ERROR)
4656- self.assertEqual(self.view.properties['subtitle'], ERROR)
4657- self.assertEqual(self.view.next, -1)
4658-
4659-
4660-class SuccessControllerTestCase(BaseTestCase):
4661- """Test the success page controller."""
4662-
4663- @defer.inlineCallbacks
4664- def setUp(self):
4665- yield super(SuccessControllerTestCase, self).setUp()
4666- self.view = SuccessPageView()
4667- self.backend = self.view
4668- self.controller = SuccessController()
4669- self.controller.view = self.view
4670- self.controller.backend = self.backend
4671-
4672- def test_set_ui(self):
4673- """Test the process that sets the ui."""
4674- self.controller._title = SUCCESS
4675- self.controller._subtitle = SUCCESS
4676- self.controller.setupUi(self.view)
4677- self.assertEqual(self.view.properties['title'], SUCCESS)
4678- self.assertEqual(self.view.properties['subtitle'], SUCCESS)
4679- self.assertEqual(self.view.next, -1)
4680-
4681-
4682-class UbuntuSSOWizardControllerTestCase(BaseTestCase):
4683- """Test the wizard controller."""
4684-
4685- @defer.inlineCallbacks
4686- def setUp(self):
4687- """Set tests."""
4688- yield super(UbuntuSSOWizardControllerTestCase, self).setUp()
4689- self.view = UbuntuSSOView()
4690- self.backend = self.view
4691- self.callback = self.view.callback
4692- self.controller = UbuntuSSOWizardController()
4693- self.controller.view = self.view
4694- self.controller.backend = self.backend
4695-
4696- def test_on_user_cancelation(self):
4697- """Test that the callback is indeed called."""
4698- self.controller.user_cancellation_callback = self.callback
4699- self.controller.on_user_cancelation()
4700- self.assertTrue(self.view.properties.get('close', False))
4701- self.assertEqual(self.view.properties['callback'],
4702- ((self.view.app_name, ), {}))
4703-
4704- def test_on_login_success(self):
4705- """Test that the callback is indeed called."""
4706- app_name = 'app'
4707- email = 'email'
4708- self.controller.login_success_callback = self.callback
4709- self.controller.login_success_callback(app_name, email)
4710- self.assertEqual(self.view.properties['callback'],
4711- ((app_name, email), {}))
4712-
4713- def test_on_registration_success(self):
4714- """Test that the callback is indeed called."""
4715- app_name = 'app'
4716- email = 'email'
4717- self.controller.registration_success_callback = self.callback
4718- self.controller.registration_success_callback(app_name, email)
4719- self.assertEqual(self.view.properties['callback'],
4720- ((app_name, email), {}))
4721-
4722- def test_show_success_message(self):
4723- """Test that the correct page will be shown."""
4724- self.controller.show_success_message()
4725- # the buttons layout we expect to have
4726- layout = []
4727- layout.append(QWizard.Stretch)
4728- layout.append(QWizard.FinishButton)
4729-
4730- self.assertEqual(self.view.page.next, self.view.success_page_id)
4731- self.assertTrue(self.view.properties['wizard_next'])
4732- self.assertEqual(self.view.properties['buttons_layout'], layout)
4733-
4734- def test_show_error_message(self):
4735- """Test that the correct page will be shown."""
4736- self.controller.show_error_message()
4737- # the buttons layout we expect to have
4738- layout = []
4739- layout.append(QWizard.Stretch)
4740- layout.append(QWizard.FinishButton)
4741-
4742- self.assertEqual(self.view.page.next, self.view.error_page_id)
4743- self.assertTrue(self.view.properties['wizard_next'])
4744- self.assertEqual(self.view.properties['buttons_layout'], layout)
4745-
4746- def test_setup_ui(self):
4747- """Test that the ui is connect."""
4748- self.controller.setupUi(self.view)
4749- self.assertEqual(self.view.properties['wizard_style'],
4750- QWizard.ModernStyle)
4751- self.assertIsInstance(self.view.properties['button'].function,
4752- collections.Callable)
4753- self.assertIsInstance(self.view.loginSuccess.function,
4754- collections.Callable)
4755- self.assertIsInstance(self.view.registrationSuccess.function,
4756- collections.Callable)
4757-
4758-
4759-class ForgottenPasswordControllerValidationTest(BaseTestCase):
4760-
4761- """Tests for ForgottenPasswordController, but without Mocker."""
4762-
4763- @defer.inlineCallbacks
4764- def setUp(self):
4765- """Set the different tests."""
4766- yield super(ForgottenPasswordControllerValidationTest, self).setUp()
4767- self.message_box = FakeMessageBox()
4768- self.controller = ForgottenPasswordController(
4769- message_box=self.message_box)
4770- self.view = FakeForgottenPasswordPage()
4771- self.controller.view = self.view
4772- self.patch(self.controller, "get_backend", self.view.fake_backend)
4773-
4774- def test_page_initialized(self):
4775- """Test the initial state of the page when it is loaded."""
4776- self.controller.pageInitialized()
4777- self.assertFalse(self.controller.view.send_button.enabled())
4778- self.assertTrue(self.controller.view.send_button.properties['default'])
4779- self.assertTrue("DisabledState" in self.controller.view.properties)
4780- self.assertEqual(self.controller.view.properties["DisabledState"],
4781- not self.controller.view.send_button.enabled())
4782- self.assertTrue(self.controller.view.properties.get('unpolish', False))
4783- self.assertTrue(self.controller.view.properties.get('polish', False))
4784-
4785- def test_page_initialized_with_text(self):
4786- """Test the initial state of the page when it is loaded."""
4787- self.controller.view.email_line_edit.setText('mail@mail.com')
4788- self.controller.pageInitialized()
4789- self.assertTrue(self.controller.view.send_button.enabled())
4790- self.assertTrue(
4791- self.controller.view.send_button.properties['default'])
4792- self.assertTrue("DisabledState" in self.controller.view.properties)
4793- self.assertEqual(self.controller.view.properties["DisabledState"],
4794- not self.controller.view.send_button.enabled())
4795- self.assertTrue(self.controller.view.properties.get('unpolish', False))
4796- self.assertTrue(self.controller.view.properties.get('polish', False))
4797-
4798- def test_valid(self):
4799- """The submit button should be enabled with a valid email."""
4800- self.controller.view.email_address_line_edit.setText("a@b")
4801- self.assertNotEqual(unicode(
4802- self.controller.view.email_address_line_edit.text()), u"")
4803- self.controller._validate()
4804- self.assertTrue(self.controller.view.send_button.enabled())
4805- self.assertTrue("DisabledState" in self.controller.view.properties)
4806- self.assertEqual(self.controller.view.properties["DisabledState"],
4807- not self.controller.view.send_button.enabled())
4808- self.assertTrue(self.controller.view.properties.get('unpolish', False))
4809- self.assertTrue(self.controller.view.properties.get('polish', False))
4810-
4811- def test_invalid(self):
4812- """The submit button should be disabled with an invalid email."""
4813- self.controller.view.email_address_line_edit.setText("ab")
4814- self.assertNotEqual(
4815- unicode(self.controller.view.email_address_line_edit.text()), u"")
4816- self.controller._validate()
4817- self.assertFalse(self.controller.view.send_button.enabled())
4818- self.assertTrue("DisabledState" in self.controller.view.properties)
4819- self.assertEqual(self.controller.view.properties["DisabledState"],
4820- not self.controller.view.send_button.enabled())
4821- self.assertTrue(self.controller.view.properties.get('unpolish', False))
4822- self.assertTrue(self.controller.view.properties.get('polish', False))
4823-
4824- def test_empty(self):
4825- """The submit button should be disabled without email."""
4826- self.assertEqual(
4827- unicode(self.controller.view.email_address_line_edit.text()), u"")
4828- self.assertFalse(self.controller.view.send_button.enabled())
4829-
4830- def test_on_password_reset_error_token_error(self):
4831- """Test that the on_password_reset_error callback works as expected."""
4832- error = dict(errtype='ResetPasswordTokenError')
4833- app_name = 'app_name'
4834- self.controller.on_password_reset_error(app_name, error)
4835- msg = REQUEST_PASSWORD_TOKEN_WRONG_EMAIL
4836- self.assertEqual(self.controller.message_box.critical_args,
4837- ((msg, self.controller.view), {}))
4838-
4839- def test_on_password_reset_error_general_error(self):
4840- """Test that the on_password_reset_error callback works as expected."""
4841- error = dict(errtype='RandomError')
4842- app_name = 'app_name'
4843- msg = REQUEST_PASSWORD_TOKEN_TECH_ERROR
4844- self.controller.on_password_reset_error(app_name, error)
4845- self.assertFalse(self.controller.view.email_widget.isVisible())
4846- self.assertFalse(
4847- self.controller.view.forgotted_password_intro_label.isVisible())
4848- self.assertTrue(self.controller.view.try_again_widget.isVisible())
4849- self.assertEqual(self.controller.message_box.critical_args,
4850- ((msg, self.controller.view), {}))
4851-
4852-
4853-class ForgottenPasswordControllerTestCase(BaseTestCase):
4854- """Test the controller of the fogotten password page."""
4855-
4856- @defer.inlineCallbacks
4857- def setUp(self):
4858- """Setup the tests."""
4859- yield super(ForgottenPasswordControllerTestCase, self).setUp()
4860- self.view = FakeForgottenPasswordPageView()
4861- self.backend = self.view
4862- self.controller = ForgottenPasswordController(
4863- message_box=FakeMessageBox())
4864- self.controller.view = self.view
4865- self.controller.backend = self.backend
4866-
4867- def test_register_fields(self):
4868- """Ensure that all the diff fields are registered."""
4869- self.controller._register_fields()
4870- self.assertEqual(self.view.field('email_address'),
4871- self.view.email_address_line_edit)
4872-
4873- def test_set_translated_strings(self):
4874- """Ensure that the correct strings are translated."""
4875- self.controller._set_translated_strings()
4876- self.assertEqual(self.view.forgotted_password_intro_label.text(),
4877- REQUEST_PASSWORD_TOKEN_LABEL % {'app_name':
4878- self.view.fake_wizard.app_name})
4879- self.assertEqual(self.view.email_address_label.text(),
4880- EMAIL_LABEL)
4881- self.assertEqual(self.view.send_button.text(),
4882- RESET_PASSWORD)
4883- self.assertEqual(self.view.try_again_button.text(),
4884- TRY_AGAIN_BUTTON)
4885-
4886- def test_set_enhanced_line_edit(self):
4887- """Test that the correct line enhancements have been added."""
4888- self.controller._set_enhanced_line_edit()
4889- elements = self.view.properties['validation_rule']
4890- self.assertFalse(elements is None)
4891- for e in elements:
4892- self.assertTrue(e[0] is self.view.email_address_line_edit)
4893- self.assertIsInstance(e[1], collections.Callable)
4894-
4895- def test_connect_ui(self):
4896- """Test that the correct ui signals are connected."""
4897- self.controller._connect_ui()
4898- self.assertEqual(self.view.email_address_line_edit.receivers(
4899- SIGNAL('textChanged(QString)')), 1)
4900- self.assertIsInstance(self.view.send_button.function,
4901- collections.Callable)
4902- self.assertIsInstance(self.view.try_again_button.function,
4903- collections.Callable)
4904- self.assertIsInstance(self.view.on_password_reset_token_sent_cb,
4905- collections.Callable)
4906- self.assertIsInstance(self.view.on_password_reset_error_cb,
4907- collections.Callable)
4908-
4909- def test_on_try_again(self):
4910- """Test that the on_try_again callback does work as expected."""
4911- self.controller.on_try_again()
4912- self.assertFalse(self.view.try_again_widget.isVisible())
4913- self.assertTrue(self.view.email_widget.isVisible())
4914-
4915- def test_on_password_reset_token_sent(self):
4916- """Test that the on_password_token_sent callback works as expected."""
4917- self.controller.on_password_reset_token_sent()
4918- self.assertTrue(self.view.properties['wizard_next'])
4919- self.assertEqual(self.view.next,
4920- self.view.fake_wizard.reset_password_page_id)
4921-
4922-
4923-class ResetPasswordControllerTestCase(BaseTestCase):
4924- """Ensure that the reset password works as expected."""
4925-
4926- @defer.inlineCallbacks
4927- def setUp(self):
4928- """Setup the tests."""
4929- yield super(ResetPasswordControllerTestCase, self).setUp()
4930- self.view = ResetPasswordPageView()
4931- self.backend = self.view
4932- self.controller = ResetPasswordController()
4933- self.controller.view = self.view
4934- self.controller.backend = self.backend
4935-
4936- def test_set_translated_strings(self):
4937- """Ensure that the correct strings are set."""
4938- self.controller._set_translated_strings()
4939- self.assertEqual(self.view.reset_password_button.text(),
4940- RESET_PASSWORD)
4941- self.assertEqual(self.view.properties['subtitle'],
4942- PASSWORD_HELP)
4943-
4944- def test_connect_ui(self):
4945- """Ensure that the diffent signals from the ui are connected."""
4946- self.controller._connect_ui()
4947- self.assertIsInstance(self.view.reset_password_button.function,
4948- collections.Callable)
4949- self.assertIsInstance(self.view.on_password_changed_cb,
4950- collections.Callable)
4951- self.assertIsInstance(self.view.on_password_change_error_cb,
4952- collections.Callable)
4953- self.assertEqual(self.view.reset_code_line_edit.receivers(
4954- SIGNAL('textChanged(QString)')), 1)
4955- self.assertEqual(self.view.password_line_edit.receivers(
4956- SIGNAL('textChanged(QString)')), 1)
4957- self.assertEqual(self.view.confirm_password_line_edit.receivers(
4958- SIGNAL('textChanged(QString)')), 1)
4959-
4960- def test_add_line_edits_validations(self):
4961- """Ensure that the line validation have been added."""
4962- self.controller._add_line_edits_validations()
4963- self.assertEqual(self.view.password_line_edit.receivers(
4964- SIGNAL('textChanged(QString)')), 1)
4965- elements = self.view.properties['validation_rule']
4966- self.assertFalse(elements is None)
4967- for e in elements:
4968- self.assertTrue(type(e[0]) is QLineEdit)
4969- self.assertIsInstance(e[1], collections.Callable)
4970-
4971- def test_set_new_password(self):
4972- """Test that the correct action is performed."""
4973- email = 'email@email.com'
4974- code = 'qwe123'
4975- password = 'T3st3rqw'
4976- self.view.wizard().forgotten.ui.email_line_edit.setText(email)
4977- self.view.reset_code_line_edit.setText(code)
4978- self.view.password_line_edit.setText(password)
4979- self.controller.set_new_password()
4980- self.assertEqual(self.view.properties['backend_new_password'],
4981- ((self.view.fake_wizard.app_name, email, code, password), {}))
4982-
4983- def test_is_correct_password_confirmation_true(self):
4984- """Test that the correct password confirmation is used."""
4985- password = 'password'
4986- self.view.password_line_edit.setText(password)
4987- self.assertTrue(self.controller.is_correct_password_confirmation(
4988- password))
4989-
4990- def test_is_correct_password_confirmation_false(self):
4991- """Test that the correct password confirmation is used."""
4992- password = 'password'
4993- self.view.password_line_edit.setText(password + password)
4994- self.assertFalse(self.controller.is_correct_password_confirmation(
4995- password))
4996-
4997-
4998-class ResetPasswordControllerValidationTest(BaseTestCase):
4999-
5000- """Tests for ResetPasswordController, but without Mocker."""
The diff has been truncated for viewing.

Subscribers

People subscribed via source and target branches