Merge lp:~diegosarmentero/ubuntu-sso-client/reset-password-page into lp:ubuntu-sso-client

Proposed by Diego Sarmentero
Status: Merged
Approved by: Natalia Bidart
Approved revision: 778
Merged at revision: 772
Proposed branch: lp:~diegosarmentero/ubuntu-sso-client/reset-password-page
Merge into: lp:ubuntu-sso-client
Diff against target: 748 lines (+604/-56)
5 files modified
data/qt/reset_password.ui (+130/-54)
ubuntu_sso/qt/common.py (+94/-0)
ubuntu_sso/qt/gui.py (+47/-2)
ubuntu_sso/qt/tests/test_common.py (+234/-0)
ubuntu_sso/qt/tests/test_reset_password.py (+99/-0)
To merge this branch: bzr merge lp:~diegosarmentero/ubuntu-sso-client/reset-password-page
Reviewer Review Type Date Requested Status
Natalia Bidart (community) Approve
Roberto Alsina (community) Approve
Review via email: mp+73522@code.launchpad.net

Commit message

Reset Password Page Complete.

Description of the change

Reset Password Page Complete.

To post a comment you must log in.
Revision history for this message
Roberto Alsina (ralsina) wrote :

+1 looks good!

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

We need to maintain a strict separation between the modules where, all the QT related stuff end inside the gui/qt python package. So all the new addings to ubuntu_sso/utils/ui.py can't be there. This module only holds constants and methods that are common to any UI implementation.

So, we can't have QT-specific markup such as BAD, GOOD, NORMAL nor any of the password_* or check_* methods. If you need a common place, I would advice gui/qt/__init__.py or a gui/qt/common.py module.
Of course tests should be moved too.

For reference, when running the non-qt suite, I'm having a crash like this:

  LineEditStyleTestCase
    test_check_invalid ... QWidget: Must construct a QApplication before a QPaintDevice
Aborted

because if no -qt switch is given, we use the glib reactor (and we want to keep that suite running with that reactor).

review: Needs Fixing
774. By Diego Sarmentero

Added Qt common functionality to common.py module.

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

* ubuntu_sso/qt/tests/test_reset_password.py:
    82: [W0212, SetupAccountTestCase.test_focus_changed_1] Access to a protected member _called of a client class
    85: [W0212, SetupAccountTestCase.test_focus_changed_1] Access to a protected member _called of a client class
    93: [W0212, SetupAccountTestCase.test_focus_changed_2] Access to a protected member _called of a client class
    96: [W0212, SetupAccountTestCase.test_focus_changed_2] Access to a protected member _called of a client class

* Question: should showEvent and hideEvent call super()?

* Silly fix: ubuntu_sso/qt/tests/test_common.py should have you as author, not mandel :-). Same for ubuntu_sso/qt/tests/test_reset_password.py.

* This is not valid style for our project!

+from ubuntu_sso.qt.common import (check_as_invalid,
+ check_as_valid,
+ password_assistance,
+ password_check_match,
+ BAD,
+ GOOD,
+ NORMAL,
+ PASSWORD_DIGIT,
+ PASSWORD_LENGTH,
+ PASSWORD_MATCH,
+ PASSWORD_UPPER)
+

it should be:

+from ubuntu_sso.qt.common import (check_as_invalid,
+ check_as_valid,
+ password_assistance,
+ password_check_match,
+ BAD,
+ GOOD,
+ NORMAL,
+ PASSWORD_DIGIT,
+ PASSWORD_LENGTH,
+ PASSWORD_MATCH,
+ PASSWORD_UPPER,
+)

* I advice to use twisted TestCase instead of unittest's.

* I think there is no need to cut off some sentences like:

+ password_assistance(line_edit,
+ label_assistance)

can you please check the rest?

* Can we have this moved to a setUp? seems to be used in every test:

+ line_edit = QtGui.QLineEdit()
+ label_assistance = QtGui.QLabel()

* This two should be together by alphabetical order:

+from PyQt4 import QtCore, QtGui
+from twisted.trial.unittest import TestCase

