Merge lp:~diegosarmentero/ubuntu-sso-client/qt-refactor into lp:ubuntu-sso-client
- qt-refactor
- Merge into trunk
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 | ||||
Related bugs: |
|
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
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.
Looks great!