It looks really good! Big applause for all the tests!

review: Needs Fixing
775. By Diego Sarmentero

Fixed to follow some coding guidelines.

776. By Diego Sarmentero

Fixed lint issues

777. By Diego Sarmentero

Added bug number.
Moving button to the bottom right.

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

Looks good!

review: Approve
778. By Diego Sarmentero

Changed import order for PyQt4 modules to avoid some issues.

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1=== modified file 'data/qt/reset_password.ui'
2--- data/qt/reset_password.ui 2011-08-05 14:37:00 +0000
3+++ data/qt/reset_password.ui 2011-09-02 11:41:29 +0000
4@@ -6,7 +6,7 @@
5 <rect>
6 <x>0</x>
7 <y>0</y>
8- <width>476</width>
9+ <width>580</width>
10 <height>262</height>
11 </rect>
12 </property>
13@@ -17,25 +17,37 @@
14 <item>
15 <layout class="QHBoxLayout" name="horizontalLayout">
16 <item>
17- <spacer name="horizontalSpacer_2">
18- <property name="orientation">
19- <enum>Qt::Horizontal</enum>
20- </property>
21- <property name="sizeHint" stdset="0">
22- <size>
23- <width>40</width>
24- <height>20</height>
25- </size>
26- </property>
27- </spacer>
28- </item>
29- <item>
30 <layout class="QVBoxLayout" name="verticalLayout_2">
31 <item>
32- <widget class="QLineEdit" name="reset_code_line_edit"/>
33+ <widget class="QLineEdit" name="reset_code_line_edit">
34+ <property name="minimumSize">
35+ <size>
36+ <width>300</width>
37+ <height>0</height>
38+ </size>
39+ </property>
40+ <property name="maximumSize">
41+ <size>
42+ <width>300</width>
43+ <height>16777215</height>
44+ </size>
45+ </property>
46+ </widget>
47 </item>
48 <item>
49 <widget class="QLineEdit" name="password_line_edit">
50+ <property name="minimumSize">
51+ <size>
52+ <width>300</width>
53+ <height>0</height>
54+ </size>
55+ </property>
56+ <property name="maximumSize">
57+ <size>
58+ <width>300</width>
59+ <height>16777215</height>
60+ </size>
61+ </property>
62 <property name="echoMode">
63 <enum>QLineEdit::Password</enum>
64 </property>
65@@ -43,46 +55,31 @@
66 </item>
67 <item>
68 <widget class="QLineEdit" name="confirm_password_line_edit">
69+ <property name="minimumSize">
70+ <size>
71+ <width>300</width>
72+ <height>0</height>
73+ </size>
74+ </property>
75+ <property name="maximumSize">
76+ <size>
77+ <width>300</width>
78+ <height>16777215</height>
79+ </size>
80+ </property>
81 <property name="echoMode">
82 <enum>QLineEdit::Password</enum>
83 </property>
84 </widget>
85 </item>
86 <item>
87- <layout class="QHBoxLayout" name="horizontalLayout_3">
88- <item>
89- <spacer name="horizontalSpacer_4">
90- <property name="orientation">
91- <enum>Qt::Horizontal</enum>
92- </property>
93- <property name="sizeHint" stdset="0">
94- <size>
95- <width>40</width>
96- <height>20</height>
97- </size>
98- </property>
99- </spacer>
100- </item>
101- <item>
102- <widget class="QPushButton" name="reset_password_button">
103- <property name="enabled">
104- <bool>false</bool>
105- </property>
106- <property name="text">
107- <string/>
108- </property>
109- </widget>
110- </item>
111- </layout>
112- </item>
113- <item>
114 <spacer name="verticalSpacer">
115 <property name="orientation">
116 <enum>Qt::Vertical</enum>
117 </property>
118 <property name="sizeHint" stdset="0">
119 <size>
120- <width>379</width>
121+ <width>300</width>
122 <height>222</height>
123 </size>
124 </property>
125@@ -91,17 +88,96 @@
126 </layout>
127 </item>
128 <item>
129- <spacer name="horizontalSpacer">
130- <property name="orientation">
131- <enum>Qt::Horizontal</enum>
132- </property>
133- <property name="sizeHint" stdset="0">
134- <size>
135- <width>40</width>
136- <height>20</height>
137- </size>
138- </property>
139- </spacer>
140+ <layout class="QVBoxLayout" name="verticalLayout_3">
141+ <item>
142+ <widget class="QLabel" name="password_assistance">
143+ <property name="minimumSize">
144+ <size>
145+ <width>250</width>
146+ <height>100</height>
147+ </size>
148+ </property>
149+ <property name="maximumSize">
150+ <size>
151+ <width>300</width>
152+ <height>16777215</height>
153+ </size>
154+ </property>
155+ <property name="text">
156+ <string>password_assistance</string>
157+ </property>
158+ <property name="indent">
159+ <number>20</number>
160+ </property>
161+ </widget>
162+ </item>
163+ <item>
164+ <spacer name="verticalSpacer_2">
165+ <property name="orientation">
166+ <enum>Qt::Vertical</enum>
167+ </property>
168+ <property name="sizeHint" stdset="0">
169+ <size>
170+ <width>20</width>
171+ <height>40</height>
172+ </size>
173+ </property>
174+ </spacer>
175+ </item>
176+ <item>
177+ <spacer name="horizontalSpacer">
178+ <property name="orientation">
179+ <enum>Qt::Horizontal</enum>
180+ </property>
181+ <property name="sizeType">
182+ <enum>QSizePolicy::Fixed</enum>
183+ </property>
184+ <property name="sizeHint" stdset="0">
185+ <size>
186+ <width>250</width>
187+ <height>0</height>
188+ </size>
189+ </property>
190+ </spacer>
191+ </item>
192+ </layout>
193+ </item>
194+ </layout>
195+ </item>
196+ <item>
197+ <layout class="QHBoxLayout" name="horizontalLayout_2">
198+ <item>
199+ <layout class="QHBoxLayout" name="horizontalLayout_3">
200+ <property name="rightMargin">
201+ <number>0</number>
202+ </property>
203+ <item>
204+ <spacer name="horizontalSpacer_4">
205+ <property name="orientation">
206+ <enum>Qt::Horizontal</enum>
207+ </property>
208+ <property name="sizeType">
209+ <enum>QSizePolicy::Expanding</enum>
210+ </property>
211+ <property name="sizeHint" stdset="0">
212+ <size>
213+ <width>40</width>
214+ <height>20</height>
215+ </size>
216+ </property>
217+ </spacer>
218+ </item>
219+ <item>
220+ <widget class="QPushButton" name="reset_password_button">
221+ <property name="enabled">
222+ <bool>false</bool>
223+ </property>
224+ <property name="text">
225+ <string/>
226+ </property>
227+ </widget>
228+ </item>
229+ </layout>
230 </item>
231 </layout>
232 </item>
233
234=== added file 'ubuntu_sso/qt/common.py'
235--- ubuntu_sso/qt/common.py 1970-01-01 00:00:00 +0000
236+++ ubuntu_sso/qt/common.py 2011-09-02 11:41:29 +0000
237@@ -0,0 +1,94 @@
238+# -*- coding: utf-8 -*-
239+#
240+# Author: Diego Sarmentero <diego.sarmentero@canonical.com>
241+#
242+# Copyright 2011 Canonical Ltd.
243+#
244+# This program is free software: you can redistribute it and/or modify it
245+# under the terms of the GNU General Public License version 3, as published
246+# by the Free Software Foundation.
247+#
248+# This program is distributed in the hope that it will be useful, but
249+# WITHOUT ANY WARRANTY; without even the implied warranties of
250+# MERCHANTABILITY, SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR
251+# PURPOSE. See the GNU General Public License for more details.
252+#
253+# You should have received a copy of the GNU General Public License along
254+# with this program. If not, see <http://www.gnu.org/licenses/>.
255+"""Common functionality used by the UI modules."""
256+
257+import re
258+import gettext
259+
260+gettext.textdomain('ubuntu-sso-client')
261+_ = gettext.gettext
262+
263+# all the text + styles that are used in the gui
264+BAD = u'<img src=":/password_hint_warning.png" /><font> %s </font>'
265+GOOD = u'<img src=":/password_hint_ok.png" /><font> %s </font>'
266+NORMAL = u'<font> %s </font>'
267+PASSWORD_DIGIT = _("At least one number")
268+PASSWORD_LENGTH = _("At least 8 characters")
269+PASSWORD_MATCH = _("Passwords don't match")
270+PASSWORD_MUST_CONTAIN = _("Your password must contain")
271+PASSWORD_UPPER = _("At least one uppercase letter")
272+
273+
274+def password_assistance(line_edit, assistance, icon_type=BAD):
275+ """Show help for the password field."""
276+ text1 = unicode(line_edit.text())
277+ label_text = ["<b>%s</b>" % PASSWORD_MUST_CONTAIN, ]
278+
279+ if len(text1) < 8:
280+ sign = icon_type
281+ else:
282+ sign = GOOD
283+ label_text.append(sign % PASSWORD_LENGTH)
284+
285+ if re.search('[A-Z]', text1) is None:
286+ sign = icon_type
287+ else:
288+ sign = GOOD
289+ label_text.append(sign % PASSWORD_UPPER)
290+
291+ if re.search('[\d+]', text1) is None:
292+ sign = icon_type
293+ else:
294+ sign = GOOD
295+ label_text.append(sign % PASSWORD_DIGIT)
296+
297+ assistance.setText("<br>".join(label_text))
298+
299+
300+def password_check_match(line_edit, line_edit_confirm, assistance):
301+ """Check if passwords match, otherwise show a message."""
302+ password_assistance(line_edit, assistance)
303+ label_text = unicode(assistance.text())
304+ text1 = unicode(line_edit.text())
305+ text2 = unicode(line_edit_confirm.text())
306+ if text1 != text2:
307+ label_text += "<br>" + BAD % PASSWORD_MATCH
308+ assistance.setText(label_text)
309+
310+
311+def password_default_assistance(assistance):
312+ """Show default help for the password field."""
313+ label_text = ["<b>%s</b>" % PASSWORD_MUST_CONTAIN, ]
314+ label_text.append(NORMAL % PASSWORD_LENGTH)
315+ label_text.append(NORMAL % PASSWORD_UPPER)
316+ label_text.append(NORMAL % PASSWORD_DIGIT)
317+ assistance.setText("<br>".join(label_text))
318+
319+
320+def check_as_invalid(line_edit):
321+ """Set QLineEdit's formError property as True, refresh the style."""
322+ line_edit.setProperty("formError", True)
323+ line_edit.style().unpolish(line_edit)
324+ line_edit.style().polish(line_edit)
325+
326+
327+def check_as_valid(line_edit):
328+ """Set QLineEdit's formError property as False, refresh the style."""
329+ line_edit.setProperty("formError", False)
330+ line_edit.style().unpolish(line_edit)
331+ line_edit.style().polish(line_edit)
332
333=== modified file 'ubuntu_sso/qt/gui.py'
334--- ubuntu_sso/qt/gui.py 2011-08-26 18:21:29 +0000
335+++ ubuntu_sso/qt/gui.py 2011-09-02 11:41:29 +0000
336@@ -16,8 +16,7 @@
337 # with this program. If not, see <http://www.gnu.org/licenses/>.
338 """Qt implementation of the UI."""
339
340-from PyQt4.QtCore import pyqtSignal
341-from PyQt4.QtCore import Qt
342+from PyQt4.QtCore import pyqtSignal, Qt, SIGNAL
343 from PyQt4.QtGui import (
344 QApplication,
345 QWidget,
346@@ -31,6 +30,7 @@
347 QLabel)
348
349 from ubuntu_sso.logger import setup_logging
350+from ubuntu_sso.qt import common
351 # pylint: disable=F0401,E0611
352 from ubuntu_sso.qt.choose_sign_in_ui import Ui_ChooseSignInPage
353 from ubuntu_sso.qt.current_user_sign_in_ui import Ui_CurrentUserSignInPage
354@@ -289,6 +289,51 @@
355 def __init__(self, ui, controller, parent=None):
356 """Create a new instance."""
357 SSOWizardEnhancedEditPage.__init__(self, ui, controller, parent)
358+ self.ui.password_line_edit.textEdited.connect(
359+ lambda: common.password_assistance(self.ui.password_line_edit,
360+ self.ui.password_assistance,
361+ common.NORMAL))
362+ self.ui.confirm_password_line_edit.textEdited.connect(
363+ lambda: common.password_check_match(self.ui.password_line_edit,
364+ self.ui.confirm_password_line_edit,
365+ self.ui.password_assistance))
366+
367+ def focus_changed(self, old, now):
368+ """Check who has the focus to activate password popups if necessary."""
369+ if now == self.ui.password_line_edit:
370+ self.ui.password_assistance.setVisible(True)
371+ common.password_default_assistance(self.ui.password_assistance)
372+ elif now == self.ui.confirm_password_line_edit:
373+ common.password_check_match(self.ui.password_line_edit,
374+ self.ui.confirm_password_line_edit,
375+ self.ui.password_assistance)
376+
377+ # Invalid name "initializePage"
378+ # pylint: disable=C0103
379+
380+ def initializePage(self):
381+ super(ResetPasswordPage, self).initializePage()
382+ common.password_default_assistance(self.ui.password_assistance)
383+ self.ui.password_assistance.setVisible(False)
384+
385+ def showEvent(self, event):
386+ """Connect focusChanged signal from the application."""
387+ super(ResetPasswordPage, self).showEvent(event)
388+ self.connect(QApplication.instance(),
389+ SIGNAL("focusChanged(QWidget*, QWidget*)"),
390+ self.focus_changed)
391+
392+ def hideEvent(self, event):
393+ """Disconnect the focusChanged signal when the page change."""
394+ super(ResetPasswordPage, self).hideEvent(event)
395+ try:
396+ self.disconnect(QApplication.instance(),
397+ SIGNAL("focusChanged(QWidget*, QWidget*)"),
398+ self.focus_changed)
399+ except TypeError:
400+ pass
401+
402+ # pylint: enable=C0103
403
404
405 class SetupAccountPage(SSOWizardEnhancedEditPage):
406
407=== added file 'ubuntu_sso/qt/tests/test_common.py'
408--- ubuntu_sso/qt/tests/test_common.py 1970-01-01 00:00:00 +0000
409+++ ubuntu_sso/qt/tests/test_common.py 2011-09-02 11:41:29 +0000
410@@ -0,0 +1,234 @@
411+# -*- coding: utf-8 -*-
412+# Author: Diego Sarmentero <diego.sarmentero@canonical.com>
413+#
414+# Copyright 2011 Canonical Ltd.
415+#
416+# This program is free software: you can redistribute it and/or modify it
417+# under the terms of the GNU General Public License version 3, as published
418+# by the Free Software Foundation.
419+#
420+# This program is distributed in the hope that it will be useful, but
421+# WITHOUT ANY WARRANTY; without even the implied warranties of
422+# MERCHANTABILITY, SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR
423+# PURPOSE. See the GNU General Public License for more details.
424+#
425+# You should have received a copy of the GNU General Public License along
426+# with this program. If not, see <http://www.gnu.org/licenses/>.
427+"""Test the common functions."""
428+
429+from PyQt4 import QtGui
430+from twisted.trial.unittest import TestCase
431+
432+from ubuntu_sso.qt.common import (check_as_invalid,
433+ check_as_valid,
434+ password_assistance,
435+ password_check_match,
436+ BAD,
437+ GOOD,
438+ NORMAL,
439+ PASSWORD_DIGIT,
440+ PASSWORD_LENGTH,
441+ PASSWORD_MATCH,
442+ PASSWORD_UPPER)
443+
444+
445+class PasswordTestCase(TestCase):
446+ """Test passwords conditions."""
447+
448+ def setUp(self):
449+ """Setup tests."""
450+ super(PasswordTestCase, self).setUp()
451+ self.line_edit = QtGui.QLineEdit()
452+ self.line_edit_confirm = QtGui.QLineEdit()
453+ self.label_assistance = QtGui.QLabel()
454+
455+ def test_short_password(self):
456+ """Status with short password.
457+
458+ * Password assistance contains the right message.
459+ """
460+ self.line_edit.setText("foobar")
461+ password_assistance(self.line_edit, self.label_assistance)
462+ self.assertIn(
463+ BAD % PASSWORD_LENGTH,
464+ unicode(self.label_assistance.text()),
465+ )
466+
467+ def test_long_password(self):
468+ """Status with long password.
469+
470+ * Password assistance contains the right message.
471+ """
472+ self.line_edit.setText("foobarbaz")
473+ password_assistance(self.line_edit, self.label_assistance)
474+ self.assertIn(
475+ GOOD % PASSWORD_LENGTH,
476+ unicode(self.label_assistance.text()),
477+ )
478+
479+ def test_no_number_password(self):
480+ """Status with password without a number.
481+
482+ * Password assistance contains the right message.
483+ """
484+ self.line_edit.setText("foobarbaz")
485+ password_assistance(self.line_edit, self.label_assistance)
486+ self.assertIn(
487+ BAD % PASSWORD_DIGIT,
488+ unicode(self.label_assistance.text()),
489+ )
490+
491+ def test_number_password(self):
492+ """Status with password with a number.
493+
494+ * Password assistance contains the right message.
495+ """
496+ self.line_edit.setText("foobarba7")
497+ password_assistance(self.line_edit, self.label_assistance)
498+ self.assertIn(
499+ GOOD % PASSWORD_DIGIT,
500+ unicode(self.label_assistance.text()),
501+ )
502+
503+ def test_no_uppercase_password(self):
504+ """Status with password without uppercase letters.
505+
506+ * Password assistance contains the right message.
507+ """
508+ self.line_edit.setText("foobarbaz")
509+ password_assistance(self.line_edit, self.label_assistance)
510+ self.assertIn(
511+ BAD % PASSWORD_UPPER,
512+ unicode(self.label_assistance.text()),
513+ )
514+
515+ def test_upper_password(self):
516+ """Status with password with uppercase letters.
517+
518+ * Password assistance contains the right message.
519+ """
520+ self.line_edit.setText("Foobarba7")
521+ password_assistance(self.line_edit, self.label_assistance)
522+ self.assertIn(
523+ GOOD % PASSWORD_UPPER,
524+ unicode(self.label_assistance.text()),
525+ )
526+
527+ def test_matching_passwords(self):
528+ """Status when the passwords match.
529+
530+ * Password assistance doesn't contain the message.
531+ """
532+ self.line_edit.setText("Foobarba7")
533+ self.line_edit_confirm.setText("Foobarba7")
534+ password_check_match(self.line_edit,
535+ self.line_edit_confirm, self.label_assistance)
536+ self.assertNotIn(
537+ BAD % PASSWORD_MATCH,
538+ unicode(self.label_assistance.text()))
539+
540+ def test_not_matching_passwords(self):
541+ """Status when the passwords not match.
542+
543+ * Password assistance contains the right message.
544+ """
545+ self.line_edit.setText("Foobarba7")
546+ self.line_edit_confirm.setText("BazBarFo0")
547+ password_check_match(self.line_edit,
548+ self.line_edit_confirm, self.label_assistance)
549+ self.assertIn(
550+ BAD % PASSWORD_MATCH,
551+ unicode(self.label_assistance.text()),
552+ )
553+
554+ def test_password_assistance_in_focus_length_correct(self):
555+ """Check the QLineEdit for password when the length is correct."""
556+ self.line_edit.setText("aaaaaaaaa")
557+ self.line_edit_confirm.setText("")
558+ password_assistance(self.line_edit, self.label_assistance, NORMAL)
559+ self.assertIn(
560+ GOOD % PASSWORD_LENGTH,
561+ unicode(self.label_assistance.text()),
562+ )
563+ self.assertIn(
564+ NORMAL % PASSWORD_UPPER,
565+ unicode(self.label_assistance.text()),
566+ )
567+ self.assertIn(
568+ NORMAL % PASSWORD_DIGIT,
569+ unicode(self.label_assistance.text()),
570+ )
571+
572+ def test_password_assistance_in_focus_with_upper(self):
573+ """Check the QLineEdit for password when it has an upper case char."""
574+ self.line_edit.setText("AAa")
575+ self.line_edit_confirm.setText("")
576+ password_assistance(self.line_edit, self.label_assistance, NORMAL)
577+ self.assertIn(
578+ NORMAL % PASSWORD_LENGTH,
579+ unicode(self.label_assistance.text()),
580+ )
581+ self.assertIn(
582+ GOOD % PASSWORD_UPPER,
583+ unicode(self.label_assistance.text()),
584+ )
585+ self.assertIn(
586+ NORMAL % PASSWORD_DIGIT,
587+ unicode(self.label_assistance.text()),
588+ )
589+
590+ def test_password_assistance_in_focus_with_number(self):
591+ """Check the QLineEdit for password when it contains numbers."""
592+ self.line_edit.setText("a123")
593+ self.line_edit_confirm.setText("")
594+ password_assistance(self.line_edit, self.label_assistance, NORMAL)
595+ self.assertIn(
596+ NORMAL % PASSWORD_LENGTH,
597+ unicode(self.label_assistance.text()),
598+ )
599+ self.assertIn(
600+ NORMAL % PASSWORD_UPPER,
601+ unicode(self.label_assistance.text()),
602+ )
603+ self.assertIn(
604+ GOOD % PASSWORD_DIGIT,
605+ unicode(self.label_assistance.text()),
606+ )
607+
608+ def test_password_assistance_in_focus_all_ok(self):
609+ """Check the QLineEdit for password when all the condition are ok."""
610+ self.line_edit.setText("T3st3rqw")
611+ self.line_edit_confirm.setText("T3st3rqw")
612+ password_assistance(self.line_edit, self.label_assistance, NORMAL)
613+ self.assertIn(
614+ GOOD % PASSWORD_LENGTH,
615+ unicode(self.label_assistance.text()),
616+ )
617+ self.assertIn(
618+ GOOD % PASSWORD_UPPER,
619+ unicode(self.label_assistance.text()),
620+ )
621+ self.assertIn(
622+ GOOD % PASSWORD_DIGIT,
623+ unicode(self.label_assistance.text()),
624+ )
625+ self.assertNotIn(
626+ NORMAL % PASSWORD_MATCH,
627+ unicode(self.label_assistance.text()),
628+ )
629+
630+
631+class LineEditStyleTestCase(TestCase):
632+ """Test QLineEdit styles for errors."""
633+
634+ def test_check_valid(self):
635+ """Check the propery value of a QLineEdit marked as valid."""
636+ line_edit = QtGui.QLineEdit()
637+ check_as_valid(line_edit)
638+ self.assertFalse(line_edit.property("formError").toBool())
639+
640+ def test_check_invalid(self):
641+ """Check the propery value of a QLineEdit marked as invalid."""
642+ line_edit = QtGui.QLineEdit()
643+ check_as_invalid(line_edit)
644+ self.assertTrue(line_edit.property("formError").toBool())
645
646=== added file 'ubuntu_sso/qt/tests/test_reset_password.py'
647--- ubuntu_sso/qt/tests/test_reset_password.py 1970-01-01 00:00:00 +0000
648+++ ubuntu_sso/qt/tests/test_reset_password.py 2011-09-02 11:41:29 +0000
649@@ -0,0 +1,99 @@
650+# -*- coding: utf-8 -*-
651+# Author: Manuel de la Pena <manuel@canonical.com>
652+#
653+# Copyright 2011 Canonical Ltd.
654+#
655+# This program is free software: you can redistribute it and/or modify it
656+# under the terms of the GNU General Public License version 3, as published
657+# by the Free Software Foundation.
658+#
659+# This program is distributed in the hope that it will be useful, but
660+# WITHOUT ANY WARRANTY; without even the implied warranties of
661+# MERCHANTABILITY, SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR
662+# PURPOSE. See the GNU General Public License for more details.
663+#
664+# You should have received a copy of the GNU General Public License along
665+# with this program. If not, see <http://www.gnu.org/licenses/>.
666+"""Test the Reset Password Page."""
667+
668+from PyQt4 import QtGui, QtCore
669+from twisted.trial.unittest import TestCase
670+
671+from ubuntu_sso.qt import common
672+from ubuntu_sso.qt.gui import ResetPasswordPage
673+from ubuntu_sso.qt.reset_password_ui import Ui_ResetPasswordPage
674+
675+
676+class FakePasswordAssistance(object):
677+ """Fake password_assistance_* calls."""
678+
679+ _called = False
680+
681+ def call(self, *args, **kwargs):
682+ """Check if the method was called."""
683+ FakePasswordAssistance._called = True
684+
685+ def clenaup(self):
686+ """Clean up the variable."""
687+ FakePasswordAssistance._called = False
688+
689+
690+class SetupAccountTestCase(TestCase):
691+ """Test the ResetPasswordPage code."""
692+
693+ def setUp(self):
694+ super(SetupAccountTestCase, self).setUp()
695+ self.page = ResetPasswordPage(Ui_ResetPasswordPage(),
696+ None,
697+ None)
698+ self.fake = FakePasswordAssistance()
699+
700+ def test_init(self):
701+ """Check the initial state of ResetPassword."""
702+ self.assertEqual(self.page.ui.password_line_edit.receivers(
703+ QtCore.SIGNAL('textEdited(QString)')), 1)
704+ self.assertEqual(self.page.ui.confirm_password_line_edit.receivers(
705+ QtCore.SIGNAL('textEdited(QString)')), 1)
706+
707+ def test_focus_changed_password_visibility(self):
708+ """Check visibility changes when focus_changed() is executed."""
709+ self.page.show()
710+ self.addCleanup(self.page.hide)
711+ self.page.focus_changed(None, self.page.ui.password_line_edit)
712+ self.assertTrue(self.page.ui.password_assistance.isVisible())
713+
714+ def test_show_hide_event(self):
715+ """Check connections to focusChanged on show and hide event."""
716+ self.page.show()
717+ self.addCleanup(self.page.hide)
718+ self.assertEqual(QtGui.QApplication.instance().receivers(
719+ QtCore.SIGNAL("focusChanged(QWidget*, QWidget*)")), 1)
720+ self.page.hide()
721+ self.assertEqual(QtGui.QApplication.instance().receivers(
722+ QtCore.SIGNAL("focusChanged(QWidget*, QWidget*)")), 0)
723+
724+ # pylint: disable=W0212
725+
726+ def test_focus_changed_1(self):
727+ """Check functions execution when focus_changed() is executed."""
728+ self.patch(common, 'password_default_assistance', self.fake.call)
729+ self.addCleanup(self.fake.clenaup)
730+ self.page.show()
731+ self.addCleanup(self.page.hide)
732+ self.assertFalse(FakePasswordAssistance._called)
733+ self.page.focus_changed(None, self.page.ui.password_line_edit)
734+ self.assertTrue(self.page.ui.password_assistance.isVisible())
735+ self.assertTrue(FakePasswordAssistance._called)
736+
737+ def test_focus_changed_2(self):
738+ """Check functions execution when focus_changed() is executed."""
739+ self.patch(common, 'password_check_match', self.fake.call)
740+ self.addCleanup(self.fake.clenaup)
741+ self.page.show()
742+ self.addCleanup(self.page.hide)
743+ self.assertFalse(FakePasswordAssistance._called)
744+ self.page.focus_changed(None, self.page.ui.confirm_password_line_edit)
745+ self.assertTrue(self.page.ui.password_assistance.isVisible())
746+ self.assertTrue(FakePasswordAssistance._called)
747+
748+ # pylint: enable=W0212

Subscribers

People subscribed via source and target branches